Compare commits

...

254 Commits

Author SHA1 Message Date
WerWolv
a1edb1b896 build: Bumped version to 1.32.2 2024-01-04 22:08:00 +01:00
WerWolv
b27e63586e build: Bundle nodes folder with executable 2024-01-04 22:07:49 +01:00
WerWolv
d2bd5b5640 impr: Make antialiasing be enabled by default for custom fonts 2024-01-04 21:33:45 +01:00
WerWolv
f91505ff09 fix: Unnecessary reinterpret_cast 2024-01-04 21:33:30 +01:00
WerWolv
464495987a fix: Crash due to task manager not being fully reset 2024-01-04 21:33:17 +01:00
WerWolv
27aef75e54 build: Added a plugin template to the SDK 2024-01-04 20:25:51 +01:00
WerWolv
70e3b4dd1a build: Cleanup build script a bit 2024-01-04 17:55:53 +01:00
WerWolv
556fd2bbc3 build: Really only build the things that are necessary 2024-01-04 13:18:27 +01:00
WerWolv
0097d1782e build: Fix external plugins not having a imhex version set 2024-01-04 12:01:40 +01:00
WerWolv
f03bdc5f45 build: Exclude libraries from main build target in sdk 2024-01-04 11:34:56 +01:00
WerWolv
ebf379f7c1 build: Fix some sdk variables not being set correctly 2024-01-04 10:39:07 +01:00
WerWolv
cd72ff1f84 build: Fix installing external plugins 2024-01-04 10:25:29 +01:00
WerWolv
eca41cac16 build: Fix more link and define issues 2024-01-04 01:06:38 +01:00
WerWolv
a8e2e132d1 build: Fix library linking and define setting issues 2024-01-04 01:01:51 +01:00
WerWolv
499711b9af build: Updated libfmt 2024-01-04 00:40:40 +01:00
WerWolv
b0eec3a844 build: Added dmg background resource 2024-01-04 00:38:40 +01:00
WerWolv
24e90f0f20 feat: Added simple HTTP request tool 2024-01-04 00:38:21 +01:00
WerWolv
ff48d37598 build: Make SDK not try to link to unbundled libraries 2024-01-04 00:37:56 +01:00
WerWolv
c402d58685 fix: Force scaling of default font to be a integer multiple 2024-01-03 18:26:48 +01:00
WerWolv
ed8934882e impr: Make sure detached windows always have an appropriate size 2024-01-03 18:26:25 +01:00
WerWolv
62093a8dd8 fix: ImHex not always going to sleep correctly 2024-01-02 17:11:52 +01:00
WerWolv
4a5f1038e0 fix: Crash when opening a file that cannot be mapped into memory 2023-12-31 14:58:20 +01:00
WerWolv
8cb833eca9 impr: Improve Documentation AI UX 2023-12-31 13:53:44 +01:00
WerWolv
e2b7a69fc8 fix: Warnings when trying to load progress for achievements that aren't in save file 2023-12-31 13:53:28 +01:00
Nik
950eaea8af impr: Make decompression support actually useful (#1481) 2023-12-31 11:39:24 +01:00
WerWolv
b22d90f9ca impr: Handle hiding of main menu better 2023-12-31 11:39:06 +01:00
WerWolv
b76e7ff678 impr: Remove duplicate window name from macOS title bar 2023-12-30 23:52:25 +01:00
WerWolv
9c386e949d git: Add brewfile lock to gitignore 2023-12-30 19:41:09 +01:00
WerWolv
2b1688be31 build: Don't try to sign unbundled executable on macOS 2023-12-30 19:40:01 +01:00
WerWolv
3d9de1aaa6 git: Fix Ubuntu builds missing commit information 2023-12-30 19:12:04 +01:00
Nik
5a0a5ad445 build: Fix macOS M1 build entirely (#1480) 2023-12-30 19:11:33 +01:00
WerWolv
038b98eacf fix: Foreground color not applying to ASCII column
Fixes #1477
2023-12-29 22:41:12 +01:00
WerWolv
3592d17c93 impr: Allow Home/End to work in hex editor view 2023-12-29 11:30:23 +01:00
WerWolv
4cbd84671c impr: Allow num keys to be interpreted as function keys if numlock isn't set
Closes #1475
2023-12-29 11:29:31 +01:00
WerWolv
c3c9603ea1 fix: Yara rules Add button not doing anything 2023-12-29 11:12:50 +01:00
WerWolv
af63b42eaf impr: Make task progress bar show animation when no progress is set 2023-12-28 22:14:45 +01:00
WerWolv
2f7da91a73 fix: Remove unused update parameter 2023-12-28 21:51:31 +01:00
WerWolv
8fcf08132e fix: Allow store to properly update files 2023-12-28 20:59:34 +01:00
WerWolv
db72ba295a build: Don't try to bundle updater on targets that don't have an updater 2023-12-28 20:44:42 +01:00
WerWolv
2d7a6a7cb5 fix: Uncaught exception when trying to update all store items 2023-12-28 20:35:35 +01:00
WerWolv
390b5a7925 fix: Logs in log view not being filtered correctly 2023-12-28 20:34:49 +01:00
WerWolv
5ca6ed30b4 build: Fix updater not being installed correctly 2023-12-28 20:26:58 +01:00
WerWolv
9685b39969 fix: Missing reference 2023-12-28 19:25:37 +01:00
WerWolv
03dc26d2d4 build: Always extract magic file database 2023-12-28 19:24:56 +01:00
WerWolv
b64bb3bec9 fix: Crashes when opening diffing view 2023-12-28 19:21:15 +01:00
WerWolv
8d3530a4f3 build: Bumped version to 1.32.1 2023-12-28 18:33:36 +01:00
WerWolv
83b1416797 build: Fixed dependencies of plugins not being bundled correctly 2023-12-28 18:05:49 +01:00
WerWolv
5adeac6bbc fix: Make sure library plugins are always initialized 2023-12-28 14:59:23 +01:00
WerWolv
f44b44a881 build: Fix wrong dependency name for libarchive on Ubuntu
Fixes #1474
2023-12-28 14:48:36 +01:00
WerWolv
1ed978f22e impr: Remove telemetry checkboxes from settings in the web version
They are disabled there and should not be enableable
2023-12-28 13:24:31 +01:00
WerWolv
7ed06ae515 git: Fixed set -x wrongfully added to windows CI
Closes  #1473
2023-12-28 11:48:13 +01:00
WerWolv
63042dbba8 build: Bumped version to 1.32.0 2023-12-28 00:37:50 +01:00
WerWolv
144d8d8ed4 web: Prevent canvas flickering when resizing browser window 2023-12-27 22:00:09 +01:00
WerWolv
99ba47a554 impr: Even less laggy UI 2023-12-27 21:23:54 +01:00
WerWolv
0462dab170 impr: Make the UI a bit less laggy 2023-12-27 21:11:40 +01:00
WerWolv
258481b0ba git: Fix errors and warnings thrown by CI 2023-12-27 21:11:27 +01:00
WerWolv
cb35f456ed build: Fix building on clang 2023-12-27 17:42:44 +01:00
WerWolv
686f8f43c3 fix: Pattern editor error tooltip becoming way too large in some cases 2023-12-27 17:29:27 +01:00
WerWolv
99dcd0a020 fix: Crash when receiving invalid data from API 2023-12-27 17:10:50 +01:00
WerWolv
3c6f52f5ea impr: Harden achievement progress store function 2023-12-27 16:57:44 +01:00
WerWolv
874619f62e impr: Harden settings store function 2023-12-27 16:53:03 +01:00
WerWolv
74b5c93caf impr: Code style improvements 2023-12-27 16:33:49 +01:00
WerWolv
ec45d1f564 build: Updated libwolv 2023-12-27 16:26:02 +01:00
WerWolv
42a75fe133 impr: Make ImHex's name not look weird in various places 2023-12-27 13:54:31 +01:00
WerWolv
e414c1cf1e impr: Implement rendering power saving using hashes 2023-12-27 13:54:00 +01:00
WerWolv
1cf692cecf fix: PerProvider move event not being unsubscribed 2023-12-27 11:33:04 +01:00
WerWolv
af5b871383 fix: Provider not having any valid regions by default 2023-12-27 11:31:25 +01:00
WerWolv
37d60411bb fix: Hyperlink items disappearing inside of scrolling containers 2023-12-27 02:14:38 +01:00
WerWolv
d7ba2e7171 feat: Add button to export pattern language section to a file 2023-12-27 01:58:20 +01:00
WerWolv
215be9255e fix: Hex editor not having a default selection color 2023-12-27 01:37:41 +01:00
WerWolv
9d0fd1f5b6 build: Try to fix Fedora build issues 2023-12-27 01:10:08 +01:00
WerWolv
4e0a93fc20 fix: MemoryProvider not having any valid regions 2023-12-27 01:05:34 +01:00
WerWolv
a0fddd2953 build: Try detecting macOS better when updating libarchive include path 2023-12-27 00:38:32 +01:00
WerWolv
40e66313a9 build: Fix library plugin install path 2023-12-27 00:31:47 +01:00
WerWolv
87155f98b3 fix: Bytes not automatically being focused in editing mode anymore 2023-12-26 23:43:11 +01:00
WerWolv
483325990c fix: Byte foreground highlighting being disabled during editing 2023-12-26 23:42:50 +01:00
WerWolv
83fa024fab feat: Added Base64 provider 2023-12-26 23:42:22 +01:00
WerWolv
96fe608d60 impr: Switch most usages of modals over to toasts 2023-12-26 00:22:47 +01:00
WerWolv
52192a3b26 impr: Better FPS graph 2023-12-25 23:23:19 +01:00
WerWolv
75e575fc01 impr: Remove shadow drawn by the no views open window 2023-12-24 14:52:14 +01:00
WerWolv
98bc89cb39 impr: Make sure all views are closed before loading new workspace or layout 2023-12-24 14:51:47 +01:00
WerWolv
d2d244ebc7 build: Make libarchive not required 2023-12-24 14:43:49 +01:00
WerWolv
9952854b53 build: Try different libarchive include path detection method 2023-12-24 14:37:59 +01:00
WerWolv
3bb079216c impr: Make sure welcome screen never gets detached from main window 2023-12-24 14:35:44 +01:00
WerWolv
b845dbb882 git: Ignore local folder produced by docker builds 2023-12-24 14:35:27 +01:00
WerWolv
7eb92c68de build: Only fix libarchive include dirs when necessary 2023-12-24 14:30:10 +01:00
WerWolv
24f8ce9d7f build: Fix macOS M1 build 2023-12-24 13:57:21 +01:00
WerWolv
343e98c99a build: Fix missing libarchive include directories on macOS 2023-12-24 13:41:10 +01:00
WerWolv
bc76eee847 build: Remove minimum required libarchive version 2023-12-24 13:33:18 +01:00
WerWolv
91ae8ba410 build: Add missing dependencies to brewfile and rpm spec 2023-12-24 13:26:05 +01:00
WerWolv
e2489151f3 feat: Added decompressing support 2023-12-24 13:14:51 +01:00
WerWolv
9066891ce2 fix: ImHex not starting on some platforms 2023-12-24 12:20:51 +01:00
WerWolv
65e2f1b5af fix: Synchronized scrolling not working correctly 2023-12-24 00:06:16 +01:00
WerWolv
020efefb25 git: Added new hashes to readme 2023-12-23 23:19:05 +01:00
WerWolv
83f8370e2a impr: Don't display plugins in the list that couldn't be loaded 2023-12-23 23:12:15 +01:00
WerWolv
61accd9569 fix: Hex editor displaying invalid bytes when no data is available 2023-12-23 23:04:40 +01:00
WerWolv
8a428df7df build: Updated HashLibPlus 2023-12-23 22:57:25 +01:00
WerWolv
de6bb5dfb9 build: Updated HashLibPlus 2023-12-23 22:55:41 +01:00
WerWolv
80561001b8 build: Updated HashLibPlus 2023-12-23 22:40:47 +01:00
WerWolv
33d077e997 build: Updated HashLibPlus 2023-12-23 22:25:01 +01:00
WerWolv
fe24db7c57 feat: Move hashes into plugin, merged in extra hashes plugin 2023-12-23 22:01:47 +01:00
Nik
61bfe10bc2 refactor: Rework features that use external libraries into optional plugins (#1470) 2023-12-23 21:09:41 +01:00
Nik
84bfd10416 build: Restructured entire custom plugin system (#1469) 2023-12-22 23:39:38 +01:00
WerWolv
538e79183c fix: Build because i64 is not the same as ImS64 somehow 2023-12-22 21:34:11 +01:00
WerWolv
ec64952cb4 impr: Only auto backup if there's something to backup 2023-12-22 21:16:09 +01:00
WerWolv
b934ca6ad3 impr: Allow a entire 64 bit address space to be displayed in the hex editor 2023-12-22 16:58:50 +01:00
WerWolv
0da6c03a8f git: Highlight collapsible headers better in readme 2023-12-22 13:59:57 +01:00
WerWolv
a54cbca6d2 git: Modernize readme 2023-12-22 13:53:29 +01:00
Justus Garbe
ad8e3e38f0 Revert pattern language until it's stable again (#1468)
- Revert pattern language
2023-12-21 22:01:07 +01:00
WerWolv
ffc1aa6a91 patterns: Updated pattern language 2023-12-21 16:56:21 +01:00
WerWolv
6ee1e72021 impr: Disable close provider button when tasks are running 2023-12-21 16:56:12 +01:00
WerWolv
5bc8e5c57c impr: Only display background color of patterns that have a color 2023-12-21 16:40:08 +01:00
WerWolv
d48acf7fef patterns: Updated pattern language 2023-12-21 16:39:37 +01:00
WerWolv
72260b5323 patterns: Updated pattern language 2023-12-21 16:23:50 +01:00
WerWolv
e84b8cb96d build: Fix glfw linking 2023-12-21 16:02:28 +01:00
WerWolv
adcaad791a patterns: Updated pattern language 2023-12-21 14:58:45 +01:00
WerWolv
b0490cfbbc build: Improve pdb generation 2023-12-21 13:57:40 +01:00
WerWolv
86231d0154 build: Release build on Windows trying to link to glfw3dll for some reason 2023-12-21 13:57:25 +01:00
WerWolv
6163f6c4a0 fix: ImHex crashing when no fonts were loaded 2023-12-20 16:31:31 +01:00
WerWolv
e3e117a14e impr: Close tutorial view when a tutorial is started 2023-12-20 15:26:45 +01:00
WerWolv
e2ae567b9f fix: Logger not printing project prefix properly 2023-12-20 15:10:53 +01:00
WerWolv
a0c2dc43f7 fix: Tutorial highlighting 2023-12-20 14:35:13 +01:00
WerWolv
f47163c4ad build: Updated libromfs 2023-12-20 14:11:51 +01:00
WerWolv
e951359a46 feat: Add frame time graph to FPS display 2023-12-20 13:42:42 +01:00
WerWolv
bf6b2db0cb fix: Infinite loop on crash 2023-12-20 13:38:13 +01:00
WerWolv
1ea8269dec impr: Better frame unlock logic 2023-12-20 12:07:22 +01:00
WerWolv
9bd1970371 fix: Debug breakpoint in crash handler not being triggered at all 2023-12-20 10:50:58 +01:00
WerWolv
5b3ae56912 patterns: Update all pattern language code to use new API 2023-12-20 10:08:40 +01:00
WerWolv
2b5789631f feat: Added basic toast popups 2023-12-19 23:21:20 +01:00
WerWolv
a6025e72fb fix: RGBA8 hex editor data visualizer not working correctly 2023-12-19 23:20:56 +01:00
WerWolv
96db2074c6 feat: Add ignore case and UTF16 search options to sequence searching 2023-12-19 14:34:35 +01:00
WerWolv
c7ab4a4569 refactor: Get rid of this->m_ 2023-12-19 13:10:25 +01:00
WerWolv
dd4be3b772 refactor: Make sure unlocalized strings are always actually unlocalized 2023-12-19 12:22:28 +01:00
WerWolv
8fe490ed03 fix: Escape to deselect bytes not working 2023-12-19 00:03:00 +01:00
WerWolv
eb21a5992f impr: Make sure no empty popup appears when right clicking search box 2023-12-18 22:51:08 +01:00
WerWolv
a3f1a5b0a9 fix: Crash when right clicking search bar when a provider with no menu options is open 2023-12-18 22:45:15 +01:00
WerWolv
71763d108b build: Updated libwolv 2023-12-18 22:39:46 +01:00
WerWolv
dc9ab135c8 impr: Make sure quick settings window always stays attached to bottom right 2023-12-18 14:57:37 +01:00
WerWolv
3dd33d0966 web: Force disable filtering of canvas 2023-12-18 14:57:17 +01:00
WerWolv
1cb2e0d765 impr: Make quick settings floating window auto resize 2023-12-18 13:55:50 +01:00
WerWolv
b34fb2d225 impr: Remove separator from view menu in release mode 2023-12-18 13:55:32 +01:00
WerWolv
4973556fc8 impr: Better UI resize handling on welcome screen 2023-12-18 13:08:17 +01:00
WerWolv
2948e57242 fix: std::views::enumerate not being widely supported yet 2023-12-18 12:43:16 +01:00
WerWolv
521ee5fe2d impr: Better pattern background coloring in pattern data view 2023-12-18 12:02:41 +01:00
WerWolv
478d6118d8 fix: Prevent empty provider menu from being opened 2023-12-18 11:58:31 +01:00
WerWolv
1b43270ae9 fix: Don't show provider as Read Only if it's empty 2023-12-18 11:58:19 +01:00
WerWolv
ec4fdc44ef impr: Draw hint in hex editor if no data can be displayed 2023-12-18 11:58:03 +01:00
WerWolv
91f49e2c6e fix: Window title not updating correctly when renaming memory file 2023-12-18 11:46:39 +01:00
WerWolv
6bc4a7242e fix: Allow search bar to be right clicked to open provider menu 2023-12-18 11:46:23 +01:00
WerWolv
eeab529bfa fix: Welcome screen close button position 2023-12-18 11:30:09 +01:00
WerWolv
d798713c60 fix: Missing includes, rename init function 2023-12-18 11:24:40 +01:00
WerWolv
edc4b18975 impr: Add plugin table to about page 2023-12-18 11:21:33 +01:00
WerWolv
450c93e029 impr: Cleanup welcome screen 2023-12-18 11:03:19 +01:00
WerWolv
c1abbfad7d impr: Make sure welcome screen stays at the back 2023-12-18 10:14:07 +01:00
WerWolv
d2d36c2211 impr: Add localization to color picker tool 2023-12-18 08:58:15 +01:00
WerWolv
aaaa02dbd0 impr: Improve handling of floating tool windows 2023-12-18 08:58:00 +01:00
WerWolv
a844fb3731 fix: Hash string popup being way too small 2023-12-17 23:47:42 +01:00
WerWolv
8f83fe5135 fix: Max field in value search not resetting when disabling range search 2023-12-17 23:38:10 +01:00
WerWolv
978558649e fix: Diffing view not highlighting correctly with a custom base address 2023-12-17 23:34:19 +01:00
WerWolv
3b5efb37e9 fix: Editing data inspector rows not working correctly 2023-12-17 23:31:01 +01:00
WerWolv
90abe982ed fix: Bookmark region setting not being locked correctly 2023-12-17 23:22:39 +01:00
WerWolv
7a0680c2cb impr: Add warning to disk provider if ImHex is not running elevated 2023-12-17 23:16:55 +01:00
WerWolv
71dd349044 fix: Highlighting in hex editor not being drawn correctly anymore 2023-12-17 23:16:04 +01:00
WerWolv
f2a795c51e fix: Provider loader interface being closable and not resizing correctly 2023-12-17 23:15:52 +01:00
WerWolv
7ad7ea061c impr: Refactor init logic 2023-12-17 20:33:17 +01:00
WerWolv
a315ecb831 fix: Flickering of selection frame in hex editor view when scrolling 2023-12-17 18:26:36 +01:00
WerWolv
c3d99e29dc web: Fix various HTML errors 2023-12-16 15:33:22 +01:00
WerWolv
f90f4b00a8 web: Some more SEO 2023-12-16 11:39:07 +01:00
WerWolv
b1aa4fd3f8 fix: Resize operations not working correctly
#1463
2023-12-15 20:59:58 +01:00
WerWolv
b5df20d7c6 feat: Exposed demangling of symbols to CLI 2023-12-15 08:11:51 +01:00
WerWolv
b58463bbaf build: Updated libwolv 2023-12-15 08:11:22 +01:00
WerWolv
b71a776770 fix: Format security error 2023-12-14 20:59:30 +01:00
WerWolv
78ef5b0d07 fix: ProviderChanged Event not being called correctly when closing first provider
Fixes #1421, Fixes #1416
2023-12-14 20:48:30 +01:00
WerWolv
c1f76be3b7 feat: Display complete window title when hovering over search bar 2023-12-14 13:50:26 +01:00
WerWolv
2ebd3c6f35 impr: Better centering of icons in icon buttons 2023-12-14 13:49:46 +01:00
WerWolv
003f9619c3 fix: Multiple localization issues 2023-12-14 11:33:54 +01:00
WerWolv
710ceedf3d fix: Rare case where workspace was saved to wrong file 2023-12-14 09:36:29 +01:00
WerWolv
5b77f511d3 impr: Added shadow to tutorial popups 2023-12-13 23:44:53 +01:00
WerWolv
f000b6bc0a feat: Added basic introduction tutorial 2023-12-13 23:03:39 +01:00
WerWolv
346f1362c6 impr: Allow more popups to be closed with Escape 2023-12-13 15:08:27 +01:00
WerWolv
92043a3d23 feat: Added tutorials view 2023-12-13 13:04:59 +01:00
WerWolv
3bc5295eae impr: Allow tutorials to work correctly with localized strings 2023-12-13 11:48:21 +01:00
WerWolv
5bcfe37b4e feat: Added tutorial system 2023-12-13 11:24:25 +01:00
WerWolv
1a8a9e53e1 impr: More information view UI improvements 2023-12-12 13:20:51 +01:00
WerWolv
c32dad75cd fix: Remove logging from CLI 2023-12-12 13:20:39 +01:00
WerWolv
045733d188 fix: Auto extractor overwriting existing files 2023-12-12 12:15:32 +01:00
WerWolv
f618e634e9 impr: Better UI for the data information view 2023-12-12 12:15:20 +01:00
WerWolv
1b457dae7d fix: Workspaces not always loading layout correctly 2023-12-12 00:16:21 +01:00
WerWolv
690b0df932 fix: Crash after restarting ImHex 2023-12-11 23:05:58 +01:00
WerWolv
e080164305 fix: Restart imhex popup not working correctly 2023-12-11 23:05:47 +01:00
WerWolv
1e4bb8c91e impr: Remove Font Awesome 2023-12-11 23:05:35 +01:00
WerWolv
17a7621342 fix: Style var not being popped correctly 2023-12-11 22:09:13 +01:00
WerWolv
ce27cb11a5 impr: Delete old backup files 2023-12-11 21:29:30 +01:00
WerWolv
b84b82c416 fix: Properly get auto backup file creation time 2023-12-11 21:17:40 +01:00
WerWolv
623e074ba0 build: Updated libromfs 2023-12-11 16:11:49 +01:00
WerWolv
91230ba438 feat: Added workspaces 2023-12-11 15:54:22 +01:00
WerWolv
cc4d61f8f5 build: Allow both libglfw3 and libglfw3-wayland as dependencies for the .deb package
#1227
2023-12-11 15:53:24 +01:00
WerWolv
7a4358a5ec feat: Added automatic backups 2023-12-11 11:42:33 +01:00
WerWolv
e6796d1458 fix: Hex editor footer taking up way too much space 2023-12-10 22:46:34 +01:00
WerWolv
1ba34c233e fix: Various scaling issues 2023-12-10 22:37:26 +01:00
WerWolv
ef7898ea8d impr: Add support for specifying filtering mode when loading textures 2023-12-09 22:00:35 +01:00
WerWolvTranslationBot
e49c3182ce lang: Translations update from Weblate (#1458)
Translations update from [Weblate](https://weblate.werwolv.net) for
[ImHex/Built-in
Plugin](https://weblate.werwolv.net/projects/imhex/built-in-plugin/).


It also includes following components:

* [ImHex/Windows
Plugin](https://weblate.werwolv.net/projects/imhex/windows-plugin/)



Current translation status:

![Weblate translation
status](https://weblate.werwolv.net/widgets/imhex/-/built-in-plugin/horizontal-auto.svg)

---------

Co-authored-by: Minseo Lee <itoupluk427@gmail.com>
2023-12-09 16:22:19 +01:00
WerWolv
c58c3dd311 impr: Make banner not as obnoxiously big 2023-12-09 16:20:58 +01:00
WerWolv
7738f8c831 impr: Highlight border of information banner when hovered 2023-12-09 15:56:26 +01:00
WerWolv
27cd3cc83a fix: Missing welcome screen header localization 2023-12-09 15:32:21 +01:00
WerWolv
2f5e04d07f web: Add sitemap to robots.txt 2023-12-09 13:57:55 +01:00
WerWolv
15af0726f1 build: Disable error on unknown warning flags 2023-12-09 13:57:46 +01:00
WerWolv
a60a45fb9d web: Fix various more SEO issues 2023-12-09 13:35:06 +01:00
WerWolv
df03ba3883 build: Fix build on macOS 2023-12-09 12:23:11 +01:00
WerWolv
09a148b8a5 impr: Add hint text about font size when no custom font is selected 2023-12-09 12:14:45 +01:00
WerWolv
350635d464 web: More UI and SEO improvements 2023-12-09 12:06:20 +01:00
WerWolv
cf13404254 web: Let's do some SEO 2023-12-08 16:22:47 +01:00
WerWolv
878f45dd80 fix: Various build errors 2023-12-08 16:22:36 +01:00
WerWolv
48bc0985d9 impr: Make info banner open a web page 2023-12-08 14:46:32 +01:00
WerWolv
e9bca123c2 impr: Allow per-OS info banners 2023-12-08 14:43:59 +01:00
WerWolv
6df3a9243f fix: Rename scaling setting to avoid old values being interpreted wrongly 2023-12-08 14:36:42 +01:00
WerWolv
f1b40d0500 build: Updated libromfs 2023-12-08 14:30:35 +01:00
WerWolv
0cbaf40747 build: Allow for better stacktraces on Linux 2023-12-08 14:00:32 +01:00
Tha_14
54c5d9debb git: Include diffing as a feature in Readme (#1459)
I saw that diffing is not mentioned anywhere in the README.md and people
I referred ImHex to did not consider using the editor due to diffing
being a requirement for them.

### Problem description
Diffing is not mentioned anywhere in the README.md

### Implementation description
Added Diffing to the features list of README.md
2023-12-08 12:56:44 +01:00
WerWolv
411884966b fix: Crash when making pattern editor window too small 2023-12-08 11:15:32 +01:00
WerWolv
ef25542220 impr: Remove old unused files 2023-12-08 11:15:18 +01:00
WerWolv
b4813660b5 refactor: Better interface for the event system 2023-12-08 10:29:44 +01:00
WerWolv
f08d1e265c impr: Make extra providers window wider 2023-12-07 23:51:11 +01:00
WerWolv
470bc8a049 fix: Adjust sidebar items to look better with new layout 2023-12-07 23:47:25 +01:00
Truman Kilen
5c84ef5f72 feat: Added Linux support to the Process Memory Provider (#1331)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->

### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
Implement a Linux backend for the ProcessMemoryProvider plugin.

### Implementation description
<!-- Explain what you did to correct the problem -->
Most of the provider code is the same between Windows and Linux. The
primary differences are:
- enumerate PIDs in `/proc/` to get the process list
- use `/proc/<PID>/cmdline` as the process name
- parse `/proc/<PID>/maps` to get the module list
- reading/writing from memory is done using
`process_vm_readv`/`process_vm_writev`

NOTE: `sudo setcap CAP_SYS_PTRACE=+eip build/imhex` must be run to give
the binary permission to read another process' memory. Running as root
user should also work but I would not recommend it.

### Additional things
The existing translations keys no longer match since I moved the plugin
from `windows` to `builtin`.

I'm not well versed in C++ so I attempted to keep my changes rather
simple. Feedback is very welcome.

---------

Co-authored-by: WerWolv <werwolv98@gmail.com>
2023-12-07 23:33:15 +01:00
WerWolv
8ab85a2af1 feat: Added unit converter to command palette 2023-12-07 16:15:00 +01:00
WerWolv
7f69f8bcdb impr: More size_t -> u64 2023-12-07 13:02:12 +01:00
WerWolv
3a016da549 impr: Make providers return a 64 bit size on all platforms 2023-12-07 12:06:26 +01:00
WerWolv
7b3e13c748 fix: Add missing localizations 2023-12-07 11:53:31 +01:00
WerWolv
f5cbcce112 impr: Add close button to toolbar 2023-12-07 11:53:16 +01:00
WerWolv
5f8c813aa7 impr: Only show provider selector bar when more than one is open 2023-12-07 11:21:13 +01:00
WerWolv
f68202a098 impr: Make hex editor footer collapsible 2023-12-07 11:20:54 +01:00
WerWolv
bfb2c6ab5f impr: Automatically remove null provider if another one is opened 2023-12-07 11:20:37 +01:00
WerWolv
00a24bc84b impr: Remove "ImHex" from title bar if a file is loaded 2023-12-07 11:19:08 +01:00
WerWolv
9ba6d7ee1e impr: Added simplified welcome screen 2023-12-07 11:18:49 +01:00
WerWolv
60ff62d018 impr: Disable resource usage widgets by default 2023-12-06 16:20:21 +01:00
WerWolv
5d24f1b691 impr: Fix input field selection in command palette 2023-12-06 16:20:06 +01:00
WerWolv
370ca740e3 feat: Allow layouts to be locked 2023-12-06 13:49:58 +01:00
WerWolv
ba8430d9e7 impr: Code style 2023-12-06 11:05:13 +01:00
WerWolv
0b71568d97 impr: Better UI handling when window is very small 2023-12-06 11:05:02 +01:00
WerWolv
37ac1b66dd refactor: Task Manager related code 2023-12-06 11:04:35 +01:00
WerWolv
1be9e8c5b1 impr: Simplify default layout down a bit 2023-12-06 09:10:16 +01:00
WerWolv
c6b9b947fb fix: Binding shortcuts to already existing shortcuts behaving weirdly 2023-12-06 09:09:32 +01:00
WerWolv
a1ef567ecd impr: Add setting to disable resource usage display in footer 2023-12-05 17:09:42 +01:00
WerWolv
2b22a15e8c feat: Added --hexdump subcommand 2023-12-05 16:45:35 +01:00
WerWolv
760b8c7a88 impr: Make long running tasks not freeze ImHex, fix saving non-continuous providers
Fixes #1454
2023-12-05 14:32:43 +01:00
WerWolvTranslationBot
13145bba03 lang: Translations update from Weblate (#1457)
Translations update from [Weblate](https://weblate.werwolv.net) for
[ImHex/Built-in
Plugin](https://weblate.werwolv.net/projects/imhex/built-in-plugin/).


It also includes following components:

* [ImHex/Windows
Plugin](https://weblate.werwolv.net/projects/imhex/windows-plugin/)



Current translation status:

![Weblate translation
status](https://weblate.werwolv.net/widgets/imhex/-/built-in-plugin/horizontal-auto.svg)

---------

Co-authored-by: Justus Garbe <gihihoh@gmail.com>
Co-authored-by: Minseo Lee <itoupluk427@gmail.com>
2023-12-05 10:50:52 +01:00
Nik
f9a9ed4846 impr: Vastly improved 3D Visualizer (#1456)
Based entirely on @paxcut's amazing PR #1443

---------

Co-authored-by: paxcut <paxcut@outlook.com>
Co-authored-by: paxcut <53811119+paxcut@users.noreply.github.com>
2023-12-05 10:49:51 +01:00
Jonathan Wright
d5a40d46bc git: Add f39 builds, remove f37 (#1451)
Fedora 37 goes EOL in less than a week.
2023-12-04 23:36:48 +01:00
WerWolv
19f3da556c fix: Native theme detection on Linux not working properly 2023-12-04 23:35:48 +01:00
WerWolv
e8f0a3bd23 impr: Force center modal views 2023-12-04 22:32:25 +01:00
WerWolv
08fd09064a fix: Settings view still using old system and not opening properly 2023-12-04 22:17:43 +01:00
Nik
caee764af3 fix: Crash when destructing a moved plugin 2023-12-04 21:01:48 +01:00
Nik
eae3cd99ee build: Disable network tests in offline builds 2023-12-04 20:01:58 +01:00
394 changed files with 17409 additions and 10996 deletions

View File

@@ -6,4 +6,11 @@ skip -rfu ^__gnu_debug::
skip -rfu ^ImGui::
# Trigger breakpoint when execution reaches triggerSafeShutdown()
break triggerSafeShutdown
break triggerSafeShutdown
# Print backtrace after execution jumped to an invalid address
define fixbt
set $pc = *(void **)$rsp
set $rsp = $rsp + 8
bt
end

5
.gitattributes vendored
View File

@@ -1 +1,4 @@
lib/external/** linguist-vendored
lib/external/** linguist-vendored
dist/*.sh eol=lf
dist/**/*Dockerfile eol=lf

View File

@@ -95,7 +95,7 @@ jobs:
echo "ImHex checks for the existence of this file to determine if it is running in portable mode. You should not delete this file" > $PWD/install/PORTABLE
- name: ⬆️ Upload Windows Installer
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: Windows Installer x86_64
@@ -103,7 +103,7 @@ jobs:
imhex-*.msi
- name: ⬆️ Upload Portable ZIP
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: Windows Portable x86_64
@@ -120,7 +120,7 @@ jobs:
mv opengl32.dll build/install
- name: ⬆️ Upload NoGPU Portable ZIP
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: Windows Portable NoGPU x86_64
@@ -238,15 +238,17 @@ jobs:
ninja package
- name: ⬆️ Upload DMG
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: macOS DMG${{matrix.suffix}} x86_64
path: build/*.dmg
macos-arm64:
macos-arm64-build:
runs-on: ubuntu-22.04
name: 🍎 macOS 12.1 arm64
outputs:
IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }}
steps:
- name: 🧰 Checkout
uses: actions/checkout@v3
@@ -266,13 +268,15 @@ jobs:
cache-target: /cache
- name: 🛠️ Build using docker
id: build
run: |
echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_OUTPUT
docker buildx build . -f dist/macOS/arm64.Dockerfile --progress=plain --build-arg 'JOBS=4' --build-arg "BUILD_TYPE=$(BUILD_TYPE)" --build-context imhex=$(pwd) --output out
- name: ⬆️ Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: macOS ZIP arm64
name: macos_arm64_intermediate
path: out/
# See https://github.com/actions/cache/issues/342#issuecomment-1711054115
@@ -282,7 +286,56 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
gh extension install actions/gh-actions-cache
gh actions-cache delete "build-macos-arm64-cache" --confirm
gh actions-cache delete "build-macos-arm64-cache" --confirm || true
macos-arm64-package:
runs-on: macos-12
name: 🍎 macOS 12.1 arm64 Packaging
needs: macos-arm64-build
env:
IMHEX_VERSION: ${{ needs.macos-arm64-build.outputs.IMHEX_VERSION }}
steps:
- name: ⬇️ Download artifact
uses: actions/download-artifact@v4
with:
name: macos_arm64_intermediate
path: out
- name: 🗑️ Delete artifact
uses: geekyeggo/delete-artifact@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: macos_arm64_intermediate
- name: ✒️ Fix Signature
run: |
set -x
cd out
codesign --remove-signature ImHex.app
codesign --force --deep --sign - ImHex.app
- name: 📁 Fix permissions
run: |
set -x
cd out
chmod -R 755 ImHex.app/
- name: 📦 Create DMG
run: |
set -x
mkdir bundle
mv out/ImHex.app bundle
cd bundle
ln -s /Applications Applications
cd ..
hdiutil create -volname "ImHex" -srcfolder bundle -ov -format UDZO imhex-${{env.IMHEX_VERSION}}-macOS-arm64.dmg
- name: ⬆️ Upload DMG
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: macOS DMG arm64
path: ./*.dmg
# Ubuntu build
ubuntu:
@@ -335,14 +388,9 @@ jobs:
with:
dotnet-version: '8.0.100'
- name: 🏔️ Set Environment variables
run: |
echo COMMIT_SHA_SHORT=$(git rev-parse --short HEAD) >> $GITHUB_ENV
echo COMMIT_SHA_LONG=$(git rev-parse HEAD) >> $GITHUB_ENV
echo COMMIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) >> $GITHUB_ENV
# Ubuntu cmake build
- name: 🛠️ Build
shell: bash
run: |
set -x
git config --global --add safe.directory '*'
@@ -354,9 +402,9 @@ jobs:
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${{ env.COMMIT_SHA_SHORT }}" \
-DIMHEX_COMMIT_HASH_LONG="${{ env.COMMIT_SHA_LONG }}" \
-DIMHEX_COMMIT_BRANCH="${{ env.COMMIT_BRANCH }}" \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DIMHEX_ENABLE_LTO=ON \
-DIMHEX_USE_GTK_FILE_PICKER=ON \
-DDOTNET_EXECUTABLE="dotnet" \
@@ -374,7 +422,7 @@ jobs:
mv build/DebDir.deb imhex-${{env.IMHEX_VERSION}}-Ubuntu-${{ matrix.release_num }}-x86_64.deb
- name: ⬆️ Upload DEB
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: Ubuntu ${{ matrix.release_num }} DEB x86_64
@@ -410,14 +458,14 @@ jobs:
- name: ⬆️ Upload AppImage
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: Linux AppImage x86_64
path: 'out/*.AppImage'
- name: ⬆️ Upload AppImage zsync
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: Linux AppImage zsync x86_64
@@ -520,7 +568,7 @@ jobs:
mv *.pkg.tar.zst imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst
- name: ⬆️ Upload imhex-archlinux.pkg.tar.zst
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: ArchLinux .pkg.tar.zst x86_64
@@ -537,14 +585,14 @@ jobs:
mock_release: rawhide
release_num: rawhide
mock_config: fedora-rawhide
- name: Fedora
mock_release: f39
release_num: 39
mock_config: fedora-39
- name: Fedora
mock_release: f38
release_num: 38
mock_config: fedora-38
- name: Fedora
mock_release: f37
release_num: 37
mock_config: fedora-37
- name: RHEL-AlmaLinux
mock_release: epel9
release_num: 9
@@ -648,7 +696,7 @@ jobs:
$GITHUB_WORKSPACE/imhex-${{env.IMHEX_VERSION}}-${{matrix.name}}-${{matrix.release_num}}-x86_64.rpm
- name: ⬆️ Upload RPM
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: ${{ matrix.name }} ${{ matrix.release_num }} RPM x86_64

View File

@@ -29,7 +29,7 @@ jobs:
uses: actions/cache@v3
with:
path: cache
key: build-web-cache
key: web-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
- name: 🐳 Inject /cache into docker
uses: reproducible-containers/buildkit-cache-dance@v2.1.2
@@ -43,9 +43,7 @@ jobs:
- name: 🔨 Fix permissions
run: |
chmod -c -R +rX "out/" | while read line; do
echo "::warning title=Invalid file permissions automatically fixed::$line"
done
chmod -c -R +rX "out/"
- name: ⬆️ Upload artifacts
uses: actions/upload-pages-artifact@v2

View File

@@ -154,7 +154,6 @@ jobs:
WINGET_GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.WINGET_GITHUB_TOKEN != '' }}"
run: |
set -x
$tagname = $env:GITHUB_REF.Replace("refs/tags/", "")
$version = $tagname.Replace("v", "")
$url = "https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-Windows-x86_64.msi"

4
.gitignore vendored
View File

@@ -3,9 +3,11 @@
cmake-build-*/
build*/
local/
venv/
*.mgc
imgui.ini
.DS_Store
./CMakeUserPresets.json
./CMakeUserPresets.json
Brewfile.lock.json

3
.gitmodules vendored
View File

@@ -33,3 +33,6 @@
path = lib/external/libwolv
url = https://github.com/WerWolv/libwolv
[submodule "lib/third_party/HashLibPlus"]
path = lib/third_party/HashLibPlus
url = https://github.com/WerWolv/HashLibPlus

View File

@@ -39,17 +39,13 @@ setDefaultBuiltTypeIfUnset()
detectBadClone()
verifyCompiler()
# List plugin names here. Project name must match folder name
set(PLUGINS
builtin
windows
script_loader
)
detectBundledPlugins()
# Add various defines
detectOS()
detectArch()
addDefines()
# Configure packaging and install targets
configurePackingResources()
setUninstallTarget()
addBundledLibraries()
@@ -64,6 +60,7 @@ add_subdirectory(main)
enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
# Configure packaging
# Configure more resources that will be added to the install package
createPackage()
generatePDBs()
generateSDKDirectory()

288
README.md
View File

@@ -51,86 +51,259 @@ If you like my work, please consider supporting me on GitHub Sponsors, Patreon o
## Screenshots
![Hex editor, patterns and data information](https://user-images.githubusercontent.com/10835354/139717326-8044769d-527b-4d88-8adf-2d4ecafdca1f.png)
![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://user-images.githubusercontent.com/10835354/139717323-1f8c9d52-f7eb-4f43-9f11-097ac728ed6c.png)
<details>
<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)
![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)
![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)
</details>
## Features
- Featureful hex view
<details>
<summary><strong>Featureful hex view</strong></summary>
- Byte patching
- Patch management
- Copy bytes as feature
- Infinite Undo/Redo
- "Copy bytes as..."
- Bytes
- Hex string
- C, C++, C#, Rust, Python, Java & JavaScript array
- ASCII-Art hex view
- HTML self-contained div
- String and hex search
- Colorful highlighting
- Simple string and hex search
- Goto from start, end and current cursor position
- Custom C++-like pattern language for parsing highlighting a file's content
- Automatic loading based on MIME type
- arrays, pointers, structs, unions, enums, bitfields, namespaces, little and big endian support, conditionals and much more!
- Colorful highlighting
- Configurable foreground highlighting rules
- Background highlighting using patterns, find results and bookmarks
- Displaying data as a list of many different types
- Hexadecimal integers (8, 16, 32, 64 bit)
- Signed and unsigned decimal integers (8, 16, 32, 64 bit)
- Floats (16, 32, 64 bit)
- RGBA8 Colors
- HexII
- Binary
- Decoding data as ASCII and custom encodings
- Built-in support for UTF-8, UTF-16, ShiftJIS, most Windows encodings and many more
- Paged data view
</details>
<details>
<summary><strong>Custom C++-like pattern language for parsing highlighting a file's content</strong></summary>
- Automatic loading based on MIME types and magic values
- Arrays, pointers, structs, unions, enums, bitfields, namespaces, little and big endian support, conditionals and much more!
- Useful error messages, syntax highlighting and error marking
- Doesn't burn out your retinas when used in late-night sessions
- Dark mode by default, but a light mode is available as well
- Data importing
- Support for visualizing many different types of data
- Images
- Audio
- 3D Models
- Coordinates
- Time stamps
</details>
<details>
<summary><strong>Theming support</strong></summary>
- Doesn't burn out your retinas when used in late-night sessions
- Dark mode by default, but a light mode is available as well
- Customizable colors and styles for all UI elements through shareable theme files
- Support for custom fonts
</details>
<details>
<summary><strong>Importing and Exporting data</strong></summary>
- Base64 files
- IPS and IPS32 patches
- Data exporting
- IPS and IPS32 patches
- Data inspector allowing interpretation of data as many different types (little and big endian)
- Huge file support with fast and efficient loading
- String search
- Copying of strings
- Copying of demangled strings
- File hashing support
- CRC16 and CRC32 with custom initial values and polynomials
- MD4, MD5
- SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
- Disassembler supporting many architectures (frontend for Capstone)
- ARM32 (ARM, Thumb, Cortex-M, AArch32)
- ARM64
- MIPS (MIPS32, MIPS64, MIPS32R6, Micro)
- x86 (16-bit, 32-bit, 64-bit)
- PowerPC (32-bit, 64-bit)
- SPARC
- IBM SystemZ
- xCORE
- M68K
- TMS320C64X
- M680X
- Ethereum
- RISC-V
- WebAssembly
- MOS65XX
- Berkeley Packet Filter
- Bookmarks
- Region highlighting
- Comments
- Data Analyzer
- Markdown reports
</details>
<details>
<summary><strong>Data Inspector</strong></summary>
- Interpreting data as many different types with endianess, decimal, hexadecimal and octal support and bit inversion
- Unsigned and signed integers (8, 16, 24, 32, 48, 64 bit)
- Floats (16, 32, 64 bit)
- Signed and Unsigned LEB128
- ASCII, Wide and UTF-8 characters and strings
- time32_t, time64_t, DOS date and time
- GUIDs
- RGBA8 and RGB65 Colors
- Copying and modifying bytes through the inspector
- Adding new data types through the pattern language
- Support for hiding rows that aren't used
</details>
<details>
<summary><strong>Node-based data pre-processor</strong></summary>
- Modify, decrypt and decode data before it's being displayed in the hex editor
- Modify data without touching the underlying source
- Support for adding custom nodes
</details>
<details>
<summary><strong>Loading data from many different data sources</strong></summary>
- Local Files
- Support for huge files with fast and efficient loading
- Raw Disks
- Loading data from raw disks and partitions
- GDB Server
- Access the RAM of a running process or embedded devices through GDB
- Intel Hex and Motorola SREC data
- Process Memory
- Inspect the entire address space of a running process
</details>
<details>
<summary><strong>Data searching</strong></summary>
- Support for searching the entire file or only a selection
- String extraction
- Option to specify minimum length and character set (lower case, upper case, digits, symbols)
- Option to specify encoding (ASCII, UTF-8, UTF-16 big and little endian)
- Sequence search
- Search for a sequence of bytes or characters
- Option to ignore character case
- Regex search
- Search for strings using regular expressions
- Binary Pattern
- Search for sequences of bytes with optional wildcards
- Numeric Value search
- Search for signed/unsigned integers and floats
- Search for ranges of values
- Option to specify size and endianess
- Option to ignore unaligned values
</details>
<details>
<summary><strong>Data hashing support</strong></summary>
- Many different algorithms available
- CRC8, CRC16 and CRC32 with custom initial values and polynomials
- Many default polynomials available
- MD5
- SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
- Adler32
- AP
- BKDR
- Bernstein, Bernstein1
- DEK, DJB, ELF, FNV1, FNV1a, JS, PJW, RS, SDBM
- OneAtTime, Rotating, ShiftAndXor, SuperFast
- Murmur2_32, MurmurHash3_x86_32, MurmurHash3_x86_128, MurmurHash3_x64_128
- SipHash64, SipHash128
- XXHash32, XXHash64
- Tiger, Tiger2
- Blake2B, Blake2S
- Hashing of specific regions of the loaded data
- Hashing of arbitrary strings
</details>
<details>
<summary><strong>Diffing support</strong></summary>
- Compare data of different data sources
- Difference highlighting
- Table view of differences
</details>
<details>
<summary><strong>Integrated disassembler</strong></summary>
- Support for all architectures supported by Capstone
- ARM32 (ARM, Thumb, Cortex-M, AArch32)
- ARM64
- MIPS (MIPS32, MIPS64, MIPS32R6, Micro)
- x86 (16-bit, 32-bit, 64-bit)
- PowerPC (32-bit, 64-bit)
- SPARC
- IBM SystemZ
- xCORE
- M68K
- TMS320C64X
- M680X
- Ethereum
- RISC-V
- WebAssembly
- MOS65XX
- Berkeley Packet Filter
</details>
<details>
<summary><strong>Bookmarks</strong></summary>
- Support for bookmarks with custom names and colors
- Highlighting of bookmarked region in the hex editor
- Jump to bookmarks
- Open content of bookmark in a new tab
- Add comments to bookmarks
</details>
<details>
<summary><strong>Featureful data analyzer and visualizer</strong></summary>
- File magic-based file parser and MIME type database
- Byte distribution graph
- Byte type distribution graph
- Entropy graph
- Highest and average entropy
- Encrypted / Compressed file detection
- Built-in Content Store
- Download all files found in the database directly from within ImHex
- Yara Rules support
- Quickly scan a file for vulnerabilities with official yara rules
- Helpful tools
- Itanium and MSVC demangler
- Digram and Layered distribution graphs
</details>
<details>
<summary><strong>YARA Rule support</strong></summary>
- Scan a file for vulnerabilities with official yara rules
- Highlight matches in the hex editor
- Jump to matches
- Apply multiple rules at once
</details>
<details>
<summary><strong>Helpful tools</strong></summary>
- Itanium, MSVC, Rust and D-Lang demangler based on LLVM
- ASCII table
- Regex replacer
- Mathematical expression evaluator (Calculator)
- Hexadecimal Color picker
- Graphing calculator
- Hexadecimal Color picker with support for many different formats
- Base converter
- Byte swapper
- UNIX Permissions calculator
- Wikipedia term definition finder
- File utilities
- File splitter
- File combiner
- File shredder
- IEEE754 Float visualizer
- Division by invariant multiplication calculator
- TCP Client/Server
- Euclidean algorithm calculator
</details>
<details>
<summary><strong>Built-in Content updater</strong></summary>
- Download all files found in the database directly from within ImHex
- Pattern files for decoding various file formats
- Libraries for the pattern language
- Magic files for file type detection
- Custom data processor nodes
- Custom encodings
- Custom themes
- Yara rules
</details>
<details>
<summary><strong>Modern Interface</strong></summary>
- Support for multiple workspaces
- Support for custom layouts
- Detachable windows
</details>
<details>
<summary><strong>Easy to get started</strong></summary>
- Support for many different languages
- Simplified mode for beginners
- Extensive documentation
- Many example files available on [the Database](https://github.com/WerWolv/ImHex-Patterns)
- Achievements guiding you through the features of ImHex
- Interactive tutorials
</details>
## Pattern Language
@@ -148,7 +321,13 @@ For format patterns, libraries, magic and constant files, check out the [ImHex-P
## Requirements
To use ImHex, the following minimal system requirements need to be met:
To use ImHex, the following minimal system requirements need to be met.
> [!IMPORTANT]
> ImHex requires a GPU with OpenGL 3.0 support in general.
> There are releases available (with the `-NoGPU` suffix) that are software rendered and don't require a GPU, however these can be a lot slower than the GPU accelerated versions.
>
> If possible at all, make ImHex use the dedicated GPU on your system instead of the integrated one (especially Intel HD GPUs are known to cause issues).
- **OS**:
- **Windows**: Windows 7 or higher (Windows 10/11 recommended)
@@ -175,8 +354,9 @@ To compile ImHex on any platform, GCC (or Clang) is required with a version that
On macOS, Clang is also required to compile some ObjC code.
All releases are being built using latest available GCC.
Many dependencies are bundled into the repository using submodules so make sure to clone it using the `--recurse-submodules` option.
All dependencies that aren't bundled, can be installed using the dependency installer scripts found in the `/dist` folder.
> [!NOTE]
> Many dependencies are bundled into the repository using submodules so make sure to clone it using the `--recurse-submodules` option.
> All dependencies that aren't bundled, can be installed using the dependency installer scripts found in the `/dist` folder.
For more information, check out the [Compiling](/dist/compiling) guide.

View File

@@ -1 +1 @@
1.31.0
1.32.2

View File

@@ -68,8 +68,10 @@ macro(detectOS)
set(PLUGINS_INSTALL_LOCATION "share/imhex/plugins")
else()
set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins")
# Warning : Do not work with portable versions such as appimage (because the path is hardcoded)
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex") # "plugins" will be appended from the app
# Add System plugin location for plugins to be loaded from
# IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex")
endif()
else ()
@@ -78,16 +80,6 @@ macro(detectOS)
endmacro()
# Detect 32 vs. 64 bit system
macro(detectArch)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
add_compile_definitions(ARCH_64_BIT)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
add_compile_definitions(ARCH_32_BIT)
endif()
endmacro()
macro(configurePackingResources)
if (WIN32)
if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
@@ -108,11 +100,12 @@ macro(configurePackingResources)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "ImHex")
set_property(INSTALL "$<TARGET_FILE_NAME:main>"
PROPERTY CPACK_START_MENU_SHORTCUTS "ImHex"
)
)
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/resources/dist/windows/LICENSE.rtf")
endif()
elseif (APPLE)
set (IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns")
elseif (APPLE OR ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin")
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns")
set(BUNDLE_NAME "imhex.app")
if (IMHEX_GENERATE_PACKAGE)
set(APPLICATION_TYPE MACOSX_BUNDLE)
@@ -129,9 +122,9 @@ macro(configurePackingResources)
string(TIMESTAMP CURR_YEAR "%Y")
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 - ${CURR_YEAR} WerWolv. All rights reserved." )
if ("${CMAKE_GENERATOR}" STREQUAL "Xcode")
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/imhex.app")
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${BUNDLE_NAME}")
else ()
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/imhex.app")
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}")
endif()
set(PLUGINS_INSTALL_LOCATION "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
@@ -147,28 +140,28 @@ macro(createPackage)
foreach (plugin IN LISTS PLUGINS)
add_subdirectory("plugins/${plugin}")
if (TARGET ${plugin})
get_target_property(IS_RUST_PROJECT ${plugin} RUST_PROJECT)
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
if (IS_RUST_PROJECT)
set_target_properties(${plugin} PROPERTIES CARGO_BUILD_TARGET_DIR ${CMAKE_BINARY_DIR}/plugins)
get_target_property(PLUGIN_LOCATION ${plugin} LOCATION)
install(FILES "${PLUGIN_LOCATION}/../${plugin}.hexplug" DESTINATION "${PLUGINS_INSTALL_LOCATION}" PERMISSIONS ${LIBRARY_PERMISSIONS})
else ()
if (APPLE)
if (IMHEX_GENERATE_PACKAGE)
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION})
else ()
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
endif ()
if (APPLE)
if (IMHEX_GENERATE_PACKAGE)
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION})
else ()
install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION})
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
endif ()
endif ()
else ()
if (WIN32)
get_target_property(target_type ${plugin} TYPE)
if (target_type STREQUAL "SHARED_LIBRARY")
install(TARGETS ${plugin} RUNTIME DESTINATION ${PLUGINS_INSTALL_LOCATION})
else ()
install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION})
endif()
else()
install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION})
endif()
endif()
add_dependencies(imhex_all ${plugin})
endif ()
@@ -180,12 +173,17 @@ macro(createPackage)
# Install binaries directly in the prefix, usually C:\Program Files\ImHex.
set(CMAKE_INSTALL_BINDIR ".")
set(PLUGIN_TARGET_FILES "")
foreach (plugin IN LISTS PLUGINS)
list(APPEND PLUGIN_TARGET_FILES "$<TARGET_FILE:${plugin}>")
endforeach ()
# Grab all dynamically linked dependencies.
INSTALL(CODE "set(CMAKE_INSTALL_BINDIR \"${CMAKE_INSTALL_BINDIR}\")")
INSTALL(CODE "LIST(APPEND DEP_FOLDERS \${PY_PARENT})")
install(CODE "set(CMAKE_INSTALL_BINDIR \"${CMAKE_INSTALL_BINDIR}\")")
install(CODE "set(PLUGIN_TARGET_FILES \"${PLUGIN_TARGET_FILES}\")")
install(CODE [[
file(GET_RUNTIME_DEPENDENCIES
EXECUTABLES $<TARGET_FILE:builtin> $<TARGET_FILE:libimhex> $<TARGET_FILE:main>
EXECUTABLES ${PLUGIN_TARGET_FILES} $<TARGET_FILE:libimhex> $<TARGET_FILE:main>
RESOLVED_DEPENDENCIES_VAR _r_deps
UNRESOLVED_DEPENDENCIES_VAR _u_deps
CONFLICTING_DEPENDENCIES_PREFIX _c_deps
@@ -212,61 +210,70 @@ macro(createPackage)
elseif(UNIX AND NOT APPLE)
set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dist/DEBIAN/control.in ${CMAKE_BINARY_DIR}/DEBIAN/control)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./share/imhex")
# install AppStream file
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/net.werwolv.imhex.metainfo.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)
# install symlink for the old standard name
file(CREATE_LINK net.werwolv.imhex.metainfo.xml ${CMAKE_CURRENT_BINARY_DIR}/net.werwolv.imhex.appdata.xml SYMBOLIC)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/net.werwolv.imhex.appdata.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)
endif()
if (IMHEX_GENERATE_PACKAGE AND APPLE)
include(PostprocessBundle)
set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
if (APPLE)
if (IMHEX_GENERATE_PACKAGE)
include(PostprocessBundle)
# Fix rpath
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>)
set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})
# FIXME: Remove this once we move/integrate the plugins directory.
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")
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS")
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources")
install(TARGETS main BUNDLE DESTINATION ".")
install(FILES $<TARGET_FILE:main> DESTINATION "${IMHEX_BUNDLE_PATH}")
# Fix rpath
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>)
# Update library references to make the bundle portable
postprocess_bundle(imhex_all main)
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
# Enforce DragNDrop packaging.
set(CPACK_GENERATOR "DragNDrop")
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS")
set (CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns" )
set (CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/imhex.app/Contents/Info.plist")
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources")
install(TARGETS main BUNDLE DESTINATION ".")
# Update library references to make the bundle portable
postprocess_bundle(imhex_all main)
# Enforce DragNDrop packaging.
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns")
set(CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}/Contents/Info.plist")
# Sign the bundle
find_program(CODESIGN_PATH codesign)
if (CODESIGN_PATH)
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_BINARY_DIR}/${BUNDLE_NAME})
endif()
endif()
else()
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if(WIN32) # Forwarder is only needed on Windows
if (TARGET updater)
install(TARGETS updater RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
if (TARGET main-forwarder)
install(TARGETS main-forwarder BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
endif()
if (IMHEX_GENERATE_PACKAGE)
set (CPACK_BUNDLE_NAME "ImHex")
set(CPACK_BUNDLE_NAME "ImHex")
include(CPack)
endif()
@@ -395,6 +402,35 @@ function(verifyCompiler)
endif()
endfunction()
macro(detectBundledPlugins)
file(GLOB PLUGINS_DIRS "plugins/*")
if (NOT DEFINED IMHEX_INCLUDE_PLUGINS)
foreach(PLUGIN_DIR ${PLUGINS_DIRS})
if (EXISTS "${PLUGIN_DIR}/CMakeLists.txt")
get_filename_component(PLUGIN_NAME ${PLUGIN_DIR} NAME)
if (NOT (${PLUGIN_NAME} IN_LIST IMHEX_EXCLUDE_PLUGINS))
list(APPEND PLUGINS ${PLUGIN_NAME})
endif ()
endif()
endforeach()
else()
set(PLUGINS ${IMHEX_INCLUDE_PLUGINS})
endif()
foreach(PLUGIN_NAME ${PLUGINS})
message(STATUS "Enabled bundled plugin '${PLUGIN_NAME}'")
endforeach()
if (NOT PLUGINS)
message(FATAL_ERROR "No bundled plugins enabled")
endif()
if (NOT ("builtin" IN_LIST PLUGINS))
message(FATAL_ERROR "The 'builtin' plugin is required for ImHex to work!")
endif ()
endmacro()
macro(setVariableInParent variable value)
get_directory_property(hasParent PARENT_DIRECTORY)
@@ -414,9 +450,9 @@ function(downloadImHexPatternsFiles dest)
endif ()
FetchContent_Declare(
imhex_patterns
GIT_REPOSITORY https://github.com/WerWolv/ImHex-Patterns.git
GIT_TAG origin/master
imhex_patterns
GIT_REPOSITORY https://github.com/WerWolv/ImHex-Patterns.git
GIT_TAG origin/master
)
message(STATUS "Downloading ImHex-Patterns repo branch ${PATTERNS_BRANCH}...")
@@ -429,7 +465,7 @@ function(downloadImHexPatternsFiles dest)
endif ()
if (EXISTS ${imhex_patterns_SOURCE_DIR})
set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic)
set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic nodes)
foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION ${dest} PATTERN "**/_schema.json" EXCLUDE)
endforeach ()
@@ -445,10 +481,14 @@ macro(setupCompilerFlags target)
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wall -Wextra -Wpedantic -Werror")
endif()
if (UNIX AND NOT APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -rdynamic")
endif()
set(IMHEX_CXX_FLAGS "-fexceptions -frtti")
# Disable some warnings
set(IMHEX_C_CXX_FLAGS "-Wno-array-bounds -Wno-deprecated-declarations")
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations")
if (IMHEX_ENABLE_UNITY_BUILD AND WIN32)
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wa,-mbig-obj")
@@ -466,6 +506,7 @@ macro(setupCompilerFlags target)
endif ()
# Set actual CMake flags
set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${IMHEX_COMMON_FLAGS} ${IMHEX_C_CXX_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${IMHEX_COMMON_FLAGS} ${IMHEX_C_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${IMHEX_COMMON_FLAGS} ${IMHEX_C_CXX_FLAGS} ${IMHEX_CXX_FLAGS}")
set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} ${IMHEX_COMMON_FLAGS}")
@@ -475,12 +516,12 @@ endmacro()
macro(setUninstallTarget)
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
endmacro()
@@ -497,13 +538,13 @@ macro(addBundledLibraries)
set_target_properties(microtar PROPERTIES POSITION_INDEPENDENT_CODE ON)
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_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(FPHSA_NAME_MISMATCHED ON CACHE BOOL "")
@@ -556,24 +597,6 @@ macro(addBundledLibraries)
find_package(LLVM REQUIRED Demangle)
endif()
if (NOT USE_SYSTEM_YARA)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/yara EXCLUDE_FROM_ALL)
set_target_properties(libyara PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(YARA_LIBRARIES libyara)
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(YARA REQUIRED IMPORTED_TARGET yara)
endif()
if (NOT USE_SYSTEM_MINIAUDIO)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/miniaudio EXCLUDE_FROM_ALL)
set_target_properties(miniaudio PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(MINIAUDIO_LIBRARIES miniaudio)
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(miniaudio REQUIRED IMPORTED_TARGET miniaudio)
endif()
if (NOT USE_SYSTEM_JTHREAD)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/jthread EXCLUDE_FROM_ALL)
set(JTHREAD_LIBRARIES jthread)
@@ -586,20 +609,6 @@ macro(addBundledLibraries)
set(JTHREAD_LIBRARIES jthread)
endif()
if (NOT USE_SYSTEM_CAPSTONE)
set(CAPSTONE_BUILD_STATIC_RUNTIME OFF CACHE BOOL "Disable shared library building")
set(CAPSTONE_BUILD_SHARED OFF CACHE BOOL "Disable shared library building")
set(CAPSTONE_BUILD_TESTS OFF CACHE BOOL "Disable tests")
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/capstone EXCLUDE_FROM_ALL)
set_target_properties(capstone PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_options(capstone PRIVATE -Wno-unused-function)
set(CAPSTONE_LIBRARIES "capstone")
set(CAPSTONE_INCLUDE_DIRS ${THIRD_PARTY_LIBS_FOLDER}/capstone/include)
else()
find_package(PkgConfig REQUIRED)
pkg_search_module(CAPSTONE 4.0.2 REQUIRED capstone)
endif()
set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF)
add_subdirectory(${EXTERNAL_LIBS_FOLDER}/pattern_language EXCLUDE_FROM_ALL)
set_target_properties(libpl PROPERTIES POSITION_INDEPENDENT_CODE ON)
@@ -673,9 +682,11 @@ function(generatePDBs)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb
WORKING_DIRECTORY ${cv2pdb_SOURCE_DIR}
COMMAND
(${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb &&
${cv2pdb_SOURCE_DIR}/cv2pdb64.exe
$<TARGET_FILE:${PDB}>) || (exit 0)
(
${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb &&
${cv2pdb_SOURCE_DIR}/cv2pdb64.exe
$<TARGET_FILE:${PDB}>
) || (exit 0)
DEPENDS $<TARGET_FILE:${PDB}>
COMMAND_EXPAND_LISTS)
@@ -686,10 +697,33 @@ function(generatePDBs)
endfunction()
function(generateSDKDirectory)
set(SDK_PATH "./sdk")
if (WIN32)
set(SDK_PATH "./sdk")
elseif (APPLE)
set(SDK_PATH "${BUNDLE_NAME}/Contents/Resources/sdk")
else()
set(SDK_PATH "share/imhex/sdk")
endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex 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")
if (NOT USE_SYSTEM_FMT)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/fmt DESTINATION "${SDK_PATH}/lib/third_party")
endif()
if (NOT USE_SYSTEM_NLOHMANN_JSON)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/nlohmann_json DESTINATION "${SDK_PATH}/lib/third_party")
endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex/include DESTINATION "${SDK_PATH}")
install(FILES ${CMAKE_SOURCE_DIR}/cmake/modules/ImHexPlugin.cmake DESTINATION "${SDK_PATH}/cmake/modules")
install(FILES ${CMAKE_SOURCE_DIR}/cmake/build_helpers.cmake DESTINATION "${SDK_PATH}/cmake")
install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/sdk/ DESTINATION "${SDK_PATH}")
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()
function(addIncludesFromLibrary target library)
get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} PRIVATE ${library_include_dirs})
endfunction()

View File

@@ -1,10 +1,15 @@
macro(add_imhex_plugin)
# Parse arguments
set(options "")
set(oneValueArgs NAME)
set(multiValueArgs SOURCES INCLUDES LIBRARIES)
set(options LIBRARY_PLUGIN)
set(oneValueArgs NAME IMHEX_VERSION)
set(multiValueArgs SOURCES INCLUDES LIBRARIES FEATURES)
cmake_parse_arguments(IMHEX_PLUGIN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (IMHEX_PLUGIN_IMHEX_VERSION)
message(STATUS "Compiling plugin ${IMHEX_PLUGIN_NAME} for ImHex Version ${IMHEX_PLUGIN_IMHEX_VERSION}")
set(IMHEX_VERSION_STRING "${IMHEX_PLUGIN_IMHEX_VERSION}")
endif()
if (IMHEX_STATIC_LINK_PLUGINS)
set(IMHEX_PLUGIN_LIBRARY_TYPE STATIC)
@@ -12,8 +17,15 @@ macro(add_imhex_plugin)
configure_file(${CMAKE_SOURCE_DIR}/dist/web/plugin-bundle.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp @ONLY)
target_sources(main PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp)
set(IMHEX_PLUGIN_SUFFIX ".hexplug")
else()
set(IMHEX_PLUGIN_LIBRARY_TYPE MODULE)
if (IMHEX_PLUGIN_LIBRARY_PLUGIN)
set(IMHEX_PLUGIN_LIBRARY_TYPE SHARED)
set(IMHEX_PLUGIN_SUFFIX ".hexpluglib")
else()
set(IMHEX_PLUGIN_LIBRARY_TYPE MODULE)
set(IMHEX_PLUGIN_SUFFIX ".hexplug")
endif()
endif()
# Define new project for plugin
@@ -24,7 +36,8 @@ macro(add_imhex_plugin)
# Add include directories and link libraries
target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES})
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} ${IMHEX_PLUGIN_LIBRARIES})
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${IMHEX_PLUGIN_LIBRARIES} ${FMT_LIBRARIES} imgui_all_includes libwolv)
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl)
# Add IMHEX_PROJECT_NAME and IMHEX_VERSION define
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}")
@@ -42,7 +55,7 @@ macro(add_imhex_plugin)
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
CXX_STANDARD 23
PREFIX ""
SUFFIX ".hexplug"
SUFFIX ${IMHEX_PLUGIN_SUFFIX}
)
# Setup a romfs for the plugin
@@ -52,12 +65,38 @@ macro(add_imhex_plugin)
set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE ${LIBROMFS_LIBRARY})
foreach(feature ${IMHEX_PLUGIN_FEATURES})
string(TOUPPER ${feature} feature)
add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=0)
endforeach()
# Add the new plugin to the main dependency list so it gets built by default
add_dependencies(imhex_all ${IMHEX_PLUGIN_NAME})
if (TARGET imhex_all)
add_dependencies(imhex_all ${IMHEX_PLUGIN_NAME})
endif()
if (IMHEX_EXTERNAL_PLUGIN_BUILD)
install(TARGETS ${IMHEX_PLUGIN_NAME} DESTINATION ".")
# Fix rpath
if (APPLE)
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
endif()
endif()
endmacro()
macro(add_romfs_resource input output)
configure_file(${input} ${CMAKE_CURRENT_BINARY_DIR}/romfs/${output} COPYONLY)
list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/romfs)
endmacro()
macro (enable_plugin_feature feature)
string(TOUPPER ${feature} feature)
if (NOT (feature IN_LIST IMHEX_PLUGIN_FEATURES))
message(FATAL_ERROR "Feature ${feature} is not enabled for plugin ${IMHEX_PLUGIN_NAME}")
endif()
remove_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=0)
add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=1)
endmacro()

View File

@@ -35,26 +35,27 @@ message(STATUS "Fixing up application bundle: ${BUNDLE_PATH}")
# Make sure to fix up any included ImHex plugin.
file(GLOB_RECURSE extra_libs "${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
# makes it sometimes break on libraries that do weird things with @rpath. Specify
# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625
# is fixed and in our minimum CMake version.
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS})
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins")
message(STATUS "Fixing up application bundle: ${extra_dirs}")
# BundleUtilities is overly verbose, so disable most of its messages
function(message)
if(NOT ARGV MATCHES "^STATUS;")
_message(${ARGV})
endif()
endfunction()
#function(message)
# if(NOT ARGV MATCHES "^STATUS;")
# _message(${ARGV})
# endif()
#endfunction()
include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS ON)
fixup_bundle("${BUNDLE_PATH}" "${extra_libs}" "${extra_dirs}")
fixup_bundle("${BUNDLE_PATH}" "${plugins};${plugin_libs}" "${extra_dirs}")
if (CODE_SIGN_CERTIFICATE_ID)
# Hack around Apple Silicon signing bugs by copying the real app, signing it and moving it back.

48
cmake/sdk/CMakeLists.txt Normal file
View File

@@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.20)
project(ImHexSDK)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_helpers.cmake")
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
include(ImHexPlugin)
function(add_subdirectory_if_exists folder)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${folder}/CMakeLists.txt")
add_subdirectory("${folder}" EXCLUDE_FROM_ALL)
endif()
endfunction()
set(IMHEX_EXTERNAL_PLUGIN_BUILD ON PARENT_SCOPE)
set(IMHEX_EXTERNAL_PLUGIN_BUILD ON)
add_custom_target(imhex_all)
add_subdirectory(lib/third_party/imgui EXCLUDE_FROM_ALL)
set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
add_subdirectory_if_exists(lib/third_party/fmt)
set(FMT_LIBRARIES fmt::fmt-header-only PARENT_SCOPE)
add_subdirectory_if_exists(lib/third_party/nlohmann_json)
add_subdirectory(lib/external/libwolv EXCLUDE_FROM_ALL)
set(LIBPL_ENABLE_CLI OFF CACHE BOOL "" FORCE)
add_subdirectory(lib/external/pattern_language EXCLUDE_FROM_ALL)
add_subdirectory(lib/libimhex)
if (WIN32)
set_target_properties(libimhex PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.dll"
IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/liblibimhex.dll.a"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
elseif (APPLE)
set_target_properties(libimhex PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.dylib"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
else()
set_target_properties(libimhex PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.so"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
endif()

View File

@@ -0,0 +1,60 @@
# ImHex Plugin Template
# =====================
# This is the official CMake template for making your own ImHex plugins
# To use this template, copy the file into its own directory and modify it to your needs
# For the most part, this is a regular CMake project with some extra functions provided by the ImHex SDK
#
# [NOTE FOR NON-C++ PLUGINS]
# The template is laid out for a C++ plugin, however you can write your plugin in any language you want
# and just make the plugin statically link against your code. The only thing that's required is a .cpp file with
# the IMHEX_PLUGIN_SETUP() macro used in it. This macro is used to setup the plugin and register it with ImHex
#
# [CMAKE FUNCTIONS]
# add_imhex_plugin(): Registers a new plugin
# NAME: The name of the plugin
# IMHEX_VERSION: The ImHex version this plugin is compatible with. If unset, the plugin will be loaded on all versions (this may not work though)
# SOURCES: Source files of the plugin
# INCLUDES: Include directories of the plugin
# LIBRARIES: Libraries to link against
# FEATURES: Optional features that can be enabled or disabled
# LIBRARY_PLUGIN: If set, turns this plugin into a library plugin. Library plugins can be linked against by other plugins
#
# add_romfs_resource(filePath romfsPath): Adds a file to the romfs of the plugin
# The RomFS is a virtual filesystem whose files can be accessed by the plugin using the functions in the `romfs::` namespace
# This function is used to add a single file to the romfs. You can however also simply create a `romfs` directory in your plugin directory and place your files and folders in there
# filePath: The path to the file on the disk
# romfsPath: The path to the file in the romfs
#
# enable_plugin_feature(feature): Enables a plugin feature
# Features are optional parts of the plugin that may or may not be available depending on build settings
# When a feature is enabled, `IMHEX_FEATURE_ENABLED(feature)` will be defined to true. Otherwise, it will be defined to false
# Use the `IMHEX_PLUGIN_FEATURES` macro in the main plugin file to define names to each feature and have them be listed in the plugin list
# feature: The name of the feature to enable
cmake_minimum_required(VERSION 3.20)
project(ImHexPlugin)
# Include the ImHex SDK
# For this to work, you need to set the IMHEX_SDK_PATH environment variable to the path of the ImHex SDK
#
# On Windows, the SDK is next to the ImHex executable
# On Linux, the SDK is usually in /usr/share/imhex/sdk but this may vary depending on your distribution
# On MacOS, the SDK is located inside of the ImHex.app bundle under ImHex.app/Contents/Resources/sdk
if (NOT EXISTS $ENV{IMHEX_SDK_PATH})
message(FATAL_ERROR "The IMHEX_SDK_PATH environment variable is not set")
endif()
add_subdirectory($ENV{IMHEX_SDK_PATH} ImHexSDK)
# Register the plugin
# This will configure everything you need to make your plugin work
# Modify the arguments to your needs. Right now it defines a plugin called `example_plugin`
# with a single source file called `example_plugin.cpp` in the `source` directory
# By default you have access to the libimhex library to interact with ImHex
# as well as libwolv, libromfs, libfmt and ImGui, but you can link against any libraries you want
add_imhex_plugin(
NAME
example_plugin
SOURCES
source/example_plugin.cpp
)

View File

@@ -0,0 +1,11 @@
#include <hex/plugin.hpp>
// Browse through the headers in lib/libimhex/include/hex/api/ to see what you can do with the API.
// Most important ones are <hex/api/imhex_api.hpp> and <hex/api/content_registry.hpp>
// This is the main entry point of your plugin. The code in the body of this construct will be executed
// when ImHex starts up and loads the plugin.
// The strings in the header are used to display information about the plugin in the UI.
IMHEX_PLUGIN_SETUP("Example Plugin", "Author", "Description") {
// Put your init code here
}

5
dist/Arch/PKGBUILD vendored
View File

@@ -8,7 +8,7 @@ pkgdesc="A Hex Editor for Reverse Engineers, Programmers and people who value th
arch=("x86_64")
url="https://github.com/WerWolv/ImHex"
license=('GPL2')
depends=(glfw mbedtls freetype2 libglvnd dbus gtk3 curl fmt yara nlohmann-json)
depends=(glfw mbedtls freetype2 libglvnd dbus gtk3 curl fmt yara nlohmann-json zlib bzip2 xz zstd)
makedepends=(git)
provides=(imhex)
conflicts=(imhex)
@@ -17,9 +17,10 @@ md5sums=(SKIP)
package() {
install -Dm755 "$srcdir/usr/bin/imhex" "$pkgdir/usr/bin/imhex"
install -Dm755 "$srcdir/usr/bin/imhex-updater" "$pkgdir/usr/bin/imhex-updater"
install -Dm644 "$srcdir/usr/lib/libimhex.so.$pkgver" "$pkgdir/usr/lib/libimhex.so.$pkgver"
for plugin in "$srcdir/usr/lib/imhex/plugins/"*.hexplug; do
for plugin in "$srcdir/usr/lib/imhex/plugins/"*.hexplug*; do
install -Dm644 "$plugin" "$pkgdir/usr/lib/imhex/plugins/${plugin##*/}"
done

6
dist/Brewfile vendored
View File

@@ -9,4 +9,8 @@ brew "curl"
brew "gcc@12"
brew "llvm"
brew "glfw"
brew "ninja"
brew "ninja"
brew "zlib"
brew "xz"
brew "bzip2"
brew "zstd"

View File

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

View File

@@ -24,5 +24,9 @@ RDEPEND="${DEPEND}
dev-cpp/nlohmann_json
dbus
xdg-desktop-portal
sys-libs/zlib
app-arch/bzip2
app-arch/lzma
app-arch/zstd
"
BDEPEND="${DEPEND}"

View File

@@ -1,17 +1,21 @@
#!/usr/bin/env sh
pacman -S $@ --needed \
cmake \
gcc \
lld \
glfw \
file \
mbedtls \
freetype2 \
dbus \
gtk3 \
curl \
fmt \
yara \
cmake \
gcc \
lld \
glfw \
file \
mbedtls \
freetype2 \
dbus \
gtk3 \
curl \
fmt \
yara \
nlohmann-json \
ninja
ninja \
zlib \
bzip2 \
xz \
zstd

View File

@@ -22,4 +22,8 @@ apt install -y \
libdbus-1-dev \
libcurl4-gnutls-dev \
libgtk-3-dev \
ninja-build
ninja-build \
zlib1g-dev \
libbz2-dev \
liblzma-dev \
libzstd-dev

View File

@@ -12,4 +12,8 @@ dnf install -y \
glfw-devel \
lld \
mbedtls-devel \
gtk3-devel
gtk3-devel \
libzstd-devel \
zlib-devel \
bzip2-devel \
xz-devel

View File

@@ -12,4 +12,8 @@ pacman -S --needed --noconfirm \
mingw-w64-x86_64-freetype \
mingw-w64-x86_64-dlfcn \
mingw-w64-x86_64-ninja \
mingw-w64-x86_64-capstone
mingw-w64-x86_64-capstone \
mingw-w64-x86_64-zlib \
mingw-w64-x86_64-bzip2 \
mingw-w64-x86_64-xz \
mingw-w64-x86_64-zstd

View File

@@ -52,6 +52,10 @@ vcpkg install --triplet=arm-osx-mytriplet curl
vcpkg install --triplet=arm-osx-mytriplet mbedtls
vcpkg install --triplet=arm-osx-mytriplet freetype
vcpkg install --triplet=arm-osx-mytriplet josuttis-jthread
vcpkg install --triplet=arm-osx-mytriplet zlib
vcpkg install --triplet=arm-osx-mytriplet bzip2
vcpkg install --triplet=arm-osx-mytriplet liblzma
vcpkg install --triplet=arm-osx-mytriplet zstd
EOF
## Install glfw3 dep
@@ -102,6 +106,9 @@ if [ "$CUSTOM_GLFW" ]; then
fi
EOF
RUN mkdir -p /vcpkg/installed/arm-osx-mytriplet/lib/pkgconfig
RUN mkdir -p /osxcross/target/macports/pkgs/vcpkg/installed/arm-osx-mytriplet/lib/pkgconfig
## Build glfw
RUN --mount=type=cache,target=/cache <<EOF
set -xe
@@ -148,6 +155,7 @@ RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/
-DIMHEX_GENERATE_PACKAGE=ON -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
`# other flags` \
-DIMHEX_STRICT_WARNINGS=OFF \
-DCMAKE_INSTALL_PREFIX=/mnt/ImHex/build/install \
-B build
## Build ImHex
RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps <<EOF
@@ -155,11 +163,11 @@ RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/
set -xe
cd /mnt/ImHex
cmake --build build --parallel $JOBS
cmake --build build --parallel $JOBS --target install
ccache -s
EOF
FROM scratch
COPY --from=build /mnt/ImHex/build/imhex.app imhex.app
COPY --from=build /mnt/ImHex/build/install/imhex.app ImHex.app

6
dist/msys2/PKGBUILD vendored
View File

@@ -16,7 +16,11 @@ makedepends=("${MINGW_PACKAGE_PREFIX}-gcc"
"${MINGW_PACKAGE_PREFIX}-file"
"${MINGW_PACKAGE_PREFIX}-mbedtls"
"${MINGW_PACKAGE_PREFIX}-polly"
"${MINGW_PACKAGE_PREFIX}-freetype")
"${MINGW_PACKAGE_PREFIX}-freetype"
"${MINGW_PACKAGE_PREFIX}-zlib"
"${MINGW_PACKAGE_PREFIX}-bzip2"
"${MINGW_PACKAGE_PREFIX}-xz"
"${MINGW_PACKAGE_PREFIX}-zstd")
source=()
sha256sums=()

View File

@@ -12,49 +12,10 @@ finish-args:
- --device=all
modules:
- name: libiconv
sources:
- type: archive
url: https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.16.tar.gz
sha256: e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04
- name: glfw
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DBUILD_SHARED_LIBS:BOOL=ON
sources:
- type: archive
url: https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
sha256: 08a33a512f29d7dbf78eab39bd7858576adcc95228c9efe8e4bc5f0f3261efc7
cleanup:
- /include
- /lib/pkgconfig
- name: mbedtls
buildsystem: cmake-ninja
config-opts:
- -DCMAKE_C_FLAGS=-fPIC
sources:
- type: archive
url: https://github.com/ARMmbed/mbedtls/archive/refs/tags/v2.27.0.tar.gz
sha256: 2a07856e541f0e5f6eaee4f78018c52f25bd244ed76f9020dea54a8b02cac6ea
- name: nlohmann-json
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DBUILD_TESTING=OFF
sources:
- type: archive
url: https://github.com/nlohmann/json/archive/v3.9.1.tar.gz
sha256: 4cf0df69731494668bdd6460ed8cb269b68de9c19ad8c27abc24cd72605b2d5b
- name: imhex
buildsystem: cmake
config-opts:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
sources:
- type: git

5
dist/rpm/imhex.spec vendored
View File

@@ -27,6 +27,10 @@ BuildRequires: mbedtls-devel
BuildRequires: yara-devel
BuildRequires: nativefiledialog-extended-devel
BuildRequires: dotnet-sdk-7.0
BuildRequires: libzstd-devel
BuildRequires: zlib-devel
BuildRequires: bzip2-devel
BuildRequires: xz-devel
%if 0%{?rhel}
BuildRequires: gcc-toolset-12
%endif
@@ -120,6 +124,7 @@ cp -a lib/third_party/xdgpp/LICENSE %{buildroot
%license %{_datadir}/licenses/%{name}/
%doc README.md
%{_bindir}/imhex
%{_bindir}/imhex-updater
%{_datadir}/pixmaps/%{name}.png
%{_datadir}/applications/%{name}.desktop
%{_libdir}/libimhex.so*

7
dist/web/Dockerfile vendored
View File

@@ -37,6 +37,10 @@ mkdir -p $VCPKG_DEFAULT_BINARY_CACHE
/vcpkg/vcpkg install --triplet=wasm32-emscripten libmagic
/vcpkg/vcpkg install --triplet=wasm32-emscripten freetype
/vcpkg/vcpkg install --triplet=wasm32-emscripten mbedtls
/vcpkg/vcpkg install --triplet=wasm32-emscripten zlib
/vcpkg/vcpkg install --triplet=wasm32-emscripten bzip2
/vcpkg/vcpkg install --triplet=wasm32-emscripten liblzma
/vcpkg/vcpkg install --triplet=wasm32-emscripten zstd
EOF
# Build ImHex
@@ -54,6 +58,7 @@ ccache -zs
cmake /imhex \
-DIMHEX_OFFLINE_BUILD=ON \
-DIMHEX_STATIC_LINK_PLUGINS=ON \
-DIMHEX_EXCLUDE_PLUGINS="script_loader" \
-DNATIVE_CMAKE_C_COMPILER=gcc \
-DNATIVE_CMAKE_CXX_COMPILER=g++ \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
@@ -83,6 +88,8 @@ COPY --from=build [ \
"/build/favicon.ico", \
"/build/icon.png", \
"/build/manifest.json", \
"/build/robots.txt", \
"/build/sitemap.xml", \
\
# Destination \
"./" \

View File

@@ -1,23 +1,22 @@
<!doctype html>
<html lang="en-us">
<head>
<link rel="manifest" href="manifest.json" />
<meta charset="utf-8">
<title>ImHex Web - Free Online Hex Editor for Reverse Engineers</title>
<link rel="manifest" href="manifest.json">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Primary Meta Tags -->
<title>ImHex Web - Online Hex Editor</title>
<meta name="title" content="ImHex">
<meta name="description" content="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.">
<meta name="description" content="Free and extremely powerful Online Hex Editor for your Web Browser. ImHex is a free and open source Hex Editor for Reverse Engineers and Developers and Data Analysts.">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="apple-touch-icon" href="icon.png">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://imhex.werwolv.net/">
<meta property="og:title" content="ImHex Web - Online Hex Editor">
<meta property="og:description">
<meta name="description" content="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.">
<meta property="og:image" content="https://imhex.werwolv.net/assets/splash_wasm.png">
<!-- Twitter -->
@@ -28,9 +27,7 @@
content="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.">
<meta property="twitter:image" content="https://imhex.werwolv.net/assets/splash_wasm.png">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="stylesheet" type="text/css" href="style.css">
<script type="application/ld+json">
{
@@ -60,15 +57,41 @@
}
</script>
<title>ImHex Web</title>
<script src="enable-threads.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<p id="loading_text">ImHex is loading...</p>
<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">
<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>
<h5>ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.</h5>
<div style="height: 50%">
<div style="height: 30%"> </div>
<h2 id="not_working">
Not loading in your Browser? <a href="https://imhex.werwolv.net">Try the native version</a>
</h2>
<div style="height: 50%"></div>
</div>
<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 type="text/javascript" src="wasm-config.js"></script>
<script async type="text/javascript" src="imhex.js"></script>
<script src="wasm-config.js"></script>
<script async src="imhex.js"></script>
</body>
</html>

4
dist/web/source/robots.txt vendored Normal file
View File

@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://imhex.werwolv.net/sitemap.xml

14
dist/web/source/sitemap.xml vendored Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>https://web.imhex.werwolv.net/</loc>
<lastmod>2023-12-07T22:53:06+00:00</lastmod>
</url>
</urlset>

View File

@@ -1,6 +1,6 @@
html, body {
height: 100%;
margin: 0px;
margin: 0;
user-select: none;
}
@@ -16,21 +16,120 @@ body {
margin-left: auto;
margin-right: auto;
display: none;
border: 0px none;
border: 0 none;
}
.canvas_full_screen {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
#loading_text {
h1, h2, h5 {
color: #F0F0F0;
font-size: 30px;
font-size: 20px;
font-family: monospace;
width: 100%;
text-align: center;
margin-top: 60px;
margin-bottom: 10px;
}
h2 {
margin-top: 15px;
font-size: 17px;
}
h5 {
margin-top: 0;
font-size: 17px;
}
#not_working {
opacity: 0;
}
#not_working.visible {
opacity: 1;
transition: opacity 2s ease;
}
a {
color: #7893ff;
text-decoration: none;
}
a:hover {
text-shadow: #3a4677 0 0 10px;
}
.footer {
width: 100%;
height: 20px;
position: absolute;
bottom: 0;
text-align: center;
color: #F0F0F0;
background-color: #0A0A0A;
padding: 10px;
font-family: monospace;
font-size: 15px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
gap: 10%;
}
.centered {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.lds-ripple {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ripple div {
position: absolute;
border: 4px solid #fff;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
.lds-ripple div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes lds-ripple {
0% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 0;
}
4.9% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 0;
}
5% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0;
left: 0;
width: 72px;
height: 72px;
opacity: 0;
}
}

View File

@@ -30,16 +30,22 @@ function glfwCreateStandardCursorCustom(shape) {
return shape
}
var notWorkingTimer = setTimeout(() => {
document.getElementById("not_working").classList.add("visible")
}, 5000);
var Module = {
preRun: [],
postRun: [],
onRuntimeInitialized: function() {
// Triggered when the wasm module is loaded and ready to use.
document.getElementById("loading_text").style.display = "none"
document.getElementById("loading").style.display = "none"
document.getElementById("canvas").style.display = "initial"
clearTimeout(notWorkingTimer);
},
print: (function() { })(),
printErr: function(text) { },
printErr: function(text) { },
canvas: (function() {
let canvas = document.getElementById('canvas');
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
@@ -67,4 +73,10 @@ function js_resizeCanvas() {
canvas.height = Math.min(document.documentElement.clientHeight, window.innerHeight || 0);
canvas.classList.add("canvas_full_screen")
if (GLFW.active && GLFW.active.windowPosFunc) {
getWasmTableEntry(GLFW.active.windowPosFunc)(GLFW.active.id, GLFW.active.x, GLFW.active.y);
}
GLFW.onWindowSizeChanged();
}

View File

@@ -14,8 +14,10 @@ set(LIBIMHEX_SOURCES
source/api/project_file_manager.cpp
source/api/theme_manager.cpp
source/api/layout_manager.cpp
source/api/workspace_manager.cpp
source/api/achievement_manager.cpp
source/api/localization_manager.cpp
source/api/tutorial_manager.cpp
source/data_processor/attribute.cpp
source/data_processor/link.cpp
@@ -33,16 +35,17 @@ set(LIBIMHEX_SOURCES
source/helpers/patches.cpp
source/helpers/encoding_file.cpp
source/helpers/logger.cpp
source/helpers/stacktrace.cpp
source/helpers/tar.cpp
source/helpers/debugging.cpp
source/providers/provider.cpp
source/providers/memory_provider.cpp
source/providers/undo/stack.cpp
source/ui/imgui_imhex_extensions.cpp
source/ui/view.cpp
source/ui/popup.cpp
source/ui/toast.cpp
source/subcommands/subcommands.cpp
)
@@ -60,12 +63,18 @@ if (APPLE)
set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES} source/helpers/utils_macos.m)
endif ()
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
if (IMHEX_STATIC_LINK_PLUGINS)
add_library(libimhex STATIC ${LIBIMHEX_SOURCES})
if (IMHEX_EXTERNAL_PLUGIN_BUILD)
add_library(libimhex IMPORTED SHARED GLOBAL)
set(LIBIMHEX_LIBRARY_TYPE INTERFACE)
else()
add_library(libimhex SHARED ${LIBIMHEX_SOURCES})
if (IMHEX_STATIC_LINK_PLUGINS)
add_library(libimhex STATIC ${LIBIMHEX_SOURCES})
else()
add_library(libimhex SHARED ${LIBIMHEX_SOURCES})
endif()
set(LIBIMHEX_LIBRARY_TYPE PUBLIC)
target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
endif()
set_target_properties(libimhex PROPERTIES POSITION_INDEPENDENT_CODE ON)
@@ -75,26 +84,30 @@ setupCompilerFlags(libimhex)
include(GenerateExportHeader)
generate_export_header(libimhex)
target_include_directories(libimhex PUBLIC include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${CAPSTONE_INCLUDE_DIRS} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${YARA_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
target_link_directories(libimhex PUBLIC ${MBEDTLS_LIBRARY_DIR} ${CAPSTONE_LIBRARY_DIRS} ${MAGIC_LIBRARY_DIRS})
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 PUBLIC ${CURL_INCLUDE_DIRS})
target_link_libraries(libimhex PUBLIC ${LIBCURL_LIBRARIES})
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_INCLUDE_DIRS})
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${LIBCURL_LIBRARIES})
endif()
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 ()
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 ${FMT_LIBRARIES})
target_link_libraries(libimhex PUBLIC dl ${IMGUI_LIBRARIES} ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} plcli libpl libpl-gen ${MINIAUDIO_LIBRARIES} ${JTHREAD_LIBRARIES} wolv::utils wolv::io wolv::hash wolv::net wolv::containers wolv::math_eval)
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)
@@ -110,6 +123,7 @@ else()
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE RESULT_BRANCH
ERROR_QUIET
)
# Get the latest abbreviated commit hash of the working branch
@@ -119,6 +133,7 @@ else()
OUTPUT_VARIABLE GIT_COMMIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE RESULT_HASH_SHORT
ERROR_QUIET
)
execute_process(
@@ -127,6 +142,7 @@ else()
OUTPUT_VARIABLE GIT_COMMIT_HASH_LONG
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE RESULT_HASH_LONG
ERROR_QUIET
)
endif ()

View File

@@ -13,6 +13,7 @@
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/api/localization_manager.hpp>
namespace hex {
@@ -20,22 +21,22 @@ namespace hex {
class Achievement {
public:
explicit Achievement(std::string unlocalizedCategory, std::string unlocalizedName) : m_unlocalizedCategory(std::move(unlocalizedCategory)), m_unlocalizedName(std::move(unlocalizedName)) { }
explicit Achievement(UnlocalizedString unlocalizedCategory, UnlocalizedString unlocalizedName) : m_unlocalizedCategory(std::move(unlocalizedCategory)), m_unlocalizedName(std::move(unlocalizedName)) { }
/**
* @brief Returns the unlocalized name of the achievement
* @return Unlocalized name of the achievement
*/
[[nodiscard]] const std::string &getUnlocalizedName() const {
return this->m_unlocalizedName;
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const {
return m_unlocalizedName;
}
/**
* @brief Returns the unlocalized category of the achievement
* @return Unlocalized category of the achievement
*/
[[nodiscard]] const std::string &getUnlocalizedCategory() const {
return this->m_unlocalizedCategory;
[[nodiscard]] const UnlocalizedString &getUnlocalizedCategory() const {
return m_unlocalizedCategory;
}
/**
@@ -43,7 +44,7 @@ namespace hex {
* @return Whether the achievement is unlocked
*/
[[nodiscard]] bool isUnlocked() const {
return this->m_progress == this->m_maxProgress;
return m_progress == m_maxProgress;
}
/**
@@ -52,7 +53,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setDescription(std::string description) {
this->m_unlocalizedDescription = std::move(description);
m_unlocalizedDescription = std::move(description);
return *this;
}
@@ -63,7 +64,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& addRequirement(std::string requirement) {
this->m_requirements.emplace_back(std::move(requirement));
m_requirements.emplace_back(std::move(requirement));
return *this;
}
@@ -74,7 +75,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& addVisibilityRequirement(std::string requirement) {
this->m_visibilityRequirements.emplace_back(std::move(requirement));
m_visibilityRequirements.emplace_back(std::move(requirement));
return *this;
}
@@ -84,7 +85,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setBlacked() {
this->m_blacked = true;
m_blacked = true;
return *this;
}
@@ -94,7 +95,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setInvisible() {
this->m_invisible = true;
m_invisible = true;
return *this;
}
@@ -104,7 +105,7 @@ namespace hex {
* @return Whether the achievement is blacked
*/
[[nodiscard]] bool isBlacked() const {
return this->m_blacked;
return m_blacked;
}
/**
@@ -112,7 +113,7 @@ namespace hex {
* @return Whether the achievement is invisible
*/
[[nodiscard]] bool isInvisible() const {
return this->m_invisible;
return m_invisible;
}
/**
@@ -120,7 +121,7 @@ namespace hex {
* @return List of requirements of the achievement
*/
[[nodiscard]] const std::vector<std::string> &getRequirements() const {
return this->m_requirements;
return m_requirements;
}
/**
@@ -128,15 +129,15 @@ namespace hex {
* @return List of visibility requirements of the achievement
*/
[[nodiscard]] const std::vector<std::string> &getVisibilityRequirements() const {
return this->m_visibilityRequirements;
return m_visibilityRequirements;
}
/**
* @brief Returns the unlocalized description of the achievement
* @return Unlocalized description of the achievement
*/
[[nodiscard]] const std::string &getUnlocalizedDescription() const {
return this->m_unlocalizedDescription;
[[nodiscard]] const UnlocalizedString &getUnlocalizedDescription() const {
return m_unlocalizedDescription;
}
/**
@@ -144,15 +145,15 @@ namespace hex {
* @return Icon of the achievement
*/
[[nodiscard]] const ImGuiExt::Texture &getIcon() const {
if (this->m_iconData.empty())
return this->m_icon;
if (this->m_icon.isValid())
if (m_iconData.empty())
return m_icon;
this->m_icon = ImGuiExt::Texture(this->m_iconData.data(), this->m_iconData.size());
if (m_icon.isValid())
return m_icon;
return this->m_icon;
m_icon = ImGuiExt::Texture(m_iconData.data(), m_iconData.size(), ImGuiExt::Texture::Filter::Linear);
return m_icon;
}
/**
@@ -161,9 +162,9 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setIcon(std::span<const std::byte> data) {
this->m_iconData.reserve(data.size());
m_iconData.reserve(data.size());
for (auto &byte : data)
this->m_iconData.emplace_back(static_cast<u8>(byte));
m_iconData.emplace_back(static_cast<u8>(byte));
return *this;
}
@@ -174,7 +175,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setIcon(std::span<const u8> data) {
this->m_iconData.assign(data.begin(), data.end());
m_iconData.assign(data.begin(), data.end());
return *this;
}
@@ -185,7 +186,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setIcon(std::vector<u8> data) {
this->m_iconData = std::move(data);
m_iconData = std::move(data);
return *this;
}
@@ -196,9 +197,9 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setIcon(const std::vector<std::byte> &data) {
this->m_iconData.reserve(data.size());
m_iconData.reserve(data.size());
for (auto &byte : data)
this->m_iconData.emplace_back(static_cast<u8>(byte));
m_iconData.emplace_back(static_cast<u8>(byte));
return *this;
}
@@ -209,7 +210,7 @@ namespace hex {
* @return Reference to the achievement
*/
Achievement& setRequiredProgress(u32 progress) {
this->m_maxProgress = progress;
m_maxProgress = progress;
return *this;
}
@@ -219,7 +220,7 @@ namespace hex {
* @return Required progress to unlock the achievement
*/
[[nodiscard]] u32 getRequiredProgress() const {
return this->m_maxProgress;
return m_maxProgress;
}
/**
@@ -227,7 +228,7 @@ namespace hex {
* @return Current progress of the achievement
*/
[[nodiscard]] u32 getProgress() const {
return this->m_progress;
return m_progress;
}
/**
@@ -235,7 +236,7 @@ namespace hex {
* @param callback Callback to call when the achievement is clicked
*/
void setClickCallback(const std::function<void(Achievement &)> &callback) {
this->m_clickCallback = callback;
m_clickCallback = callback;
}
/**
@@ -243,7 +244,7 @@ namespace hex {
* @return Callback to call when the achievement is clicked
*/
[[nodiscard]] const std::function<void(Achievement &)> &getClickCallback() const {
return this->m_clickCallback;
return m_clickCallback;
}
/**
@@ -251,7 +252,7 @@ namespace hex {
* @return Whether the achievement is temporary
*/
[[nodiscard]] bool isTemporary() const {
return this->m_temporary;
return m_temporary;
}
/**
@@ -260,21 +261,21 @@ namespace hex {
*/
void setUnlocked(bool unlocked) {
if (unlocked) {
if (this->m_progress < this->m_maxProgress)
this->m_progress++;
if (m_progress < m_maxProgress)
m_progress++;
} else {
this->m_progress = 0;
m_progress = 0;
}
}
protected:
void setProgress(u32 progress) {
this->m_progress = progress;
m_progress = progress;
}
private:
std::string m_unlocalizedCategory, m_unlocalizedName;
std::string m_unlocalizedDescription;
UnlocalizedString m_unlocalizedCategory, m_unlocalizedName;
UnlocalizedString m_unlocalizedDescription;
bool m_blacked = false;
bool m_invisible = false;
@@ -364,7 +365,7 @@ namespace hex {
* @param unlocalizedCategory Unlocalized category of the achievement
* @param unlocalizedName Unlocalized name of the achievement
*/
static void unlockAchievement(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
static void unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName);
/**
* @brief Returns all registered achievements

View File

@@ -1,21 +1,19 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/helpers/concepts.hpp>
#include <pl/pattern_language.hpp>
#include <functional>
#include <map>
#include <span>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <jthread.hpp>
#include <pl/pattern_language.hpp>
#include <nlohmann/json.hpp>
#include <wolv/io/fs.hpp>
using ImGuiDataType = int;
using ImGuiInputTextFlags = int;
@@ -63,32 +61,32 @@ namespace hex {
friend class Widget;
Interface& requiresRestart() {
this->m_requiresRestart = true;
m_requiresRestart = true;
return *this;
}
Interface& setEnabledCallback(std::function<bool()> callback) {
this->m_enabledCallback = std::move(callback);
m_enabledCallback = std::move(callback);
return *this;
}
Interface& setChangedCallback(std::function<void(Widget&)> callback) {
this->m_changedCallback = std::move(callback);
m_changedCallback = std::move(callback);
return *this;
}
Interface& setTooltip(const std::string &tooltip) {
this->m_tooltip = tooltip;
m_tooltip = tooltip;
return *this;
}
[[nodiscard]]
Widget& getWidget() const {
return *this->m_widget;
return *m_widget;
}
private:
@@ -103,34 +101,34 @@ namespace hex {
[[nodiscard]]
bool doesRequireRestart() const {
return this->m_interface.m_requiresRestart;
return m_interface.m_requiresRestart;
}
[[nodiscard]]
bool isEnabled() const {
return !this->m_interface.m_enabledCallback || this->m_interface.m_enabledCallback();
return !m_interface.m_enabledCallback || m_interface.m_enabledCallback();
}
[[nodiscard]]
const std::optional<std::string>& getTooltip() const {
return this->m_interface.m_tooltip;
return m_interface.m_tooltip;
}
void onChanged() {
if (this->m_interface.m_changedCallback)
this->m_interface.m_changedCallback(*this);
if (m_interface.m_changedCallback)
m_interface.m_changedCallback(*this);
}
[[nodiscard]]
Interface& getInterface() {
return this->m_interface;
return m_interface;
}
private:
Interface m_interface = Interface(this);
};
class Checkbox : public Widget {
class Checkbox : public Widget {
public:
explicit Checkbox(bool defaultValue) : m_value(defaultValue) { }
@@ -139,11 +137,12 @@ namespace hex {
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]] bool isChecked() const { return this->m_value; }
[[nodiscard]] bool isChecked() const { return m_value; }
private:
bool m_value;
};
class SliderInteger : public Widget {
public:
SliderInteger(i32 defaultValue, i32 min, i32 max) : m_value(defaultValue), m_min(min), m_max(max) { }
@@ -152,13 +151,14 @@ namespace hex {
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]] i32 getValue() const { return this->m_value; }
[[nodiscard]] i32 getValue() const { return m_value; }
private:
int m_value;
i32 m_min, m_max;
};
class SliderFloat : public Widget {
class SliderFloat : public Widget {
public:
SliderFloat(float defaultValue, float min, float max) : m_value(defaultValue), m_min(min), m_max(max) { }
bool draw(const std::string &name) override;
@@ -166,13 +166,14 @@ namespace hex {
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]] float getValue() const { return this->m_value; }
[[nodiscard]] float getValue() const { return m_value; }
private:
float m_value;
float m_min, m_max;
};
class ColorPicker : public Widget {
class ColorPicker : public Widget {
public:
explicit ColorPicker(ImColor defaultColor);
@@ -186,7 +187,8 @@ namespace hex {
private:
std::array<float, 4> m_value{};
};
class DropDown : public Widget {
class DropDown : public Widget {
public:
explicit DropDown(const std::vector<std::string> &items, const std::vector<nlohmann::json> &settingsValues, const nlohmann::json &defaultItem) : m_items(items), m_settingsValues(settingsValues), m_defaultItem(defaultItem) { }
@@ -205,7 +207,8 @@ namespace hex {
int m_value = -1;
};
class TextBox : public Widget {
class TextBox : public Widget {
public:
explicit TextBox(std::string defaultValue) : m_value(std::move(defaultValue)) { }
@@ -215,43 +218,52 @@ namespace hex {
nlohmann::json store() override;
[[nodiscard]]
const std::string& getValue() const { return this->m_value; }
const std::string& getValue() const { return m_value; }
private:
std::string m_value;
};
class FilePicker : public Widget {
class FilePicker : public Widget {
public:
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]]
std::fs::path getPath() const { return this->m_value; }
[[nodiscard]] std::fs::path getPath() const {
return m_value;
}
private:
std::string m_value;
};
class Label : public Widget {
public:
bool draw(const std::string &name) override;
void load(const nlohmann::json &) override {}
nlohmann::json store() override { return {}; }
};
}
namespace impl {
struct Entry {
std::string unlocalizedName;
UnlocalizedString unlocalizedName;
std::unique_ptr<Widgets::Widget> widget;
};
struct SubCategory {
std::string unlocalizedName;
UnlocalizedString unlocalizedName;
std::vector<Entry> entries;
};
struct Category {
std::string unlocalizedName;
std::string unlocalizedDescription;
UnlocalizedString unlocalizedName;
UnlocalizedString unlocalizedDescription;
std::vector<SubCategory> subCategories;
};
@@ -260,14 +272,14 @@ namespace hex {
void clear();
std::vector<Category> &getSettings();
nlohmann::json& getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue);
nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue);
nlohmann::json &getSettingsData();
Widgets::Widget* add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &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);
}
template<std::derived_from<Widgets::Widget> T>
Widgets::Widget::Interface& add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &unlocalizedName, auto && ... args) {
Widgets::Widget::Interface& add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, auto && ... args) {
return impl::add(
unlocalizedCategory,
unlocalizedSubCategory,
@@ -276,10 +288,10 @@ namespace hex {
)->getInterface();
}
void setCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedDescription);
void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription);
nlohmann::json read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue);
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &value);
[[nodiscard]] nlohmann::json read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue);
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value);
}
/* Command Palette Command Registry. Allows adding of new commands to the command palette */
@@ -304,7 +316,7 @@ namespace hex {
struct Entry {
Type type;
std::string command;
std::string unlocalizedDescription;
UnlocalizedString unlocalizedDescription;
DisplayCallback displayCallback;
ExecuteCallback executeCallback;
};
@@ -332,7 +344,7 @@ namespace hex {
void add(
Type type,
const std::string &command,
const std::string &unlocalizedDescription,
const UnlocalizedString &unlocalizedDescription,
const impl::DisplayCallback &displayCallback,
const impl::ExecuteCallback &executeCallback = [](auto) {});
@@ -470,7 +482,7 @@ namespace hex {
* @param unlocalizedName The unlocalized name of the view
* @return The view if it exists, nullptr otherwise
*/
View* getViewByName(const std::string &unlocalizedName);
View* getViewByName(const UnlocalizedString &unlocalizedName);
}
/* Tools Registry. Allows adding new entries to the tools window */
@@ -495,7 +507,7 @@ namespace hex {
* @param unlocalizedName The unlocalized name of the tool
* @param function The function that will be called to draw the tool
*/
void add(const std::string &unlocalizedName, const impl::Callback &function);
void add(const UnlocalizedString &unlocalizedName, const impl::Callback &function);
}
/* Data Inspector Registry. Allows adding of new types to the data inspector */
@@ -515,7 +527,7 @@ namespace hex {
using GeneratorFunction = std::function<DisplayFunction(const std::vector<u8> &, std::endian, NumberDisplayStyle)>;
struct Entry {
std::string unlocalizedName;
UnlocalizedString unlocalizedName;
size_t requiredSize;
size_t maxSize;
GeneratorFunction generatorFunction;
@@ -533,7 +545,7 @@ namespace hex {
* @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
*/
void add(const std::string &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
@@ -543,7 +555,7 @@ namespace hex {
* @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
*/
void add(const std::string &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 */
@@ -554,8 +566,8 @@ namespace hex {
using CreatorFunction = std::function<std::unique_ptr<dp::Node>()>;
struct Entry {
std::string category;
std::string name;
UnlocalizedString unlocalizedCategory;
UnlocalizedString unlocalizedName;
CreatorFunction creatorFunction;
};
@@ -574,10 +586,10 @@ namespace hex {
* @param args Arguments passed to the constructor of the node
*/
template<std::derived_from<dp::Node> T, typename... Args>
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, Args &&...args) {
void add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, Args &&...args) {
add(impl::Entry {
unlocalizedCategory.c_str(),
unlocalizedName.c_str(),
unlocalizedCategory,
unlocalizedName,
[=, ...args = std::forward<Args>(args)] mutable {
auto node = std::make_unique<T>(std::forward<Args>(args)...);
node->setUnlocalizedName(unlocalizedName);
@@ -622,11 +634,11 @@ namespace hex {
using ClickCallback = std::function<void()>;
struct MainMenuItem {
std::string unlocalizedName;
UnlocalizedString unlocalizedName;
};
struct MenuItem {
std::vector<std::string> unlocalizedNames;
std::vector<UnlocalizedString> unlocalizedNames;
std::unique_ptr<Shortcut> shortcut;
View *view;
MenuCallback callback;
@@ -641,7 +653,7 @@ namespace hex {
struct TitleBarButton {
std::string icon;
std::string unlocalizedTooltip;
UnlocalizedString unlocalizedTooltip;
ClickCallback callback;
};
@@ -664,7 +676,7 @@ namespace hex {
* @param unlocalizedName The unlocalized name of the entry
* @param priority The priority of the entry. Lower values are displayed first
*/
void registerMainMenuItem(const std::string &unlocalizedName, u32 priority);
void registerMainMenuItem(const UnlocalizedString &unlocalizedName, u32 priority);
/**
* @brief Adds a new main menu entry
@@ -675,7 +687,7 @@ namespace hex {
* @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
*/
void addMenuItem(const std::vector<std::string> &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, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr);
/**
* @brief Adds a new main menu sub-menu entry
@@ -684,14 +696,14 @@ namespace hex {
* @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<std::string> 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 separator
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
* @param priority The priority of the entry. Lower values are displayed first
*/
void addMenuItemSeparator(std::vector<std::string> unlocalizedMainMenuNames, u32 priority);
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority);
/**
@@ -726,7 +738,7 @@ namespace hex {
* @param unlocalizedTooltip The unlocalized tooltip to use for the button
* @param function The function to call when the button is clicked
*/
void addTitleBarButton(const std::string &icon, const std::string &unlocalizedTooltip, const impl::ClickCallback &function);
void addTitleBarButton(const std::string &icon, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function);
}
@@ -735,7 +747,7 @@ namespace hex {
namespace impl {
void addProviderName(const std::string &unlocalizedName);
void addProviderName(const UnlocalizedString &unlocalizedName);
using ProviderCreationFunction = prv::Provider*(*)();
void add(const std::string &typeName, ProviderCreationFunction creationFunction);
@@ -770,7 +782,7 @@ namespace hex {
using Callback = std::function<std::string(prv::Provider *provider, u64 address, size_t size)>;
struct Entry {
std::string unlocalizedName;
UnlocalizedString unlocalizedName;
Callback callback;
};
@@ -784,7 +796,7 @@ namespace hex {
* @param unlocalizedName The unlocalized name of the formatter
* @param callback The function to call to format the data
*/
void add(const std::string &unlocalizedName, const impl::Callback &callback);
void add(const UnlocalizedString &unlocalizedName, const impl::Callback &callback);
}
@@ -817,7 +829,7 @@ namespace hex {
class DataVisualizer {
public:
DataVisualizer(std::string unlocalizedName, u16 bytesPerCell, u16 maxCharsPerCell)
DataVisualizer(UnlocalizedString unlocalizedName, u16 bytesPerCell, u16 maxCharsPerCell)
: m_unlocalizedName(std::move(unlocalizedName)),
m_bytesPerCell(bytesPerCell),
m_maxCharsPerCell(maxCharsPerCell) { }
@@ -827,10 +839,10 @@ namespace hex {
virtual void draw(u64 address, const u8 *data, size_t size, bool upperCase) = 0;
virtual bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) = 0;
[[nodiscard]] u16 getBytesPerCell() const { return this->m_bytesPerCell; }
[[nodiscard]] u16 getMaxCharsPerCell() const { return this->m_maxCharsPerCell; }
[[nodiscard]] u16 getBytesPerCell() const { return m_bytesPerCell; }
[[nodiscard]] u16 getMaxCharsPerCell() const { return m_maxCharsPerCell; }
[[nodiscard]] const std::string& getUnlocalizedName() const { return this->m_unlocalizedName; }
[[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }
protected:
const static int TextInputFlags;
@@ -839,7 +851,7 @@ namespace hex {
bool drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const;
private:
std::string m_unlocalizedName;
UnlocalizedString m_unlocalizedName;
u16 m_bytesPerCell;
u16 m_maxCharsPerCell;
};
@@ -867,7 +879,7 @@ namespace hex {
* @param unlocalizedName Unlocalized name of the data visualizer
* @return The data visualizer, or nullptr if it doesn't exist
*/
std::shared_ptr<DataVisualizer> getVisualizerByName(const std::string &unlocalizedName);
std::shared_ptr<DataVisualizer> getVisualizerByName(const UnlocalizedString &unlocalizedName);
}
@@ -876,7 +888,7 @@ namespace hex {
class Hash {
public:
explicit Hash(std::string unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {}
explicit Hash(UnlocalizedString unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {}
virtual ~Hash() = default;
class Function {
@@ -888,20 +900,20 @@ namespace hex {
}
[[nodiscard]] Hash *getType() { return this->m_type; }
[[nodiscard]] const Hash *getType() const { return this->m_type; }
[[nodiscard]] const std::string &getName() const { return this->m_name; }
[[nodiscard]] Hash *getType() { return m_type; }
[[nodiscard]] const Hash *getType() const { return m_type; }
[[nodiscard]] const std::string &getName() const { return m_name; }
const std::vector<u8>& get(const Region& region, prv::Provider *provider) {
if (this->m_cache.empty()) {
this->m_cache = this->m_callback(region, provider);
if (m_cache.empty()) {
m_cache = m_callback(region, provider);
}
return this->m_cache;
return m_cache;
}
void reset() {
this->m_cache.clear();
m_cache.clear();
}
private:
@@ -918,8 +930,8 @@ namespace hex {
[[nodiscard]] virtual nlohmann::json store() const = 0;
virtual void load(const nlohmann::json &json) = 0;
[[nodiscard]] const std::string &getUnlocalizedName() const {
return this->m_unlocalizedName;
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const {
return m_unlocalizedName;
}
protected:
@@ -928,7 +940,7 @@ namespace hex {
}
private:
std::string m_unlocalizedName;
UnlocalizedString m_unlocalizedName;
};
namespace impl {
@@ -957,16 +969,10 @@ namespace hex {
namespace impl {
using Callback = std::function<void()>;
struct Service {
std::string name;
std::jthread thread;
};
std::vector<Service> &getServices();
void stopServices();
}
void registerService(const std::string &unlocalizedName, const impl::Callback &callback);
void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback);
}
/* Network Communication Interface Registry. Allows adding new communication interface endpoints */
@@ -988,14 +994,14 @@ namespace hex {
namespace impl {
struct Experiment {
std::string unlocalizedName, unlocalizedDescription;
UnlocalizedString unlocalizedName, unlocalizedDescription;
bool enabled;
};
std::map<std::string, Experiment> &getExperiments();
}
void addExperiment(const std::string &experimentName, const std::string &unlocalizedName, const std::string &unlocalizedDescription = "");
void addExperiment(const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = "");
void enableExperiement(const std::string &experimentName, bool enabled);
[[nodiscard]] bool isExperimentEnabled(const std::string &experimentName);

View File

@@ -13,12 +13,18 @@
#include <wolv/types/type_name.hpp>
#define EVENT_DEF_IMPL(event_name, event_name_string, should_log, ...) \
struct event_name final : public hex::impl::Event<__VA_ARGS__> { \
constexpr static auto Id = [] { return hex::impl::EventId(event_name_string); }(); \
constexpr static auto ShouldLog = (should_log); \
explicit event_name(Callback func) noexcept : Event(std::move(func)) { } \
}
#define EVENT_DEF_IMPL(event_name, event_name_string, should_log, ...) \
struct event_name final : public hex::impl::Event<__VA_ARGS__> { \
constexpr static auto Id = [] { return hex::impl::EventId(event_name_string); }(); \
constexpr static auto ShouldLog = (should_log); \
explicit event_name(Callback func) noexcept : Event(std::move(func)) { } \
\
static EventManager::EventList::iterator subscribe(Event::Callback function) { return EventManager::subscribe<event_name>(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(void *token) noexcept { EventManager::unsubscribe<event_name>(token); } \
static void post(auto &&...args) noexcept { 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_NO_LOG(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, false, __VA_ARGS__)
@@ -26,7 +32,10 @@
/* Forward declarations */
struct GLFWwindow;
namespace hex { class Achievement; }
namespace hex {
class Achievement;
class View;
}
namespace hex {
@@ -36,15 +45,15 @@ namespace hex {
class EventId {
public:
explicit constexpr EventId(const char *eventName) {
this->m_hash = 0x811C'9DC5;
m_hash = 0x811C'9DC5;
for (auto c : std::string_view(eventName)) {
this->m_hash = (this->m_hash >> 5) | (this->m_hash << 27);
this->m_hash ^= c;
m_hash = (m_hash >> 5) | (m_hash << 27);
m_hash ^= c;
}
}
constexpr bool operator==(const EventId &other) const {
return this->m_hash == other.m_hash;
return m_hash == other.m_hash;
}
private:
@@ -62,7 +71,7 @@ namespace hex {
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
void operator()(Params... params) const noexcept {
this->m_func(params...);
m_func(params...);
}
private:
@@ -241,7 +250,8 @@ namespace hex {
EVENT_DEF(EventStoreContentRemoved, const std::fs::path&);
EVENT_DEF(EventImHexClosing);
EVENT_DEF(EventAchievementUnlocked, const Achievement&);
EVENT_DEF(EventSearchBoxClicked);
EVENT_DEF(EventSearchBoxClicked, u32);
EVENT_DEF(EventViewOpened, View*);
EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*);
EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64);
@@ -263,6 +273,7 @@ namespace hex {
EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t, u64*);
EVENT_DEF(RequestRemoveBookmark, u64);
EVENT_DEF(RequestSetPatternLanguageCode, std::string);
EVENT_DEF(RequestRunPatternCode);
EVENT_DEF(RequestLoadPatternLanguageFile, std::fs::path);
EVENT_DEF(RequestSavePatternLanguageFile, std::fs::path);
EVENT_DEF(RequestUpdateWindowTitle);
@@ -278,11 +289,6 @@ namespace hex {
EVENT_DEF(RequestCreateProvider, std::string, bool, bool, hex::prv::Provider **);
EVENT_DEF(RequestInitThemeHandlers);
EVENT_DEF(RequestOpenInfoPopup, const std::string);
EVENT_DEF(RequestOpenErrorPopup, const std::string);
EVENT_DEF(RequestOpenFatalPopup, const std::string);
/**
* @brief Send an event to the main Imhex instance
*/

View File

@@ -1,6 +1,7 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <optional>
#include <span>
@@ -33,8 +34,8 @@ namespace hex {
Highlighting() = default;
Highlighting(Region region, color_t color);
[[nodiscard]] const Region &getRegion() const { return this->m_region; }
[[nodiscard]] const color_t &getColor() const { return this->m_color; }
[[nodiscard]] const Region &getRegion() const { return m_region; }
[[nodiscard]] const color_t &getColor() const { return m_color; }
private:
Region m_region = {};
@@ -46,9 +47,9 @@ namespace hex {
Tooltip() = default;
Tooltip(Region region, std::string value, color_t color);
[[nodiscard]] const Region &getRegion() const { return this->m_region; }
[[nodiscard]] const color_t &getColor() const { return this->m_color; }
[[nodiscard]] const std::string &getValue() const { return this->m_value; }
[[nodiscard]] const Region &getRegion() const { return m_region; }
[[nodiscard]] const color_t &getColor() const { return m_color; }
[[nodiscard]] const std::string &getValue() const { return m_value; }
private:
Region m_region = {};
@@ -338,7 +339,7 @@ namespace hex {
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
* @param select Whether to select the provider after adding it
*/
prv::Provider* createProvider(const std::string &unlocalizedName, bool skipLoadInterface = false, bool select = true);
prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface = false, bool select = true);
}
@@ -365,6 +366,7 @@ namespace hex {
void addInitArgument(const std::string &key, const std::string &value = { });
void setLastFrameTime(double time);
}
struct ProgramArguments {
@@ -556,6 +558,8 @@ namespace hex {
bool updateImHex(UpdateType updateType);
void addStartupTask(const std::string &name, bool async, const std::function<bool()> &function);
double getLastFrameTime();
}
/**

View File

@@ -4,8 +4,6 @@
#include <string>
#include <nlohmann/json_fwd.hpp>
namespace hex {
class LayoutManager {
@@ -27,11 +25,17 @@ namespace hex {
*/
static void load(const std::fs::path &path);
/**
* @brief Saves the current layout to a string
* @return String containing the layout
*/
static std::string saveToString();
/**
* @brief Load a layout from a string
* @param content Layout string
*/
static void loadString(const std::string &content);
static void loadFromString(const std::string &content);
/**
* @brief Get a list of all layouts
@@ -55,6 +59,23 @@ namespace hex {
*/
static void reset();
/**
* @brief Checks is the current layout is locked
*/
static bool isLayoutLocked();
/**
* @brief Locks or unlocks the current layout
* @note If the layout is locked, it cannot be modified by the user anymore
* @param locked True to lock the layout, false to unlock it
*/
static void lockLayout(bool locked);
/**
* @brief Closes all views
*/
static void closeAllViews();
private:
LayoutManager() = default;
};

View File

@@ -32,10 +32,13 @@ namespace hex {
[[nodiscard]] const std::string &getSelectedLanguage();
}
struct UnlocalizedString;
class Lang {
public:
explicit Lang(const char *unlocalizedString);
explicit Lang(std::string unlocalizedString);
explicit Lang(const std::string &unlocalizedString);
explicit Lang(const UnlocalizedString &unlocalizedString);
explicit Lang(std::string_view unlocalizedString);
[[nodiscard]] operator std::string() const;
@@ -60,6 +63,43 @@ namespace hex {
return Lang(string);
}
struct UnlocalizedString {
public:
UnlocalizedString() = default;
UnlocalizedString(auto && arg) : m_unlocalizedString(std::forward<decltype(arg)>(arg)) {
static_assert(!std::same_as<std::remove_cvref_t<decltype(arg)>, Lang>, "Expected a unlocalized name, got a localized one!");
}
[[nodiscard]] operator std::string() const {
return m_unlocalizedString;
}
[[nodiscard]] operator std::string_view() const {
return m_unlocalizedString;
}
[[nodiscard]] operator const char *() const {
return m_unlocalizedString.c_str();
}
[[nodiscard]] const std::string &get() const {
return m_unlocalizedString;
}
[[nodiscard]] bool empty() const {
return m_unlocalizedString.empty();
}
auto operator<=>(const UnlocalizedString &) const = default;
auto operator<=>(const std::string &other) const {
return m_unlocalizedString <=> other;
}
private:
std::string m_unlocalizedString;
};
}
template<>

View File

@@ -5,6 +5,7 @@
#include <string>
#include <wolv/io/fs.hpp>
#include <hex/helpers/logger.hpp>
struct ImGuiContext;
@@ -16,8 +17,14 @@ namespace hex {
std::function<void(const std::vector<std::string>&)> callback;
};
struct Feature {
std::string name;
bool enabled;
};
struct PluginFunctions {
using InitializePluginFunc = void (*)();
using InitializeLibraryFunc = void (*)();
using GetPluginNameFunc = const char *(*)();
using GetPluginAuthorFunc = const char *(*)();
using GetPluginDescriptionFunc = const char *(*)();
@@ -25,8 +32,10 @@ namespace hex {
using SetImGuiContextFunc = void (*)(ImGuiContext *);
using IsBuiltinPluginFunc = bool (*)();
using GetSubCommandsFunc = void* (*)();
using GetFeaturesFunc = void* (*)();
InitializePluginFunc initializePluginFunction = nullptr;
InitializeLibraryFunc initializeLibraryFunction = nullptr;
GetPluginNameFunc getPluginNameFunction = nullptr;
GetPluginAuthorFunc getPluginAuthorFunction = nullptr;
GetPluginDescriptionFunc getPluginDescriptionFunction = nullptr;
@@ -34,17 +43,21 @@ namespace hex {
SetImGuiContextFunc setImGuiContextFunction = nullptr;
IsBuiltinPluginFunc isBuiltinPluginFunction = nullptr;
GetSubCommandsFunc getSubCommandsFunction = nullptr;
GetFeaturesFunc getFeaturesFunction = nullptr;
};
class Plugin {
public:
explicit Plugin(const std::fs::path &path);
explicit Plugin(PluginFunctions functions);
explicit Plugin(const PluginFunctions &functions);
Plugin(const Plugin &) = delete;
Plugin(Plugin &&other) noexcept;
~Plugin();
Plugin& operator=(const Plugin &) = delete;
Plugin& operator=(Plugin &&other) noexcept;
[[nodiscard]] bool initializePlugin() const;
[[nodiscard]] std::string getPluginName() const;
[[nodiscard]] std::string getPluginAuthor() const;
@@ -55,9 +68,13 @@ namespace hex {
[[nodiscard]] const std::fs::path &getPath() const;
[[nodiscard]] bool isValid() const;
[[nodiscard]] bool isLoaded() const;
[[nodiscard]] std::span<SubCommand> getSubCommands() const;
[[nodiscard]] std::span<Feature> getFeatures() const;
[[nodiscard]] bool isLibraryPlugin() const;
private:
uintptr_t m_handle = 0;

View File

@@ -1,7 +1,7 @@
#pragma once
#include <hex.hpp>
#include <GLFW/glfw3.h>
#include <hex/api/localization_manager.hpp>
#include <functional>
#include <memory>
@@ -9,6 +9,8 @@
#include <set>
#include <string>
#include <GLFW/glfw3.h>
struct ImGuiWindow;
namespace hex {
@@ -138,7 +140,7 @@ namespace hex {
auto operator<=>(const Key &) const = default;
[[nodiscard]] constexpr u32 getKeyCode() const { return this->m_key; }
[[nodiscard]] constexpr u32 getKeyCode() const { return m_key; }
private:
u32 m_key = 0;
};
@@ -179,17 +181,17 @@ namespace hex {
}
Shortcut &operator+=(const Key &other) {
this->m_keys.insert(other);
m_keys.insert(other);
return *this;
}
bool operator<(const Shortcut &other) const {
return this->m_keys < other.m_keys;
return m_keys < other.m_keys;
}
bool operator==(const Shortcut &other) const {
auto thisKeys = this->m_keys;
auto thisKeys = m_keys;
auto otherKeys = other.m_keys;
thisKeys.erase(CurrentView);
@@ -201,7 +203,7 @@ namespace hex {
}
bool isLocal() const {
return this->m_keys.contains(CurrentView);
return m_keys.contains(CurrentView);
}
std::string toString() const {
@@ -221,7 +223,7 @@ namespace hex {
constexpr static auto Concatination = " + ";
auto keys = this->m_keys;
auto keys = m_keys;
if (keys.erase(CTRL) > 0) {
result += CTRL_NAME;
result += Concatination;
@@ -367,7 +369,7 @@ namespace hex {
return result;
}
const std::set<Key>& getKeys() const { return this->m_keys; }
const std::set<Key>& getKeys() const { return m_keys; }
private:
friend Shortcut operator+(const Key &lhs, const Key &rhs);
@@ -391,7 +393,7 @@ namespace hex {
using Callback = std::function<void()>;
struct ShortcutEntry {
Shortcut shortcut;
std::string unlocalizedName;
UnlocalizedString unlocalizedName;
Callback callback;
};
@@ -401,7 +403,7 @@ namespace hex {
* @param unlocalizedName The unlocalized name of the shortcut
* @param callback The callback to call when the shortcut is triggered.
*/
static void addGlobalShortcut(const Shortcut &shortcut, const std::string &unlocalizedName, const Callback &callback);
static void addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback);
/**
* @brief Add a view-specific shortcut. View-specific shortcuts can only be triggered when the specified view is focused.
@@ -410,7 +412,7 @@ namespace hex {
* @param unlocalizedName The unlocalized name of the shortcut
* @param callback The callback to call when the shortcut is triggered.
*/
static void addShortcut(View *view, const Shortcut &shortcut, const std::string &unlocalizedName, const Callback &callback);
static void addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback);
/**
@@ -446,7 +448,7 @@ namespace hex {
[[nodiscard]] static std::optional<Shortcut> getPreviousShortcut();
[[nodiscard]] static std::vector<ShortcutEntry> getGlobalShortcuts();
[[nodiscard]] static std::vector<ShortcutEntry> getViewShortcuts(View *view);
[[nodiscard]] static std::vector<ShortcutEntry> getViewShortcuts(const View *view);
[[nodiscard]] static bool updateShortcut(const Shortcut &oldShortcut, const Shortcut &newShortcut, View *view = nullptr);
};

View File

@@ -1,17 +1,15 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <cstdio>
#include <thread>
#include <functional>
#include <mutex>
#include <memory>
#include <list>
#include <condition_variable>
#include <jthread.hpp>
namespace hex {
class TaskHolder;
@@ -23,7 +21,7 @@ namespace hex {
class Task {
public:
Task() = default;
Task(std::string unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
Task(const Task&) = delete;
Task(Task &&other) noexcept;
@@ -64,7 +62,7 @@ namespace hex {
void clearException();
[[nodiscard]] std::string getExceptionMessage() const;
[[nodiscard]] const std::string &getUnlocalizedName();
[[nodiscard]] const UnlocalizedString &getUnlocalizedName();
[[nodiscard]] u64 getValue() const;
[[nodiscard]] u64 getMaxValue() const;
@@ -76,7 +74,7 @@ namespace hex {
private:
mutable std::mutex m_mutex;
std::string m_unlocalizedName;
UnlocalizedString m_unlocalizedName;
std::atomic<u64> m_currValue = 0, m_maxValue = 0;
std::function<void()> m_interruptCallback;
std::function<void(Task &)> m_function;
@@ -165,7 +163,7 @@ namespace hex {
static void runDeferredCalls();
private:
static void runner(const std::stop_token &stopToken);
static TaskHolder createTask(std::string name, u64 maxValue, bool background, std::function<void(Task &)> function);
};
}

View File

@@ -0,0 +1,175 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <string>
#include <list>
#include <variant>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex {
class TutorialManager {
public:
enum class Position : u8 {
None = 0,
Top = 1,
Bottom = 2,
Left = 4,
Right = 8
};
struct Tutorial {
Tutorial() = delete;
Tutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) :
m_unlocalizedName(unlocalizedName),
m_unlocalizedDescription(unlocalizedDescription) { }
struct Step {
explicit Step(Tutorial *parent) : m_parent(parent) { }
/**
* @brief Adds a highlighting with text to a specific element
* @param unlocalizedText Unlocalized text to display next to the highlighting
* @param ids ID of the element to highlight
* @return Current step
*/
Step& addHighlight(const UnlocalizedString &unlocalizedText, std::initializer_list<std::variant<Lang, std::string, int>> &&ids);
/**
* @brief Adds a highlighting to a specific element
* @param ids ID of the element to highlight
* @return Current step
*/
Step& addHighlight(std::initializer_list<std::variant<Lang, std::string, int>> &&ids);
/**
* @brief Sets the text that will be displayed in the tutorial message box
* @param unlocalizedTitle Title of the message box
* @param unlocalizedMessage Main message of the message box
* @param position Position of the message box
* @return Current step
*/
Step& setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position = Position::None);
/**
* @brief Allows this step to be skipped by clicking on the advance button
* @return Current step
*/
Step& allowSkip();
Step& onAppear(std::function<void()> callback);
Step& onComplete(std::function<void()> callback);
/**
* @brief Checks if this step is the current step
* @return True if this step is the current step
*/
bool isCurrent() const;
/**
* @brief Completes this step if it is the current step
*/
void complete() const;
private:
struct Highlight {
UnlocalizedString unlocalizedText;
std::vector<std::variant<Lang, std::string, int>> highlightIds;
};
struct Message {
Position position;
UnlocalizedString unlocalizedTitle;
UnlocalizedString unlocalizedMessage;
bool allowSkip;
};
private:
void addHighlights() const;
void removeHighlights() const;
void advance(i32 steps = 1) const;
friend class TutorialManager;
Tutorial *m_parent;
std::vector<Highlight> m_highlights;
std::optional<Message> m_message;
std::function<void()> m_onAppear, m_onComplete;
};
Step& addStep();
const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }
const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }
private:
friend class TutorialManager;
void start();
UnlocalizedString m_unlocalizedName;
UnlocalizedString m_unlocalizedDescription;
std::list<Step> m_steps;
decltype(m_steps)::iterator m_currentStep, m_latestStep;
};
/**
* @brief Gets a list of all tutorials
* @return List of all tutorials
*/
static const std::map<std::string, Tutorial>& getTutorials();
/**
* @brief Gets the currently running tutorial
* @return Iterator pointing to the current tutorial
*/
static std::map<std::string, Tutorial>::iterator getCurrentTutorial();
/**
* @brief Creates a new tutorial that can be started later
* @param unlocalizedName Name of the tutorial
* @param unlocalizedDescription
* @return Reference to created tutorial
*/
static Tutorial& createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription);
/**
* @brief Starts the tutorial with the given name
* @param unlocalizedName Name of tutorial to start
*/
static void startTutorial(const UnlocalizedString &unlocalizedName);
/**
* @brief Draws the tutorial
* @note This function should only be called by the main GUI
*/
static void drawTutorial();
/**
* @brief Resets the tutorial manager
*/
static void reset();
private:
TutorialManager() = delete;
static void drawHighlights();
static void drawMessageBox(std::optional<Tutorial::Step::Message> message);
};
inline TutorialManager::Position operator|(TutorialManager::Position a, TutorialManager::Position b) {
return static_cast<TutorialManager::Position>(static_cast<u8>(a) | static_cast<u8>(b));
}
inline TutorialManager::Position operator&(TutorialManager::Position a, TutorialManager::Position b) {
return static_cast<TutorialManager::Position>(static_cast<u8>(a) & static_cast<u8>(b));
}
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <wolv/io/fs.hpp>
#include <map>
#include <string>
namespace hex {
class WorkspaceManager {
public:
struct Workspace {
std::string layout;
std::fs::path path;
};
static void createWorkspace(const std::string &name, const std::string &layout = "");
static void switchWorkspace(const std::string &name);
static void importFromFile(const std::fs::path &path);
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {});
static const auto& getWorkspaces() { return s_workspaces; }
static const auto& getCurrentWorkspace() { return s_currentWorkspace; }
static void reset();
static void process();
private:
WorkspaceManager() = default;
static std::map<std::string, Workspace> s_workspaces;
static decltype(s_workspaces)::iterator s_currentWorkspace, s_previousWorkspace;
};
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <string>
#include <string_view>
@@ -23,32 +25,32 @@ namespace hex::dp {
Out
};
Attribute(IOType ioType, Type type, std::string unlocalizedName);
Attribute(IOType ioType, Type type, UnlocalizedString unlocalizedName);
~Attribute();
[[nodiscard]] int getId() const { return this->m_id; }
void setId(int id) { this->m_id = id; }
[[nodiscard]] int getId() const { return m_id; }
void setId(int id) { m_id = id; }
[[nodiscard]] IOType getIOType() const { return this->m_ioType; }
[[nodiscard]] Type getType() const { return this->m_type; }
[[nodiscard]] const std::string &getUnlocalizedName() const { return this->m_unlocalizedName; }
[[nodiscard]] IOType getIOType() const { return m_ioType; }
[[nodiscard]] Type getType() const { return m_type; }
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; }
void addConnectedAttribute(int linkId, Attribute *to) { this->m_connectedAttributes.insert({ linkId, to }); }
void removeConnectedAttribute(int linkId) { this->m_connectedAttributes.erase(linkId); }
[[nodiscard]] std::map<int, Attribute *> &getConnectedAttributes() { return this->m_connectedAttributes; }
void addConnectedAttribute(int linkId, Attribute *to) { m_connectedAttributes.insert({ linkId, to }); }
void removeConnectedAttribute(int linkId) { m_connectedAttributes.erase(linkId); }
[[nodiscard]] std::map<int, Attribute *> &getConnectedAttributes() { return m_connectedAttributes; }
[[nodiscard]] Node *getParentNode() const { return this->m_parentNode; }
[[nodiscard]] Node *getParentNode() const { return m_parentNode; }
[[nodiscard]] std::vector<u8>& getOutputData() {
if (!this->m_outputData.empty())
return this->m_outputData;
if (!m_outputData.empty())
return m_outputData;
else
return this->m_defaultData;
return m_defaultData;
}
void clearOutputData() { this->m_outputData.clear(); }
void clearOutputData() { m_outputData.clear(); }
[[nodiscard]] std::vector<u8>& getDefaultData() { return this->m_defaultData; }
[[nodiscard]] std::vector<u8>& getDefaultData() { return m_defaultData; }
static void setIdCounter(int id);
@@ -56,7 +58,7 @@ namespace hex::dp {
int m_id;
IOType m_ioType;
Type m_type;
std::string m_unlocalizedName;
UnlocalizedString m_unlocalizedName;
std::map<int, Attribute *> m_connectedAttributes;
Node *m_parentNode = nullptr;
@@ -64,7 +66,7 @@ namespace hex::dp {
std::vector<u8> m_defaultData;
friend class Node;
void setParentNode(Node *node) { this->m_parentNode = node; }
void setParentNode(Node *node) { m_parentNode = node; }
static int s_idCounter;
};

View File

@@ -6,11 +6,11 @@ namespace hex::dp {
public:
Link(int from, int to);
[[nodiscard]] int getId() const { return this->m_id; }
void setId(int id) { this->m_id = id; }
[[nodiscard]] int getId() const { return m_id; }
void setId(int id) { m_id = id; }
[[nodiscard]] int getFromId() const { return this->m_from; }
[[nodiscard]] int getToId() const { return this->m_to; }
[[nodiscard]] int getFromId() const { return m_from; }
[[nodiscard]] int getToId() const { return m_to; }
static void setIdCounter(int id);

View File

@@ -1,6 +1,7 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/helpers/intrinsics.hpp>
#include <hex/data_processor/attribute.hpp>
@@ -21,24 +22,24 @@ namespace hex::dp {
class Node {
public:
Node(std::string unlocalizedTitle, std::vector<Attribute> attributes);
Node(UnlocalizedString unlocalizedTitle, std::vector<Attribute> attributes);
virtual ~Node() = default;
[[nodiscard]] int getId() const { return this->m_id; }
void setId(int id) { this->m_id = id; }
[[nodiscard]] int getId() const { return m_id; }
void setId(int id) { m_id = id; }
[[nodiscard]] const std::string &getUnlocalizedName() const { return this->m_unlocalizedName; }
void setUnlocalizedName(const std::string &unlocalizedName) { this->m_unlocalizedName = unlocalizedName; }
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; }
void setUnlocalizedName(const UnlocalizedString &unlocalizedName) { m_unlocalizedName = unlocalizedName; }
[[nodiscard]] const std::string &getUnlocalizedTitle() const { return this->m_unlocalizedTitle; }
void setUnlocalizedTitle(std::string title) { this->m_unlocalizedTitle = std::move(title); }
[[nodiscard]] const UnlocalizedString &getUnlocalizedTitle() const { return m_unlocalizedTitle; }
void setUnlocalizedTitle(std::string title) { m_unlocalizedTitle = std::move(title); }
[[nodiscard]] std::vector<Attribute> &getAttributes() { return this->m_attributes; }
[[nodiscard]] const std::vector<Attribute> &getAttributes() const { return this->m_attributes; }
[[nodiscard]] std::vector<Attribute> &getAttributes() { return m_attributes; }
[[nodiscard]] const std::vector<Attribute> &getAttributes() const { return m_attributes; }
void setCurrentOverlay(prv::Overlay *overlay) {
this->m_overlay = overlay;
m_overlay = overlay;
}
virtual void drawNode() { }
@@ -53,20 +54,20 @@ namespace hex::dp {
};
void resetOutputData() {
for (auto &attribute : this->m_attributes)
for (auto &attribute : m_attributes)
attribute.clearOutputData();
}
void resetProcessedInputs() {
this->m_processedInputs.clear();
m_processedInputs.clear();
}
void setPosition(ImVec2 pos) {
this->m_position = pos;
m_position = pos;
}
[[nodiscard]] ImVec2 getPosition() const {
return this->m_position;
return m_position;
}
static void setIdCounter(int id);
@@ -81,7 +82,7 @@ namespace hex::dp {
private:
int m_id;
std::string m_unlocalizedTitle, m_unlocalizedName;
UnlocalizedString m_unlocalizedTitle, m_unlocalizedName;
std::vector<Attribute> m_attributes;
std::set<u32> m_processedInputs;
prv::Overlay *m_overlay = nullptr;
@@ -106,13 +107,13 @@ namespace hex::dp {
}
void markInputProcessed(u32 index) {
const auto &[iter, inserted] = this->m_processedInputs.insert(index);
const auto &[iter, inserted] = m_processedInputs.insert(index);
if (!inserted)
throwNodeError("Recursion detected!");
}
void unmarkInputProcessed(u32 index) {
this->m_processedInputs.erase(index);
m_processedInputs.erase(index);
}
protected:
@@ -123,9 +124,9 @@ namespace hex::dp {
void setOverlayData(u64 address, const std::vector<u8> &data);
void setAttributes(std::vector<Attribute> attributes) {
this->m_attributes = std::move(attributes);
m_attributes = std::move(attributes);
for (auto &attr : this->m_attributes)
for (auto &attr : m_attributes)
attr.setParentNode(this);
}
};

View File

@@ -17,13 +17,13 @@ namespace hex {
BinaryPattern() = default;
explicit BinaryPattern(const std::string &pattern) : m_patterns(parseBinaryPatternString(pattern)) { }
[[nodiscard]] bool isValid() const { return !this->m_patterns.empty(); }
[[nodiscard]] bool isValid() const { return !m_patterns.empty(); }
[[nodiscard]] bool matches(const std::vector<u8> &bytes) const {
if (bytes.size() < this->m_patterns.size())
if (bytes.size() < m_patterns.size())
return false;
for (u32 i = 0; i < this->m_patterns.size(); i++) {
for (u32 i = 0; i < m_patterns.size(); i++) {
if (!this->matchesByte(bytes[i], i))
return false;
}
@@ -32,13 +32,13 @@ namespace hex {
}
[[nodiscard]] bool matchesByte(u8 byte, u32 offset) const {
const auto &pattern = this->m_patterns[offset];
const auto &pattern = m_patterns[offset];
return (byte & pattern.mask) == pattern.value;
}
[[nodiscard]] size_t getSize() const {
return this->m_patterns.size();
[[nodiscard]] u64 getSize() const {
return m_patterns.size();
}
private:

View File

@@ -29,9 +29,9 @@ namespace hex::dbg {
if constexpr (std::same_as<Type, bool>) {
ImGui::Checkbox(name.data(), &variable);
} else if constexpr (std::integral<Type> || std::floating_point<Type>) {
ImGui::InputScalar(name.data(), ImGuiExt::getImGuiDataType<Type>(), &variable);
ImGui::DragScalar(name.data(), ImGuiExt::getImGuiDataType<Type>(), &variable);
} else if constexpr (std::same_as<Type, ImVec2>) {
ImGui::InputFloat2(name.data(), &variable.x);
ImGui::DragFloat2(name.data(), &variable.x);
} else if constexpr (std::same_as<Type, std::string>) {
ImGui::InputText(name.data(), variable);
} else if constexpr (std::same_as<Type, ImColor>) {

View File

@@ -29,13 +29,13 @@ namespace hex {
[[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(std::span<u8> buffer) const;
[[nodiscard]] size_t getEncodingLengthFor(std::span<u8> buffer) const;
[[nodiscard]] size_t getLongestSequence() const { return this->m_longestSequence; }
[[nodiscard]] size_t getLongestSequence() const { return m_longestSequence; }
[[nodiscard]] bool valid() const { return this->m_valid; }
[[nodiscard]] bool valid() const { return m_valid; }
[[nodiscard]] const std::string& getTableContent() const { return this->m_tableContent; }
[[nodiscard]] const std::string& getTableContent() const { return m_tableContent; }
[[nodiscard]] const std::string& getName() const { return this->m_name; }
[[nodiscard]] const std::string& getName() const { return m_name; }
private:
void parse(const std::string &content);

View File

@@ -38,6 +38,7 @@ namespace hex::fs {
Plugins,
Yara,
Config,
Backups,
Resources,
Constants,
Encodings,
@@ -49,6 +50,7 @@ namespace hex::fs {
Libraries,
Nodes,
Layouts,
Workspaces,
END
};

View File

@@ -31,7 +31,7 @@ namespace hex {
explicit ResultBase(u32 statusCode) : m_statusCode(statusCode), m_valid(true) { }
[[nodiscard]] u32 getStatusCode() const {
return this->m_statusCode;
return m_statusCode;
}
[[nodiscard]] bool isSuccess() const {
@@ -39,7 +39,7 @@ namespace hex {
}
[[nodiscard]] bool isValid() const {
return this->m_valid;
return m_valid;
}
private:
@@ -55,7 +55,7 @@ namespace hex {
[[nodiscard]]
const T& getData() const {
return this->m_data;
return m_data;
}
private:
@@ -76,31 +76,31 @@ namespace hex {
static void setProxyUrl(std::string proxy);
void setMethod(std::string method) {
this->m_method = std::move(method);
m_method = std::move(method);
}
void setUrl(std::string url) {
this->m_url = std::move(url);
m_url = std::move(url);
}
void addHeader(std::string key, std::string value) {
this->m_headers[std::move(key)] = std::move(value);
m_headers[std::move(key)] = std::move(value);
}
void setBody(std::string body) {
this->m_body = std::move(body);
m_body = std::move(body);
}
void setTimeout(u32 timeout) {
this->m_timeout = timeout;
m_timeout = timeout;
}
float getProgress() const {
return this->m_progress;
return m_progress;
}
void cancel() {
this->m_canceled = true;
m_canceled = true;
}
template<typename T = std::string>

View File

@@ -44,24 +44,24 @@ namespace hex {
template<typename T>
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
strcpy(this->m_attr.requestMethod, this->m_method.c_str());
this->m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
strcpy(m_attr.requestMethod, m_method.c_str());
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
if (!this->m_body.empty()) {
this->m_attr.requestData = this->m_body.c_str();
this->m_attr.requestDataSize = this->m_body.size();
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 = this->m_headers.begin(); it != this->m_headers.end(); it++) {
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);
this->m_attr.requestHeaders = headers.data();
m_attr.requestHeaders = headers.data();
// Send request
emscripten_fetch_t* fetch = emscripten_fetch(&this->m_attr, this->m_url.c_str());
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());

View File

@@ -18,8 +18,8 @@ namespace hex {
std::vector<u8> response;
wolv::io::File file(path, wolv::io::File::Mode::Create);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &file);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
return this->executeImpl<T>(response);
});
@@ -30,7 +30,7 @@ namespace hex {
return std::async(std::launch::async, [this, path, mimeName]{
auto fileName = wolv::util::toUTF8String(path.filename());
curl_mime *mime = curl_mime_init(this->m_curl);
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);
@@ -58,11 +58,11 @@ namespace hex {
curl_mime_filename(part, fileName.c_str());
curl_mime_name(part, mimeName.c_str());
curl_easy_setopt(this->m_curl, CURLOPT_MIMEPOST, mime);
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
std::vector<u8> responseData;
curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
@@ -71,7 +71,7 @@ namespace hex {
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(this->m_curl);
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());
@@ -79,11 +79,11 @@ namespace hex {
curl_mime_filename(part, fileNameStr.c_str());
curl_mime_name(part, mimeName.c_str());
curl_easy_setopt(this->m_curl, CURLOPT_MIMEPOST, mime);
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
std::vector<u8> responseData;
curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
@@ -94,8 +94,8 @@ namespace hex {
return std::async(std::launch::async, [this] {
std::vector<u8> responseData;
curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
@@ -103,33 +103,33 @@ namespace hex {
template<typename T>
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
curl_easy_setopt(this->m_curl, CURLOPT_URL, this->m_url.c_str());
curl_easy_setopt(this->m_curl, CURLOPT_CUSTOMREQUEST, this->m_method.c_str());
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
setDefaultConfig();
if (!this->m_body.empty()) {
curl_easy_setopt(this->m_curl, CURLOPT_POSTFIELDS, this->m_body.c_str());
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] : this->m_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(this->m_curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
{
std::scoped_lock lock(this->m_transmissionMutex);
std::scoped_lock lock(m_transmissionMutex);
auto result = curl_easy_perform(this->m_curl);
auto result = curl_easy_perform(m_curl);
if (result != CURLE_OK){
char *url = nullptr;
curl_easy_getinfo(this->m_curl, CURLINFO_EFFECTIVE_URL, &url);
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", this->m_method, url, u32(result), curl_easy_strerror(result));
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 { };
@@ -137,7 +137,7 @@ namespace hex {
}
long statusCode = 0;
curl_easy_getinfo(this->m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
return Result<T>(statusCode, { data.begin(), data.end() });
}

View File

@@ -29,19 +29,19 @@ namespace hex::log {
std::vector<LogEntry>& getLogEntries();
[[maybe_unused]] void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level);
[[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) {
std::scoped_lock lock(impl::g_loggerMutex);
std::scoped_lock lock(g_loggerMutex);
auto dest = impl::getDestination();
printPrefix(dest, ts, level);
auto dest = getDestination();
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}\n", message);
fflush(dest);
impl::getLogEntries().push_back({ IMHEX_PROJECT_NAME, level, std::move(message) });
getLogEntries().push_back({ IMHEX_PROJECT_NAME, level, std::move(message) });
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -33,8 +33,8 @@ namespace hex {
wolv::util::Expected<std::vector<u8>, IPSError> toIPSPatch() const;
wolv::util::Expected<std::vector<u8>, IPSError> toIPS32Patch() const;
const auto& get() const { return this->m_patches; }
auto& get() { return this->m_patches; }
const auto& get() const { return m_patches; }
auto& get() { return m_patches; }
private:
std::map<u64, u8> m_patches;

View File

@@ -4,7 +4,9 @@
#include <hex/helpers/fs.hpp>
#include <microtar.h>
#include <memory>
struct mtar_t;
namespace hex {
@@ -32,28 +34,28 @@ namespace hex {
*/
std::string getOpenErrorString() const;
[[nodiscard]] std::vector<u8> readVector(const std::fs::path &path);
[[nodiscard]] std::string readString(const std::fs::path &path);
[[nodiscard]] std::vector<u8> readVector(const std::fs::path &path) const;
[[nodiscard]] std::string readString(const std::fs::path &path) const;
void writeVector(const std::fs::path &path, const std::vector<u8> &data);
void writeString(const std::fs::path &path, const std::string &data);
void writeVector(const std::fs::path &path, const std::vector<u8> &data) const;
void writeString(const std::fs::path &path, const std::string &data) const;
[[nodiscard]] std::vector<std::fs::path> listEntries(const std::fs::path &basePath = "/");
[[nodiscard]] bool contains(const std::fs::path &path);
[[nodiscard]] std::vector<std::fs::path> listEntries(const std::fs::path &basePath = "/") const;
[[nodiscard]] bool contains(const std::fs::path &path) const;
void extract(const std::fs::path &path, const std::fs::path &outputPath);
void extractAll(const std::fs::path &outputPath);
void extract(const std::fs::path &path, const std::fs::path &outputPath) const;
void extractAll(const std::fs::path &outputPath) const;
[[nodiscard]] bool isValid() const { return this->m_valid; }
[[nodiscard]] bool isValid() const { return m_valid; }
private:
mtar_t m_ctx = { };
std::unique_ptr<mtar_t> m_ctx;
std::fs::path m_path;
bool m_valid = false;
// These will be updated when the constructor is called
int m_tarOpenErrno = MTAR_ESUCCESS;
int m_tarOpenErrno = 0;
int m_fileOpenErrno = 0;
};

View File

@@ -63,6 +63,9 @@ namespace hex {
[[nodiscard]] std::string to_string(u128 value);
[[nodiscard]] std::string to_string(i128 value);
[[nodiscard]] std::string toLower(std::string string);
[[nodiscard]] std::string toUpper(std::string string);
[[nodiscard]] std::vector<u8> parseHexString(std::string string);
[[nodiscard]] std::optional<u8> parseBinaryString(const std::string &string);
[[nodiscard]] std::string toByteString(u64 bytes);
@@ -75,6 +78,8 @@ namespace hex {
[[nodiscard]] std::string encodeByteString(const std::vector<u8> &bytes);
[[nodiscard]] std::vector<u8> decodeByteString(const std::string &string);
std::wstring utf8ToUtf16(const std::string& utf8);
[[nodiscard]] constexpr u64 extract(u8 from, u8 to, const std::unsigned_integral auto &value) {
if (from < to) std::swap(from, to);

View File

@@ -2,6 +2,8 @@
#if defined(OS_MACOS)
struct GLFWwindow;
extern "C" {
void errorMessageMacos(const char *message);
@@ -9,6 +11,8 @@
bool isMacosSystemDarkModeEnabled();
float getBackingScaleFactor();
void setupMacosWindowStyle(GLFWwindow *window);
}
#endif

View File

@@ -2,6 +2,7 @@
#include <hex.hpp>
#include <hex/api/plugin_manager.hpp>
#include <hex/helpers/logger.hpp>
#include <string>
@@ -22,29 +23,57 @@
* 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_LIBRARY_SETUP() IMHEX_LIBRARY_SETUP_IMPL()
#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *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)() { \
hex::PluginManager::addPlugin(hex::PluginFunctions { \
initializePlugin, \
getPluginName, \
getPluginAuthor, \
getPluginDescription, \
getCompatibleVersion, \
setImGuiContext, \
nullptr, \
nullptr \
}); \
} \
#define IMHEX_LIBRARY_SETUP_IMPL() \
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializeLibrary(); \
static auto WOLV_TOKEN_CONCAT(libraryInitializer_, IMHEX_PLUGIN_NAME) = [] { \
initializeLibrary(); \
hex::log::info("Library plugin '{}' initialized successfully", WOLV_STRINGIFY(IMHEX_PLUGIN_NAME)); \
return 0; \
}(); \
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \
ImGui::SetCurrentContext(ctx); \
GImGui = ctx; \
} \
extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
hex::PluginManager::addPlugin(hex::PluginFunctions { \
nullptr, \
initializeLibrary, \
nullptr, \
nullptr, \
nullptr, \
nullptr, \
setImGuiContext, \
nullptr, \
nullptr \
}); \
} \
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializeLibrary()
#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *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)() { \
hex::PluginManager::addPlugin(hex::PluginFunctions { \
initializePlugin, \
nullptr, \
getPluginName, \
getPluginAuthor, \
getPluginDescription, \
getCompatibleVersion, \
setImGuiContext, \
nullptr, \
nullptr \
}); \
} \
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin()
/**
@@ -63,3 +92,12 @@
return &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_PLUGIN_FEATURES() IMHEX_PLUGIN_FEATURES_IMPL()
#define IMHEX_PLUGIN_FEATURES_IMPL() \
extern std::vector<hex::Feature> g_features; \
extern "C" [[gnu::visibility("default")]] void* getFeatures() { \
return &g_features; \
} \
std::vector<hex::Feature> g_features

View File

@@ -0,0 +1,46 @@
#pragma once
#include <hex/providers/provider.hpp>
namespace hex::prv {
/**
* This is a simple mock provider that can be used to pass in-memory data to APIs that require a provider.
* It's NOT a provider that can be loaded by the user.
*/
class MemoryProvider : public hex::prv::Provider {
public:
MemoryProvider() = default;
explicit MemoryProvider(std::vector<u8> data) : m_data(std::move(data)) { }
~MemoryProvider() override = default;
[[nodiscard]] bool isAvailable() const override { return true; }
[[nodiscard]] bool isReadable() const override { return true; }
[[nodiscard]] bool isWritable() const override { return true; }
[[nodiscard]] bool isResizable() const override { return true; }
[[nodiscard]] bool isSavable() const override { return m_name.empty(); }
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
[[nodiscard]] bool open() override;
void close() override { }
void readRaw(u64 offset, void *buffer, size_t size) override;
void writeRaw(u64 offset, const void *buffer, size_t size) override;
[[nodiscard]] u64 getActualSize() const override { return m_data.size(); }
void resizeRaw(u64 newSize) override;
void insertRaw(u64 offset, u64 size) override;
void removeRaw(u64 offset, u64 size) override;
[[nodiscard]] std::string getName() const override { return ""; }
[[nodiscard]] std::string getTypeName() const override { return "MemoryProvider"; }
private:
void renameFile();
private:
std::vector<u8> m_data;
std::string m_name;
};
}

View File

@@ -10,11 +10,11 @@ namespace hex::prv {
public:
Overlay() = default;
void setAddress(u64 address) { this->m_address = address; }
[[nodiscard]] u64 getAddress() const { return this->m_address; }
void setAddress(u64 address) { m_address = address; }
[[nodiscard]] u64 getAddress() const { return m_address; }
[[nodiscard]] u64 getSize() const { return this->m_data.size(); }
[[nodiscard]] std::vector<u8> &getData() { return this->m_data; }
[[nodiscard]] u64 getSize() const { return m_data.size(); }
[[nodiscard]] std::vector<u8> &getData() { return m_data; }
private:
u64 m_address = 0;

View File

@@ -33,7 +33,7 @@ namespace hex::prv {
std::function<void()> callback;
};
constexpr static size_t MaxPageSize = 0x1000'0000;
constexpr static u64 MaxPageSize = 0xFFFF'FFFF'FFFF'FFFF;
Provider();
virtual ~Provider();
@@ -137,7 +137,7 @@ namespace hex::prv {
* @brief Get the full size of the data in this provider
* @return The size of the entire available data of this provider
*/
[[nodiscard]] virtual size_t getActualSize() const = 0;
[[nodiscard]] virtual u64 getActualSize() const = 0;
/**
* @brief Gets the type name of this provider
@@ -156,13 +156,13 @@ namespace hex::prv {
*/
[[nodiscard]] virtual std::string getName() const = 0;
void resize(size_t newSize);
void insert(u64 offset, size_t size);
void remove(u64 offset, size_t size);
void resize(u64 newSize);
void insert(u64 offset, u64 size);
void remove(u64 offset, u64 size);
virtual void resizeRaw(size_t newSize) { hex::unused(newSize); }
virtual void insertRaw(u64 offset, size_t size) { hex::unused(offset, size); }
virtual void removeRaw(u64 offset, size_t size) { hex::unused(offset, size); }
virtual void resizeRaw(u64 newSize) { hex::unused(newSize); }
virtual void insertRaw(u64 offset, u64 size) { hex::unused(offset, size); }
virtual void removeRaw(u64 offset, u64 size) { hex::unused(offset, size); }
virtual void save();
virtual void saveAs(const std::fs::path &path);
@@ -172,8 +172,8 @@ namespace hex::prv {
void applyOverlays(u64 offset, void *buffer, size_t size) const;
[[nodiscard]] const std::list<std::unique_ptr<Overlay>> &getOverlays() const;
[[nodiscard]] size_t getPageSize() const;
void setPageSize(size_t pageSize);
[[nodiscard]] u64 getPageSize() const;
void setPageSize(u64 pageSize);
[[nodiscard]] u32 getPageCount() const;
[[nodiscard]] u32 getCurrentPage() const;
@@ -182,7 +182,7 @@ namespace hex::prv {
virtual void setBaseAddress(u64 address);
[[nodiscard]] virtual u64 getBaseAddress() const;
[[nodiscard]] virtual u64 getCurrentPageAddress() const;
[[nodiscard]] virtual size_t getSize() const;
[[nodiscard]] virtual u64 getSize() const;
[[nodiscard]] virtual std::optional<u32> getPageOfAddress(u64 address) const;
[[nodiscard]] virtual std::vector<Description> getDataDescription() const;
@@ -210,23 +210,23 @@ namespace hex::prv {
[[nodiscard]] virtual nlohmann::json storeSettings(nlohmann::json settings) const;
virtual void loadSettings(const nlohmann::json &settings);
void markDirty(bool dirty = true) { this->m_dirty = dirty; }
[[nodiscard]] bool isDirty() const { return this->m_dirty; }
void markDirty(bool dirty = true) { m_dirty = dirty; }
[[nodiscard]] bool isDirty() const { return m_dirty; }
[[nodiscard]] virtual std::pair<Region, bool> getRegionValidity(u64 address) const;
void skipLoadInterface() { this->m_skipLoadInterface = true; }
[[nodiscard]] bool shouldSkipLoadInterface() const { return this->m_skipLoadInterface; }
void skipLoadInterface() { m_skipLoadInterface = true; }
[[nodiscard]] bool shouldSkipLoadInterface() const { return m_skipLoadInterface; }
void setErrorMessage(const std::string &errorMessage) { this->m_errorMessage = errorMessage; }
[[nodiscard]] const std::string& getErrorMessage() const { return this->m_errorMessage; }
void setErrorMessage(const std::string &errorMessage) { m_errorMessage = errorMessage; }
[[nodiscard]] const std::string& getErrorMessage() const { return m_errorMessage; }
template<std::derived_from<undo::Operation> T>
bool addUndoableOperation(auto && ... args) {
return this->m_undoRedoStack.add<T>(std::forward<decltype(args)...>(args)...);
return m_undoRedoStack.add<T>(std::forward<decltype(args)...>(args)...);
}
[[nodiscard]] undo::Stack& getUndoStack() { return this->m_undoRedoStack; }
[[nodiscard]] undo::Stack& getUndoStack() { return m_undoRedoStack; }
protected:
u32 m_currPage = 0;
@@ -255,7 +255,7 @@ namespace hex::prv {
std::string m_errorMessage;
size_t m_pageSize = MaxPageSize;
u64 m_pageSize = MaxPageSize;
};
}

View File

@@ -34,19 +34,19 @@ namespace hex {
}
T& get(prv::Provider *provider = ImHexApi::Provider::get()) {
return this->m_data[provider];
return m_data[provider];
}
const T& get(prv::Provider *provider = ImHexApi::Provider::get()) const {
return this->m_data[provider];
return m_data.at(provider);
}
void set(const T &data, prv::Provider *provider = ImHexApi::Provider::get()) {
this->m_data[provider] = data;
m_data[provider] = data;
}
void set(T &&data, prv::Provider *provider = ImHexApi::Provider::get()) {
this->m_data[provider] = std::move(data);
m_data[provider] = std::move(data);
}
T& operator*() {
@@ -73,20 +73,20 @@ namespace hex {
private:
void onCreate() {
EventManager::subscribe<EventProviderOpened>(this, [this](prv::Provider *provider) {
this->m_data.emplace(provider, T());
EventProviderOpened::subscribe(this, [this](prv::Provider *provider) {
m_data.emplace(provider, T());
});
EventManager::subscribe<EventProviderDeleted>(this, [this](prv::Provider *provider){
this->m_data.erase(provider);
EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){
m_data.erase(provider);
});
EventManager::subscribe<EventImHexClosing>(this, [this] {
this->m_data.clear();
EventImHexClosing::subscribe(this, [this] {
m_data.clear();
});
// Moves the data of this PerProvider instance from one provider to another
EventManager::subscribe<MovePerProviderData>(this, [this](prv::Provider *from, prv::Provider *to) {
MovePerProviderData::subscribe(this, [this](prv::Provider *from, prv::Provider *to) {
// Get the value from the old provider, (removes it from the map)
auto node = m_data.extract(from);
@@ -94,18 +94,19 @@ namespace hex {
if (node.empty()) return;
// Delete the value from the new provider, that we want to replace
this->m_data.erase(to);
m_data.erase(to);
// Re-insert it with the key of the new provider
node.key() = to;
this->m_data.insert(std::move(node));
m_data.insert(std::move(node));
});
}
void onDestroy() {
EventManager::unsubscribe<EventProviderOpened>(this);
EventManager::unsubscribe<EventProviderDeleted>(this);
EventManager::unsubscribe<EventImHexClosing>(this);
EventProviderOpened::unsubscribe(this);
EventProviderDeleted::unsubscribe(this);
EventImHexClosing::unsubscribe(this);
MovePerProviderData::unsubscribe(this);
}
private:

View File

@@ -10,44 +10,44 @@ namespace hex::prv::undo {
class OperationGroup : public Operation {
public:
explicit OperationGroup(std::string unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {}
explicit OperationGroup(UnlocalizedString unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {}
OperationGroup(const OperationGroup &other) {
for (const auto &operation : other.m_operations)
this->m_operations.emplace_back(operation->clone());
m_operations.emplace_back(operation->clone());
}
void undo(Provider *provider) override {
for (auto &operation : this->m_operations)
for (auto &operation : m_operations)
operation->undo(provider);
}
void redo(Provider *provider) override {
for (auto &operation : this->m_operations)
for (auto &operation : m_operations)
operation->redo(provider);
}
void addOperation(std::unique_ptr<Operation> &&newOperation) {
auto newRegion = newOperation->getRegion();
if (newRegion.getStartAddress() < this->m_startAddress)
this->m_startAddress = newRegion.getStartAddress();
if (newRegion.getEndAddress() > this->m_endAddress)
this->m_endAddress = newRegion.getEndAddress();
if (newRegion.getStartAddress() < m_startAddress)
m_startAddress = newRegion.getStartAddress();
if (newRegion.getEndAddress() > m_endAddress)
m_endAddress = newRegion.getEndAddress();
if (this->m_formattedContent.size() <= 10)
this->m_formattedContent.emplace_back(newOperation->format());
if (m_formattedContent.size() <= 10)
m_formattedContent.emplace_back(newOperation->format());
else
this->m_formattedContent.back() = hex::format("[{}x] ...", (this->m_operations.size() - 10) + 1);
m_formattedContent.back() = hex::format("[{}x] ...", (m_operations.size() - 10) + 1);
this->m_operations.emplace_back(std::move(newOperation));
m_operations.emplace_back(std::move(newOperation));
}
[[nodiscard]] std::string format() const override {
return hex::format("{}", Lang(this->m_unlocalizedName));
return hex::format("{}", Lang(m_unlocalizedName));
}
[[nodiscard]] Region getRegion() const override {
return Region { this->m_startAddress, (this->m_endAddress - this->m_startAddress) + 1 };
return Region { m_startAddress, (m_endAddress - m_startAddress) + 1 };
}
std::unique_ptr<Operation> clone() const override {
@@ -55,11 +55,11 @@ namespace hex::prv::undo {
}
std::vector<std::string> formatContent() const override {
return this->m_formattedContent;
return m_formattedContent;
}
private:
std::string m_unlocalizedName;
UnlocalizedString m_unlocalizedName;
std::vector<std::unique_ptr<Operation>> m_operations;
u64 m_startAddress = std::numeric_limits<u64>::max();

View File

@@ -1,9 +1,10 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/providers/undo_redo/operations/operation.hpp>
#include <atomic>
#include <map>
#include <memory>
#include <mutex>
@@ -24,7 +25,7 @@ namespace hex::prv::undo {
void undo(u32 count = 1);
void redo(u32 count = 1);
void groupOperations(u32 count, const std::string &unlocalizedName);
void groupOperations(u32 count, const UnlocalizedString &unlocalizedName);
void apply(const Stack &otherStack);
[[nodiscard]] bool canUndo() const;
@@ -38,21 +39,21 @@ namespace hex::prv::undo {
bool add(std::unique_ptr<Operation> &&operation);
const std::vector<std::unique_ptr<Operation>> &getAppliedOperations() const {
return this->m_undoStack;
return m_undoStack;
}
const std::vector<std::unique_ptr<Operation>> &getUndoneOperations() const {
return this->m_redoStack;
return m_redoStack;
}
void reset() {
this->m_undoStack.clear();
this->m_redoStack.clear();
m_undoStack.clear();
m_redoStack.clear();
}
private:
[[nodiscard]] Operation* getLastOperation() const {
return this->m_undoStack.back().get();
return m_undoStack.back().get();
}
private:

View File

@@ -10,6 +10,7 @@
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/concepts.hpp>
#include <hex/helpers/fs.hpp>
#include <wolv/utils/string.hpp>
@@ -69,10 +70,16 @@ namespace ImGuiExt {
class Texture {
public:
enum class Filter {
Linear,
Nearest
};
Texture() = default;
Texture(const ImU8 *buffer, int size, int width = 0, int height = 0);
Texture(std::span<const std::byte> bytes, int width = 0, int height = 0);
explicit Texture(const char *path);
Texture(const ImU8 *buffer, int size, Filter filter = Filter::Nearest, int width = 0, int height = 0);
Texture(std::span<const std::byte> bytes, Filter filter = Filter::Nearest, int width = 0, int height = 0);
explicit Texture(const char *path, Filter filter = Filter::Nearest);
explicit Texture(const std::fs::path &path, Filter filter = Filter::Nearest);
Texture(unsigned int texture, int width, int height);
Texture(const Texture&) = delete;
Texture(Texture&& other) noexcept;
@@ -83,21 +90,25 @@ namespace ImGuiExt {
Texture& operator=(Texture&& other) noexcept;
[[nodiscard]] constexpr bool isValid() const noexcept {
return this->m_textureId != nullptr;
return m_textureId != nullptr;
}
[[nodiscard]] constexpr operator ImTextureID() const noexcept {
return this->m_textureId;
[[nodiscard]] operator ImTextureID() const noexcept {
return m_textureId;
}
[[nodiscard]] operator intptr_t() const noexcept {
return reinterpret_cast<intptr_t>(m_textureId);
}
[[nodiscard]] auto getSize() const noexcept {
return ImVec2(this->m_width, this->m_height);
return ImVec2(m_width, m_height);
}
[[nodiscard]] constexpr auto getAspectRatio() const noexcept {
if (this->m_height == 0) return 1.0F;
if (m_height == 0) return 1.0F;
return float(this->m_width) / float(this->m_height);
return float(m_width) / float(m_height);
}
private:
@@ -122,7 +133,7 @@ namespace ImGuiExt {
void Header(const char *label, bool firstEntry = false);
void HeaderColored(const char *label, ImColor color, bool firstEntry);
bool InfoTooltip(const char *text = "");
bool InfoTooltip(const char *text = "",bool = false);
bool TitleBarButton(const char *label, ImVec2 size_arg);
bool ToolBarButton(const char *symbol, ImVec4 color);
@@ -258,6 +269,7 @@ namespace ImGuiExt {
bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size = ImVec2(0, 0));
bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size);
bool DimmedIconToggle(const char *icon, bool *v);
bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v);
void TextOverlay(const char *text, ImVec2 pos);
@@ -277,6 +289,14 @@ namespace ImGuiExt {
if (ImGui::Button(textRight, ImVec2(width / 3, 0)))
rightButtonCallback();
}
bool VSliderAngle(const char* label, ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags);
bool InputFilePicker(const char *label, std::fs::path &path, const std::vector<hex::fs::ItemFilter> &validExtensions);
bool ToggleSwitch(const char *label, bool *v);
bool ToggleSwitch(const char *label, bool v);
template<typename T>
constexpr ImGuiDataType getImGuiDataType() {
if constexpr (std::same_as<T, u8>) return ImGuiDataType_U8;

View File

@@ -1,6 +1,7 @@
#pragma once
#include <hex.hpp>
#include <hex/api/localization_manager.hpp>
#include <memory>
#include <string>
@@ -17,7 +18,7 @@ namespace hex {
class PopupBase {
public:
explicit PopupBase(std::string unlocalizedName, bool closeButton, bool modal)
explicit PopupBase(UnlocalizedString unlocalizedName, bool closeButton, bool modal)
: m_unlocalizedName(std::move(unlocalizedName)), m_closeButton(closeButton), m_modal(modal) { }
virtual ~PopupBase() = default;
@@ -35,29 +36,30 @@ namespace hex {
[[nodiscard]] static std::vector<std::unique_ptr<PopupBase>> &getOpenPopups();
[[nodiscard]] const std::string &getUnlocalizedName() const {
return this->m_unlocalizedName;
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const {
return m_unlocalizedName;
}
[[nodiscard]] bool hasCloseButton() const {
return this->m_closeButton;
return m_closeButton;
}
[[nodiscard]] bool isModal() const {
return this->m_modal;
return m_modal;
}
void close() {
this->m_close = true;
m_close = true;
}
[[nodiscard]] bool shouldClose() const {
return this->m_close;
return m_close;
}
protected:
static std::mutex& getMutex();
private:
std::string m_unlocalizedName;
UnlocalizedString m_unlocalizedName;
bool m_closeButton, m_modal;
std::atomic<bool> m_close = false;
};
@@ -68,13 +70,12 @@ namespace hex {
template<typename T>
class Popup : public impl::PopupBase {
protected:
explicit Popup(std::string unlocalizedName, bool closeButton = true, bool modal = true) : PopupBase(std::move(unlocalizedName), closeButton, modal) { }
explicit Popup(UnlocalizedString unlocalizedName, bool closeButton = true, bool modal = true) : PopupBase(std::move(unlocalizedName), closeButton, modal) { }
public:
template<typename ...Args>
static void open(Args && ... args) {
static std::mutex mutex;
std::lock_guard lock(mutex);
std::lock_guard lock(getMutex());
auto popup = std::make_unique<T>(std::forward<Args>(args)...);

View File

@@ -0,0 +1,61 @@
#pragma once
#include <hex.hpp>
#include <imgui.h>
#include <list>
#include <memory>
#include <mutex>
namespace hex {
namespace impl {
class ToastBase {
public:
ToastBase(ImColor color) : m_color(color) {}
virtual ~ToastBase() = default;
virtual void draw() { drawContent(); }
virtual void drawContent() = 0;
[[nodiscard]] static std::list<std::unique_ptr<ToastBase>> &getQueuedToasts();
[[nodiscard]] const ImColor& getColor() const {
return m_color;
}
void setAppearTime(double appearTime) {
m_appearTime = appearTime;
}
[[nodiscard]] double getAppearTime() const {
return m_appearTime;
}
constexpr static double VisibilityTime = 4.0;
protected:
static std::mutex& getMutex();
double m_appearTime = 0;
ImColor m_color;
};
}
template<typename T>
class Toast : public impl::ToastBase {
public:
using impl::ToastBase::ToastBase;
template<typename ...Args>
static void open(Args && ... args) {
std::lock_guard lock(getMutex());
auto toast = std::make_unique<T>(std::forward<Args>(args)...);
getQueuedToasts().emplace_back(std::move(toast));
}
};
}

View File

@@ -6,17 +6,17 @@
#include <imgui_internal.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <fonts/fontawesome_font.h>
#include <fonts/codicons_font.h>
#include <hex/api/imhex_api.hpp>
#include <hex/api/shortcut_manager.hpp>
#include <hex/api/event_manager.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/providers/provider.hpp>
#include <hex/providers/provider_data.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/api/localization_manager.hpp>
#include <map>
#include <string>
@@ -24,7 +24,7 @@
namespace hex {
class View {
explicit View(std::string unlocalizedName);
explicit View(UnlocalizedString unlocalizedName);
public:
virtual ~View() = default;
@@ -87,15 +87,17 @@ namespace hex {
[[nodiscard]] bool &getWindowOpenState();
[[nodiscard]] const bool &getWindowOpenState() const;
[[nodiscard]] const std::string &getUnlocalizedName() const;
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const;
[[nodiscard]] std::string getName() const;
[[nodiscard]] bool didWindowJustOpen();
void setWindowJustOpened(bool state);
void trackViewOpenState();
static void discardNavigationRequests();
[[nodiscard]] static std::string toWindowName(const std::string &unlocalizedName);
[[nodiscard]] static std::string toWindowName(const UnlocalizedString &unlocalizedName);
public:
class Window;
@@ -104,8 +106,8 @@ namespace hex {
class Modal;
private:
std::string m_unlocalizedViewName;
bool m_windowOpen = false;
UnlocalizedString m_unlocalizedViewName;
bool m_windowOpen = false, m_prevWindowOpen = false;
std::map<Shortcut, ShortcutManager::ShortcutEntry> m_shortcuts;
bool m_windowJustOpened = false;
@@ -118,7 +120,7 @@ namespace hex {
*/
class View::Window : public View {
public:
explicit Window(std::string unlocalizedName) : View(std::move(unlocalizedName)) {}
explicit Window(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {}
void draw() final {
if (this->shouldDraw()) {
@@ -137,7 +139,7 @@ namespace hex {
*/
class View::Special : public View {
public:
explicit Special(std::string unlocalizedName) : View(std::move(unlocalizedName)) {}
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {}
void draw() final {
if (this->shouldDraw()) {
@@ -152,9 +154,9 @@ namespace hex {
*/
class View::Floating : public View::Window {
public:
explicit Floating(std::string unlocalizedName) : Window(std::move(unlocalizedName)) {}
explicit Floating(UnlocalizedString unlocalizedName) : Window(std::move(unlocalizedName)) {}
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const { return ImGuiWindowFlags_NoDocking; }
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoDocking; }
};
/**
@@ -162,22 +164,27 @@ namespace hex {
*/
class View::Modal : public View {
public:
explicit Modal(std::string unlocalizedName) : View(std::move(unlocalizedName)) {}
explicit Modal(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {}
void draw() final {
if (this->shouldDraw()) {
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
if (this->getWindowOpenState())
ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str());
if (ImGui::BeginPopupModal(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | this->getWindowFlags())) {
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
if (ImGui::BeginPopupModal(View::toWindowName(this->getUnlocalizedName()).c_str(), this->hasCloseButton() ? &this->getWindowOpenState() : nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | this->getWindowFlags())) {
this->drawContent();
ImGui::EndPopup();
}
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
this->getWindowOpenState() = false;
}
}
virtual bool hasCloseButton() const { return true; }
};
}

View File

@@ -21,34 +21,34 @@ namespace hex::ui {
}
const std::vector<const T*> &draw(const auto &entries) {
if (this->m_filteredEntries.empty() && this->m_searchBuffer.empty()) {
if (m_filteredEntries.empty() && m_searchBuffer.empty()) {
for (auto &entry : entries)
this->m_filteredEntries.push_back(&entry);
m_filteredEntries.push_back(&entry);
}
if (ImGui::InputText("##search", this->m_searchBuffer)) {
this->m_pendingUpdate = true;
if (ImGui::InputText("##search", m_searchBuffer)) {
m_pendingUpdate = true;
}
if (this->m_pendingUpdate && !this->m_updateTask.isRunning()) {
this->m_pendingUpdate = false;
this->m_filteredEntries.clear();
this->m_filteredEntries.reserve(entries.size());
if (m_pendingUpdate && !m_updateTask.isRunning()) {
m_pendingUpdate = false;
m_filteredEntries.clear();
m_filteredEntries.reserve(entries.size());
this->m_updateTask = TaskManager::createBackgroundTask("Searching", [this, &entries, searchBuffer = this->m_searchBuffer](Task&) {
m_updateTask = TaskManager::createBackgroundTask("Searching", [this, &entries, searchBuffer = m_searchBuffer](Task&) {
for (auto &entry : entries) {
if (searchBuffer.empty() || this->m_comparator(searchBuffer, entry))
this->m_filteredEntries.push_back(&entry);
if (searchBuffer.empty() || m_comparator(searchBuffer, entry))
m_filteredEntries.push_back(&entry);
}
});
}
return this->m_filteredEntries;
return m_filteredEntries;
}
void reset() {
this->m_filteredEntries.clear();
m_filteredEntries.clear();
}
private:
std::atomic<bool> m_pendingUpdate = false;

View File

@@ -87,7 +87,7 @@ namespace hex {
return startNodes;
}
void AchievementManager::unlockAchievement(const std::string &unlocalizedCategory, const std::string &unlocalizedName) {
void AchievementManager::unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName) {
auto &categories = getAchievements();
auto categoryIter = categories.find(unlocalizedCategory);
@@ -128,7 +128,7 @@ namespace hex {
achievement->setUnlocked(true);
if (achievement->isUnlocked())
EventManager::post<EventAchievementUnlocked>(*achievement);
EventAchievementUnlocked::post(*achievement);
return;
}
@@ -201,7 +201,11 @@ namespace hex {
for (const auto &[categoryName, achievements] : getAchievements()) {
for (const auto &[achievementName, achievement] : achievements) {
try {
achievement->setProgress(json[categoryName][achievementName]);
const auto &progress = json[categoryName][achievementName];
if (progress.is_null())
continue;
achievement->setProgress(progress);
} catch (const std::exception &e) {
log::warn("Failed to load achievement progress for '{}::{}': {}", categoryName, achievementName, e.what());
}
@@ -218,7 +222,7 @@ namespace hex {
for (const auto &directory : fs::getDefaultPaths(fs::ImHexPath::Config)) {
auto path = directory / AchievementsFile;
wolv::io::File file(path, wolv::io::File::Mode::Create);
wolv::io::File file(path, wolv::io::File::Mode::Write);
if (!file.isValid()) {
continue;
@@ -234,7 +238,9 @@ namespace hex {
}
}
file.writeString(json.dump(4));
auto result = json.dump(4);
file.setSize(0);
file.writeString(result);
break;
}
}

View File

@@ -8,7 +8,6 @@
#include <hex/data_processor/node.hpp>
#include <filesystem>
#include <thread>
#include <jthread.hpp>
#if defined(OS_WEB)
@@ -28,7 +27,7 @@ namespace hex {
namespace impl {
nlohmann::json& getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue) {
nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue) {
auto &settings = getSettingsData();
if (!settings.contains(unlocalizedCategory))
@@ -90,16 +89,21 @@ namespace hex {
}
void store() {
auto settingsData = getSettingsData();
// During a crash settings can be empty, causing them to be overwritten.
if(getSettingsData().empty()) {
if (settingsData.empty()) {
return;
}
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Create);
wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Write);
if (file.isValid()) {
file.writeString(getSettingsData().dump(4));
auto result = settingsData.dump(4);
file.setSize(0);
file.writeString(result);
break;
}
}
@@ -113,7 +117,7 @@ namespace hex {
#endif
template<typename T>
static T* insertOrGetEntry(std::vector<T> &vector, const std::string &unlocalizedName) {
static T* insertOrGetEntry(std::vector<T> &vector, const UnlocalizedString &unlocalizedName) {
T *foundEntry = nullptr;
for (auto &entry : vector) {
if (entry.unlocalizedName == unlocalizedName) {
@@ -138,7 +142,7 @@ namespace hex {
return categories;
}
Widgets::Widget* add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &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) {
const auto category = insertOrGetEntry(getSettings(), unlocalizedCategory);
const auto subCategory = insertOrGetEntry(category->subCategories, unlocalizedSubCategory);
const auto entry = insertOrGetEntry(subCategory->entries, unlocalizedName);
@@ -152,13 +156,13 @@ namespace hex {
}
void setCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedDescription) {
void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription) {
const auto category = insertOrGetEntry(impl::getSettings(), unlocalizedCategory);
category->unlocalizedDescription = unlocalizedDescription;
}
nlohmann::json read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue) {
nlohmann::json read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue) {
auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);
if (setting.is_number() && defaultValue.is_boolean())
@@ -169,67 +173,67 @@ namespace hex {
return setting;
}
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &value) {
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value) {
impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;
}
namespace Widgets {
bool Checkbox::draw(const std::string &name) {
return ImGui::Checkbox(name.c_str(), &this->m_value);
return ImGui::Checkbox(name.c_str(), &m_value);
}
void Checkbox::load(const nlohmann::json &data) {
if (data.is_number()) {
this->m_value = data.get<int>() != 0;
m_value = data.get<int>() != 0;
} else if (data.is_boolean()) {
this->m_value = data.get<bool>();
m_value = data.get<bool>();
} else {
log::warn("Invalid data type loaded from settings for checkbox!");
}
}
nlohmann::json Checkbox::store() {
return this->m_value;
return m_value;
}
bool SliderInteger::draw(const std::string &name) {
return ImGui::SliderInt(name.c_str(), &this->m_value, this->m_min, this->m_max);
return ImGui::SliderInt(name.c_str(), &m_value, m_min, m_max);
}
void SliderInteger::load(const nlohmann::json &data) {
if (data.is_number_integer()) {
this->m_value = data.get<int>();
m_value = data.get<int>();
} else {
log::warn("Invalid data type loaded from settings for slider!");
}
}
nlohmann::json SliderInteger::store() {
return this->m_value;
return m_value;
}
bool SliderFloat::draw(const std::string &name) {
return ImGui::SliderFloat(name.c_str(), &this->m_value, this->m_min, this->m_max);
return ImGui::SliderFloat(name.c_str(), &m_value, m_min, m_max);
}
void SliderFloat::load(const nlohmann::json &data) {
if (data.is_number()) {
this->m_value = data.get<float>();
m_value = data.get<float>();
} else {
log::warn("Invalid data type loaded from settings for slider!");
}
}
nlohmann::json SliderFloat::store() {
return this->m_value;
return m_value;
}
ColorPicker::ColorPicker(ImColor defaultColor) {
this->m_value = {
m_value = {
defaultColor.Value.x,
defaultColor.Value.y,
defaultColor.Value.z,
@@ -238,43 +242,43 @@ namespace hex {
}
bool ColorPicker::draw(const std::string &name) {
return ImGui::ColorEdit4(name.c_str(), this->m_value.data(), ImGuiColorEditFlags_NoInputs);
return ImGui::ColorEdit4(name.c_str(), m_value.data(), ImGuiColorEditFlags_NoInputs);
}
void ColorPicker::load(const nlohmann::json &data) {
if (data.is_number()) {
ImColor color(data.get<u32>());
this->m_value = { color.Value.x, color.Value.y, color.Value.z, color.Value.w };
m_value = { color.Value.x, color.Value.y, color.Value.z, color.Value.w };
} else {
log::warn("Invalid data type loaded from settings for color picker!");
}
}
nlohmann::json ColorPicker::store() {
const ImColor color(this->m_value[0], this->m_value[1], this->m_value[2], this->m_value[3]);
const ImColor color(m_value[0], m_value[1], m_value[2], m_value[3]);
return static_cast<ImU32>(color);
}
ImColor ColorPicker::getColor() const {
return { this->m_value[0], this->m_value[1], this->m_value[2], this->m_value[3] };
return { m_value[0], m_value[1], m_value[2], m_value[3] };
}
bool DropDown::draw(const std::string &name) {
const char *preview = "";
if (static_cast<size_t>(this->m_value) < this->m_items.size())
preview = this->m_items[this->m_value].c_str();
if (static_cast<size_t>(m_value) < m_items.size())
preview = m_items[m_value].c_str();
bool changed = false;
if (ImGui::BeginCombo(name.c_str(), Lang(preview))) {
int index = 0;
for (const auto &item : this->m_items) {
const bool selected = index == this->m_value;
for (const auto &item : m_items) {
const bool selected = index == m_value;
if (ImGui::Selectable(Lang(item), selected)) {
this->m_value = index;
m_value = index;
changed = true;
}
@@ -291,60 +295,60 @@ namespace hex {
}
void DropDown::load(const nlohmann::json &data) {
this->m_value = 0;
m_value = 0;
int defaultItemIndex = 0;
int index = 0;
for (const auto &item : this->m_settingsValues) {
if (item == this->m_defaultItem)
for (const auto &item : m_settingsValues) {
if (item == m_defaultItem)
defaultItemIndex = index;
if (item == data) {
this->m_value = index;
m_value = index;
return;
}
index += 1;
}
this->m_value = defaultItemIndex;
m_value = defaultItemIndex;
}
nlohmann::json DropDown::store() {
if (this->m_value == -1)
return this->m_defaultItem;
if (static_cast<size_t>(this->m_value) >= this->m_items.size())
return this->m_defaultItem;
if (m_value == -1)
return m_defaultItem;
if (static_cast<size_t>(m_value) >= m_items.size())
return m_defaultItem;
return this->m_settingsValues[this->m_value];
return m_settingsValues[m_value];
}
const nlohmann::json& DropDown::getValue() const {
return this->m_settingsValues[this->m_value];
return m_settingsValues[m_value];
}
bool TextBox::draw(const std::string &name) {
return ImGui::InputText(name.c_str(), this->m_value);
return ImGui::InputText(name.c_str(), m_value);
}
void TextBox::load(const nlohmann::json &data) {
if (data.is_string()) {
this->m_value = data.get<std::string>();
m_value = data.get<std::string>();
} else {
log::warn("Invalid data type loaded from settings for text box!");
}
}
nlohmann::json TextBox::store() {
return this->m_value;
return m_value;
}
bool FilePicker::draw(const std::string &name) {
bool changed = false;
if (ImGui::InputText("##font_path", this->m_value)) {
if (ImGui::InputText("##font_path", m_value)) {
changed = true;
}
@@ -353,7 +357,7 @@ namespace hex {
if (ImGuiExt::IconButton(ICON_VS_FOLDER_OPENED, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
return fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } },
[&](const std::fs::path &path) {
this->m_value = wolv::util::toUTF8String(path);
m_value = wolv::util::toUTF8String(path);
});
}
@@ -366,16 +370,24 @@ namespace hex {
void FilePicker::load(const nlohmann::json &data) {
if (data.is_string()) {
this->m_value = data.get<std::string>();
m_value = data.get<std::string>();
} else {
log::warn("Invalid data type loaded from settings for file picker!");
}
}
nlohmann::json FilePicker::store() {
return this->m_value;
return m_value;
}
bool Label::draw(const std::string& name) {
ImGui::NewLine();
ImGui::TextUnformatted(name.c_str());
return false;
}
}
}
@@ -383,7 +395,7 @@ namespace hex {
namespace ContentRegistry::CommandPaletteCommands {
void add(Type type, const std::string &command, const std::string &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) {
void add(Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) {
log::debug("Registered new command palette command: {}", command);
impl::getEntries().push_back(impl::Entry { type, command, unlocalizedDescription, displayCallback, executeCallback });
@@ -553,12 +565,12 @@ namespace hex {
}
void impl::add(std::unique_ptr<View> &&view) {
log::debug("Registered new view: {}", view->getUnlocalizedName());
log::debug("Registered new view: {}", view->getUnlocalizedName().get());
getEntries().insert({ view->getUnlocalizedName(), std::move(view) });
}
View* getViewByName(const std::string &unlocalizedName) {
View* getViewByName(const UnlocalizedString &unlocalizedName) {
auto &views = impl::getEntries();
if (views.contains(unlocalizedName))
@@ -571,8 +583,8 @@ namespace hex {
namespace ContentRegistry::Tools {
void add(const std::string &unlocalizedName, const impl::Callback &function) {
log::debug("Registered new tool: {}", unlocalizedName);
void add(const UnlocalizedString &unlocalizedName, const impl::Callback &function) {
log::debug("Registered new tool: {}", unlocalizedName.get());
impl::getEntries().emplace_back(impl::Entry { unlocalizedName, function, false });
}
@@ -591,14 +603,14 @@ namespace hex {
namespace ContentRegistry::DataInspector {
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
log::debug("Registered new data inspector format: {}", unlocalizedName);
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
log::debug("Registered new data inspector format: {}", unlocalizedName.get());
impl::getEntries().push_back({ unlocalizedName, requiredSize, requiredSize, std::move(displayGeneratorFunction), std::move(editingFunction) });
}
void add(const std::string &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
log::debug("Registered new data inspector format: {}", unlocalizedName);
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
log::debug("Registered new data inspector format: {}", unlocalizedName.get());
impl::getEntries().push_back({ unlocalizedName, requiredSize, maxSize, std::move(displayGeneratorFunction), std::move(editingFunction) });
}
@@ -619,7 +631,7 @@ namespace hex {
namespace ContentRegistry::DataProcessorNode {
void impl::add(const Entry &entry) {
log::debug("Registered new data processor node type: [{}]: {}", entry.category, entry.name);
log::debug("Registered new data processor node type: [{}]: {}", entry.unlocalizedCategory.get(), entry.unlocalizedName.get());
getEntries().push_back(entry);
}
@@ -704,14 +716,14 @@ namespace hex {
namespace ContentRegistry::Interface {
void registerMainMenuItem(const std::string &unlocalizedName, u32 priority) {
log::debug("Registered new main menu item: {}", unlocalizedName);
void registerMainMenuItem(const UnlocalizedString &unlocalizedName, u32 priority) {
log::debug("Registered new main menu item: {}", unlocalizedName.get());
impl::getMainMenuItems().insert({ priority, { unlocalizedName } });
}
void addMenuItem(const std::vector<std::string> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) {
log::debug("Added new menu item to menu {} with priority {}", wolv::util::combineStrings(unlocalizedMainMenuNames, " -> "), priority);
void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) {
log::debug("Added new menu item to menu {} with priority {}", unlocalizedMainMenuNames[0].get(), priority);
impl::getMenuItems().insert({
priority, impl::MenuItem { unlocalizedMainMenuNames, std::make_unique<Shortcut>(shortcut), view, function, enabledCallback }
@@ -725,8 +737,8 @@ namespace hex {
}
}
void addMenuItemSubMenu(std::vector<std::string> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) {
log::debug("Added new menu item sub menu to menu {} with priority {}", wolv::util::combineStrings(unlocalizedMainMenuNames, " -> "), priority);
void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) {
log::debug("Added new menu item sub menu to menu {} with priority {}", unlocalizedMainMenuNames[0].get(), priority);
unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue);
impl::getMenuItems().insert({
@@ -734,7 +746,7 @@ namespace hex {
});
}
void addMenuItemSeparator(std::vector<std::string> unlocalizedMainMenuNames, u32 priority) {
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority) {
unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue);
impl::getMenuItems().insert({
priority, impl::MenuItem { unlocalizedMainMenuNames, std::make_unique<Shortcut>(), nullptr, []{}, []{ return true; } }
@@ -757,7 +769,7 @@ namespace hex {
impl::getSidebarItems().push_back({ icon, function, enabledCallback });
}
void addTitleBarButton(const std::string &icon, const std::string &unlocalizedTooltip, const impl::ClickCallback &function) {
void addTitleBarButton(const std::string &icon, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function) {
impl::getTitleBarButtons().push_back({ icon, unlocalizedTooltip, function });
}
@@ -809,7 +821,7 @@ namespace hex {
namespace impl {
void add(const std::string &typeName, ProviderCreationFunction creationFunction) {
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, prv::Provider **provider) {
(void)RequestCreateProvider::subscribe([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, prv::Provider **provider) {
if (name != expectedName) return;
prv::Provider *newProvider = creationFunction();
@@ -827,8 +839,8 @@ namespace hex {
return providerNames;
}
void addProviderName(const std::string &unlocalizedName) {
log::debug("Registered new provider: {}", unlocalizedName);
void addProviderName(const UnlocalizedString &unlocalizedName) {
log::debug("Registered new provider: {}", unlocalizedName.get());
getEntries().push_back(unlocalizedName);
}
@@ -840,8 +852,8 @@ namespace hex {
namespace ContentRegistry::DataFormatter {
void add(const std::string &unlocalizedName, const impl::Callback &callback) {
log::debug("Registered new data formatter: {}", unlocalizedName);
void add(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) {
log::debug("Registered new data formatter: {}", unlocalizedName.get());
impl::getEntries().push_back({ unlocalizedName, callback });
}
@@ -958,7 +970,7 @@ namespace hex {
}
std::shared_ptr<DataVisualizer> getVisualizerByName(const std::string &unlocalizedName) {
std::shared_ptr<DataVisualizer> getVisualizerByName(const UnlocalizedString &unlocalizedName) {
for (const auto &visualizer : impl::getVisualizers()) {
if (visualizer->getUnlocalizedName() == unlocalizedName)
return visualizer;
@@ -992,6 +1004,11 @@ namespace hex {
namespace impl {
struct Service {
std::string name;
std::jthread thread;
};
std::vector<Service> &getServices() {
static std::vector<Service> services;
@@ -999,20 +1016,24 @@ namespace hex {
}
void stopServices() {
for (auto &service : getServices()) {
auto &services = getServices();
for (auto &service : services) {
service.thread.request_stop();
}
for (auto &service : getServices()) {
for (auto &service : services) {
if (service.thread.joinable())
service.thread.join();
}
services.clear();
}
}
void registerService(const std::string &unlocalizedName, const impl::Callback &callback) {
log::debug("Registered new background service: {}", unlocalizedName);
void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) {
log::debug("Registered new background service: {}", unlocalizedName.get());
impl::getServices().push_back(impl::Service {
unlocalizedName,
@@ -1059,7 +1080,7 @@ namespace hex {
}
void addExperiment(const std::string &experimentName, const std::string &unlocalizedName, const std::string &unlocalizedDescription) {
void addExperiment(const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) {
auto &experiments = impl::getExperiments();
if (experiments.contains(experimentName)) {

View File

@@ -82,7 +82,7 @@ namespace hex {
id, Highlighting {region, color}
});
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
return id;
}
@@ -90,7 +90,7 @@ namespace hex {
void removeBackgroundHighlight(u32 id) {
impl::getBackgroundHighlights().erase(id);
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
}
u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) {
@@ -100,7 +100,7 @@ namespace hex {
impl::getBackgroundHighlightingFunctions().insert({ id, function });
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
return id;
}
@@ -108,7 +108,7 @@ namespace hex {
void removeBackgroundHighlightingProvider(u32 id) {
impl::getBackgroundHighlightingFunctions().erase(id);
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
}
u32 addForegroundHighlight(const Region &region, color_t color) {
@@ -120,7 +120,7 @@ namespace hex {
id, Highlighting {region, color}
});
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
return id;
}
@@ -128,7 +128,7 @@ namespace hex {
void removeForegroundHighlight(u32 id) {
impl::getForegroundHighlights().erase(id);
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
}
u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) {
@@ -138,7 +138,7 @@ namespace hex {
impl::getForegroundHighlightingFunctions().insert({ id, function });
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
return id;
}
@@ -146,7 +146,7 @@ namespace hex {
void removeForegroundHighlightingProvider(u32 id) {
impl::getForegroundHighlightingFunctions().erase(id);
EventManager::post<EventHighlightingChanged>();
EventHighlightingChanged::post();
}
static u32 tooltipId = 0;
@@ -190,7 +190,7 @@ namespace hex {
}
void setSelection(const ProviderRegion &region) {
EventManager::post<RequestSelectionChange>(region);
RequestSelectionChange::post(region);
}
void setSelection(u64 address, size_t size, prv::Provider *provider) {
@@ -204,7 +204,7 @@ namespace hex {
u64 add(Region region, const std::string &name, const std::string &comment, u32 color) {
u64 id = 0;
EventManager::post<RequestAddBookmark>(region, name, comment, color, &id);
RequestAddBookmark::post(region, name, comment, color, &id);
return id;
}
@@ -214,7 +214,7 @@ namespace hex {
}
void remove(u64 id) {
EventManager::post<RequestRemoveBookmark>(id);
RequestRemoveBookmark::post(id);
}
}
@@ -256,7 +256,7 @@ namespace hex {
if (index < s_providers.size() && s_currentProvider != index) {
auto oldProvider = get();
s_currentProvider = index;
EventManager::post<EventProviderChanged>(oldProvider, get());
EventProviderChanged::post(oldProvider, get());
}
}
@@ -291,7 +291,7 @@ namespace hex {
provider->skipLoadInterface();
s_providers.push_back(provider);
EventManager::post<EventProviderCreated>(provider);
EventProviderCreated::post(provider);
if (select || s_providers.size() == 1)
setCurrentProvider(s_providers.size() - 1);
@@ -308,7 +308,7 @@ namespace hex {
impl::s_closingProviders.push_back(provider);
bool shouldClose = true;
EventManager::post<EventProviderClosing>(provider, &shouldClose);
EventProviderClosing::post(provider, &shouldClose);
if (!shouldClose)
return;
}
@@ -321,6 +321,9 @@ namespace hex {
if (it == s_providers.begin()) {
// If the first provider is being closed, select the one that's the first one now
setCurrentProvider(0);
if (s_providers.size() > 1)
EventProviderChanged::post(s_providers[0], s_providers[1]);
}
else if (std::distance(s_providers.begin(), it) == s_currentProvider) {
// If the current provider is being closed, select the one that's before it
@@ -350,21 +353,21 @@ namespace hex {
setCurrentProvider(0);
if (s_providers.empty())
EventManager::post<EventProviderChanged>(provider, nullptr);
EventProviderChanged::post(provider, nullptr);
provider->close();
EventManager::post<EventProviderClosed>(provider);
EventProviderClosed::post(provider);
TaskManager::runWhenTasksFinished([provider] {
EventManager::post<EventProviderDeleted>(provider);
EventProviderDeleted::post(provider);
std::erase(impl::s_closingProviders, provider);
delete provider;
});
}
prv::Provider* createProvider(const std::string &unlocalizedName, bool skipLoadInterface, bool select) {
prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {
prv::Provider* result = nullptr;
EventManager::post<RequestCreateProvider>(unlocalizedName, skipLoadInterface, select, &result);
RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result);
return result;
}
@@ -378,7 +381,6 @@ namespace hex {
// Default to true means we forward to ourselves by default
static bool s_isMainInstance = true;
void setMainInstanceStatus(bool status) {
s_isMainInstance = status;
}
@@ -433,6 +435,12 @@ namespace hex {
getInitArguments()[key] = value;
}
static double s_lastFrameTime;
void setLastFrameTime(double time) {
s_lastFrameTime = time;
}
}
bool isMainInstance() {
@@ -440,16 +448,16 @@ namespace hex {
}
void closeImHex(bool noQuestions) {
EventManager::post<RequestCloseImHex>(noQuestions);
RequestCloseImHex::post(noQuestions);
}
void restartImHex() {
EventManager::post<RequestRestartImHex>();
EventManager::post<RequestCloseImHex>(false);
RequestRestartImHex::post();
RequestCloseImHex::post(false);
}
void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress) {
EventManager::post<EventSetTaskBarIconState>(u32(state), u32(type), progress);
EventSetTaskBarIconState::post(u32(state), u32(type), progress);
}
@@ -503,8 +511,8 @@ namespace hex {
void enableSystemThemeDetection(bool enabled) {
s_systemThemeDetection = enabled;
EventManager::post<EventSettingsChanged>();
EventManager::post<EventOSThemeChanged>();
EventSettingsChanged::post();
EventOSThemeChanged::post();
}
bool usesSystemThemeDetection() {
@@ -598,9 +606,9 @@ namespace hex {
std::string getImHexVersion(bool withBuildType) {
#if defined IMHEX_VERSION
if (withBuildType)
if (withBuildType) {
return IMHEX_VERSION;
else {
} else {
auto version = std::string(IMHEX_VERSION);
return version.substr(0, version.find('-'));
}
@@ -666,7 +674,7 @@ namespace hex {
break;
}
EventManager::subscribe<EventImHexClosing>([executablePath, updateTypeString] {
EventImHexClosing::subscribe([executablePath, updateTypeString] {
hex::executeCommand(
hex::format("{} {}",
wolv::util::toUTF8String(executablePath),
@@ -681,9 +689,14 @@ namespace hex {
}
void addStartupTask(const std::string &name, bool async, const std::function<bool()> &function) {
EventManager::post<RequestAddInitTask>(name, async, function);
RequestAddInitTask::post(name, async, function);
}
double getLastFrameTime() {
return impl::s_lastFrameTime;
}
}
namespace ImHexApi::Messaging {

View File

@@ -1,11 +1,13 @@
#include <hex/api/layout_manager.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <wolv/utils/string.hpp>
#include <imgui.h>
#include <fmt/format.h>
#include <hex/api/content_registry.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/ui/view.hpp>
namespace hex {
@@ -15,6 +17,8 @@ namespace hex {
std::optional<std::string> s_layoutStringToLoad;
std::vector<LayoutManager::Layout> s_layouts;
bool s_layoutLocked = false;
}
@@ -22,7 +26,7 @@ namespace hex {
s_layoutPathToLoad = path;
}
void LayoutManager::loadString(const std::string &content) {
void LayoutManager::loadFromString(const std::string &content) {
s_layoutStringToLoad = content;
}
@@ -32,25 +36,57 @@ namespace hex {
std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower);
fileName += ".hexlyt";
const auto path = hex::fs::getDefaultPaths(fs::ImHexPath::Layouts).front() / fileName;
ImGui::SaveIniSettingsToDisk(wolv::util::toUTF8String(path).c_str());
std::fs::path layoutPath;
for (const auto &path : hex::fs::getDefaultPaths(fs::ImHexPath::Layouts)) {
if (!hex::fs::isPathWritable(layoutPath))
continue;
layoutPath = path / fileName;
}
if (layoutPath.empty()) {
log::error("Failed to save layout '{}'. No writable path found", name);
return;
}
const auto pathString = wolv::util::toUTF8String(layoutPath);
ImGui::SaveIniSettingsToDisk(pathString.c_str());
log::info("Layout '{}' saved to {}", name, pathString);
LayoutManager::reload();
}
std::string LayoutManager::saveToString() {
return ImGui::SaveIniSettingsToMemory();
}
std::vector<LayoutManager::Layout> LayoutManager::getLayouts() {
return s_layouts;
}
void LayoutManager::closeAllViews() {
for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries())
view->getWindowOpenState() = false;
}
void LayoutManager::process() {
if (s_layoutPathToLoad.has_value()) {
ImGui::LoadIniSettingsFromDisk(wolv::util::toUTF8String(*s_layoutPathToLoad).c_str());
const auto pathString = wolv::util::toUTF8String(*s_layoutPathToLoad);
LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromDisk(pathString.c_str());
s_layoutPathToLoad = std::nullopt;
log::info("Loaded layout from {}", pathString);
}
if (s_layoutStringToLoad.has_value()) {
LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromMemory(s_layoutStringToLoad->c_str());
s_layoutStringToLoad = std::nullopt;
log::info("Loaded layout from string");
}
}
@@ -85,4 +121,13 @@ namespace hex {
s_layouts.clear();
}
}
bool LayoutManager::isLayoutLocked() {
return s_layoutLocked;
}
void LayoutManager::lockLayout(bool locked) {
log::info("Layout {}", locked ? "locked" : "unlocked");
s_layoutLocked = locked;
}
}

View File

@@ -31,13 +31,13 @@ namespace hex {
if (value.empty())
continue;
this->m_entries.insert({ key, value });
m_entries.insert({ key, value });
}
}
const std::map<std::string, std::string> &LanguageDefinition::getEntries() const {
return this->m_entries;
return m_entries;
}
void loadLanguage(const std::string &language) {
@@ -75,7 +75,8 @@ namespace hex {
}
Lang::Lang(const char *unlocalizedString) : m_unlocalizedString(unlocalizedString) { }
Lang::Lang(std::string unlocalizedString) : m_unlocalizedString(std::move(unlocalizedString)) { }
Lang::Lang(const std::string &unlocalizedString) : m_unlocalizedString(unlocalizedString) { }
Lang::Lang(const UnlocalizedString &unlocalizedString) : m_unlocalizedString(unlocalizedString.get()) { }
Lang::Lang(std::string_view unlocalizedString) : m_unlocalizedString(unlocalizedString) { }
Lang::operator std::string() const {
@@ -120,10 +121,10 @@ namespace hex {
const std::string &Lang::get() const {
auto &lang = LocalizationManager::s_currStrings;
if (lang.contains(this->m_unlocalizedString))
return lang[this->m_unlocalizedString];
if (lang.contains(m_unlocalizedString))
return lang[m_unlocalizedString];
else
return this->m_unlocalizedString;
return m_unlocalizedString;
}
}

View File

@@ -17,76 +17,94 @@
namespace hex {
Plugin::Plugin(const std::fs::path &path) : m_path(path) {
log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename()));
#if defined(OS_WINDOWS)
this->m_handle = uintptr_t(LoadLibraryW(path.c_str()));
m_handle = uintptr_t(LoadLibraryW(path.c_str()));
if (this->m_handle == uintptr_t(INVALID_HANDLE_VALUE) || this->m_handle == 0) {
log::error("LoadLibraryW failed: {}!", std::system_category().message(::GetLastError()));
if (m_handle == uintptr_t(INVALID_HANDLE_VALUE) || m_handle == 0) {
log::error("Loading plugin '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), std::system_category().message(::GetLastError()));
return;
}
#else
this->m_handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
m_handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
if (this->m_handle == 0) {
log::error("dlopen failed: {}!", dlerror());
if (m_handle == 0) {
log::error("Loading plugin '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
return;
}
#endif
this->m_functions.initializePluginFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializePlugin");
this->m_functions.getPluginNameFunction = getPluginFunction<PluginFunctions::GetPluginNameFunc>("getPluginName");
this->m_functions.getPluginAuthorFunction = getPluginFunction<PluginFunctions::GetPluginAuthorFunc>("getPluginAuthor");
this->m_functions.getPluginDescriptionFunction = getPluginFunction<PluginFunctions::GetPluginDescriptionFunc>("getPluginDescription");
this->m_functions.getCompatibleVersionFunction = getPluginFunction<PluginFunctions::GetCompatibleVersionFunc>("getCompatibleVersion");
this->m_functions.setImGuiContextFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>("setImGuiContext");
this->m_functions.isBuiltinPluginFunction = getPluginFunction<PluginFunctions::IsBuiltinPluginFunc>("isBuiltinPlugin");
this->m_functions.getSubCommandsFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getSubCommands");
log::info("Loaded plugin '{}'", wolv::util::toUTF8String(path.filename()));
m_functions.initializePluginFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializePlugin");
m_functions.initializeLibraryFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializeLibrary");
m_functions.getPluginNameFunction = getPluginFunction<PluginFunctions::GetPluginNameFunc>("getPluginName");
m_functions.getPluginAuthorFunction = getPluginFunction<PluginFunctions::GetPluginAuthorFunc>("getPluginAuthor");
m_functions.getPluginDescriptionFunction = getPluginFunction<PluginFunctions::GetPluginDescriptionFunc>("getPluginDescription");
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(hex::PluginFunctions functions) {
this->m_handle = 0;
this->m_functions = functions;
Plugin::Plugin(const hex::PluginFunctions &functions) {
m_handle = 0;
m_functions = functions;
}
Plugin::Plugin(Plugin &&other) noexcept {
this->m_handle = other.m_handle;
m_handle = other.m_handle;
other.m_handle = 0;
this->m_path = std::move(other.m_path);
m_path = std::move(other.m_path);
this->m_functions = other.m_functions;
m_functions = other.m_functions;
other.m_functions = {};
}
Plugin& Plugin::operator=(Plugin &&other) noexcept {
m_handle = other.m_handle;
other.m_handle = 0;
m_path = std::move(other.m_path);
m_functions = other.m_functions;
other.m_functions = {};
return *this;
}
Plugin::~Plugin() {
#if defined(OS_WINDOWS)
if (this->m_handle != 0)
FreeLibrary(HMODULE(this->m_handle));
if (m_handle != 0)
FreeLibrary(HMODULE(m_handle));
#else
dlclose(reinterpret_cast<void*>(this->m_handle));
if (m_handle != 0)
dlclose(reinterpret_cast<void*>(m_handle));
#endif
}
bool Plugin::initializePlugin() const {
const auto pluginName = wolv::util::toUTF8String(this->m_path.filename());
const auto pluginName = wolv::util::toUTF8String(m_path.filename());
if (this->isLibraryPlugin()) {
return true;
}
const auto requestedVersion = getCompatibleVersion();
if (requestedVersion != ImHexApi::System::getImHexVersion()) {
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(this->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 {
log::error("Refused to load plugin '{}' which was built for a different version of ImHex: '{}'", wolv::util::toUTF8String(this->m_path.filename()), requestedVersion);
log::error("Refused to load plugin '{}' which was built for a different version of ImHex: '{}'", wolv::util::toUTF8String(m_path.filename()), requestedVersion);
return false;
}
}
if (this->m_functions.initializePluginFunction != nullptr) {
if (m_functions.initializePluginFunction != nullptr) {
try {
this->m_functions.initializePluginFunction();
m_functions.initializePluginFunction();
} catch (const std::exception &e) {
log::error("Plugin '{}' threw an exception on init: {}", pluginName, e.what());
return false;
@@ -101,72 +119,97 @@ namespace hex {
log::info("Plugin '{}' initialized successfully", pluginName);
this->m_initialized = true;
m_initialized = true;
return true;
}
std::string Plugin::getPluginName() const {
if (this->m_functions.getPluginNameFunction != nullptr)
return this->m_functions.getPluginNameFunction();
else
return hex::format("Unknown Plugin @ 0x{0:016X}", this->m_handle);
if (m_functions.getPluginNameFunction != nullptr) {
return m_functions.getPluginNameFunction();
} else {
if (this->isLibraryPlugin())
return "Library Plugin";
else
return hex::format("Unknown Plugin @ 0x{0:016X}", m_handle);
}
}
std::string Plugin::getPluginAuthor() const {
if (this->m_functions.getPluginAuthorFunction != nullptr)
return this->m_functions.getPluginAuthorFunction();
if (m_functions.getPluginAuthorFunction != nullptr)
return m_functions.getPluginAuthorFunction();
else
return "Unknown";
}
std::string Plugin::getPluginDescription() const {
if (this->m_functions.getPluginDescriptionFunction != nullptr)
return this->m_functions.getPluginDescriptionFunction();
if (m_functions.getPluginDescriptionFunction != nullptr)
return m_functions.getPluginDescriptionFunction();
else
return "";
}
std::string Plugin::getCompatibleVersion() const {
if (this->m_functions.getCompatibleVersionFunction != nullptr)
return this->m_functions.getCompatibleVersionFunction();
if (m_functions.getCompatibleVersionFunction != nullptr)
return m_functions.getCompatibleVersionFunction();
else
return "";
}
void Plugin::setImGuiContext(ImGuiContext *ctx) const {
if (this->m_functions.setImGuiContextFunction != nullptr)
this->m_functions.setImGuiContextFunction(ctx);
if (m_functions.setImGuiContextFunction != nullptr)
m_functions.setImGuiContextFunction(ctx);
}
[[nodiscard]] bool Plugin::isBuiltinPlugin() const {
if (this->m_functions.isBuiltinPluginFunction != nullptr)
return this->m_functions.isBuiltinPluginFunction();
if (m_functions.isBuiltinPluginFunction != nullptr)
return m_functions.isBuiltinPluginFunction();
else
return false;
}
const std::fs::path &Plugin::getPath() const {
return this->m_path;
return m_path;
}
bool Plugin::isValid() const {
return m_handle != 0 || m_functions.initializeLibraryFunction != nullptr || m_functions.initializePluginFunction != nullptr;
}
bool Plugin::isLoaded() const {
return this->m_initialized;
return m_initialized;
}
std::span<SubCommand> Plugin::getSubCommands() const {
if (this->m_functions.getSubCommandsFunction != nullptr) {
auto result = this->m_functions.getSubCommandsFunction();
if (m_functions.getSubCommandsFunction != nullptr) {
auto result = m_functions.getSubCommandsFunction();
return *static_cast<std::vector<SubCommand>*>(result);
} else
} else {
return { };
}
}
std::span<Feature> Plugin::getFeatures() const {
if (m_functions.getFeaturesFunction != nullptr) {
auto result = m_functions.getFeaturesFunction();
return *static_cast<std::vector<Feature>*>(result);
} else {
return { };
}
}
bool Plugin::isLibraryPlugin() const {
return m_functions.initializeLibraryFunction != nullptr &&
m_functions.initializePluginFunction == nullptr;
}
void *Plugin::getPluginFunction(const std::string &symbol) const {
#if defined(OS_WINDOWS)
return reinterpret_cast<void *>(GetProcAddress(HMODULE(this->m_handle), symbol.c_str()));
return reinterpret_cast<void *>(GetProcAddress(HMODULE(m_handle), symbol.c_str()));
#else
return dlsym(reinterpret_cast<void*>(this->m_handle), symbol.c_str());
return dlsym(reinterpret_cast<void*>(m_handle), symbol.c_str());
#endif
}
@@ -176,11 +219,22 @@ namespace hex {
getPluginPaths().push_back(pluginFolder);
// Load library plugins first
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexpluglib")
getPlugins().emplace_back(pluginPath.path());
}
// Load regular plugins afterwards
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug")
getPlugins().emplace_back(pluginPath.path());
}
std::erase_if(getPlugins(), [](const Plugin &plugin) {
return !plugin.isValid();
});
if (getPlugins().empty())
return false;

View File

@@ -15,11 +15,11 @@ namespace hex {
}
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::string &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 } });
}
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::string &unlocalizedName, const std::function<void()> &callback) {
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) {
view->m_shortcuts.insert({ shortcut + CurrentView, { shortcut, unlocalizedName, callback } });
}
@@ -98,7 +98,7 @@ namespace hex {
return result;
}
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(View *view) {
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(const View *view) {
std::vector<ShortcutManager::ShortcutEntry> result;
for (auto &[shortcut, entry] : view->m_shortcuts)

View File

@@ -5,6 +5,8 @@
#include <algorithm>
#include <jthread.hpp>
#if defined(OS_WINDOWS)
#include <windows.h>
#include <processthreadsapi.h>
@@ -55,25 +57,25 @@ namespace hex {
#endif
}
Task::Task(std::string 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) { }
Task::Task(hex::Task &&other) noexcept {
{
std::scoped_lock thisLock(this->m_mutex);
std::scoped_lock thisLock(m_mutex);
std::scoped_lock otherLock(other.m_mutex);
this->m_function = std::move(other.m_function);
this->m_unlocalizedName = std::move(other.m_unlocalizedName);
m_function = std::move(other.m_function);
m_unlocalizedName = std::move(other.m_unlocalizedName);
}
this->m_maxValue = u64(other.m_maxValue);
this->m_currValue = u64(other.m_currValue);
m_maxValue = u64(other.m_maxValue);
m_currValue = u64(other.m_currValue);
this->m_finished = bool(other.m_finished);
this->m_hadException = bool(other.m_hadException);
this->m_interrupted = bool(other.m_interrupted);
this->m_shouldInterrupt = bool(other.m_shouldInterrupt);
m_finished = bool(other.m_finished);
m_hadException = bool(other.m_hadException);
m_interrupted = bool(other.m_interrupted);
m_shouldInterrupt = bool(other.m_shouldInterrupt);
}
Task::~Task() {
@@ -82,131 +84,141 @@ namespace hex {
}
void Task::update(u64 value) {
this->m_currValue.store(value, std::memory_order_relaxed);
// Update the current progress value of the task
m_currValue.store(value, std::memory_order_relaxed);
if (this->m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]
// Check if the task has been interrupted by the main thread and if yes,
// throw an exception that is generally not caught by the task
if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]
throw TaskInterruptor();
}
void Task::setMaxValue(u64 value) {
this->m_maxValue = value;
m_maxValue = value;
}
void Task::interrupt() {
this->m_shouldInterrupt = true;
m_shouldInterrupt = true;
if (this->m_interruptCallback)
this->m_interruptCallback();
// Call the interrupt callback on the current thread if one is set
if (m_interruptCallback)
m_interruptCallback();
}
void Task::setInterruptCallback(std::function<void()> callback) {
this->m_interruptCallback = std::move(callback);
m_interruptCallback = std::move(callback);
}
bool Task::isBackgroundTask() const {
return this->m_background;
return m_background;
}
bool Task::isFinished() const {
return this->m_finished;
return m_finished;
}
bool Task::hadException() const {
return this->m_hadException;
return m_hadException;
}
bool Task::shouldInterrupt() const {
return this->m_shouldInterrupt;
return m_shouldInterrupt;
}
bool Task::wasInterrupted() const {
return this->m_interrupted;
return m_interrupted;
}
void Task::clearException() {
this->m_hadException = false;
m_hadException = false;
}
std::string Task::getExceptionMessage() const {
std::scoped_lock lock(this->m_mutex);
std::scoped_lock lock(m_mutex);
return this->m_exceptionMessage;
return m_exceptionMessage;
}
const std::string &Task::getUnlocalizedName() {
return this->m_unlocalizedName;
const UnlocalizedString &Task::getUnlocalizedName() {
return m_unlocalizedName;
}
u64 Task::getValue() const {
return this->m_currValue;
return m_currValue;
}
u64 Task::getMaxValue() const {
return this->m_maxValue;
return m_maxValue;
}
void Task::finish() {
this->m_finished = true;
m_finished = true;
}
void Task::interruption() {
this->m_interrupted = true;
m_interrupted = true;
}
void Task::exception(const char *message) {
std::scoped_lock lock(this->m_mutex);
std::scoped_lock lock(m_mutex);
this->m_exceptionMessage = message;
this->m_hadException = true;
// Store information about the caught exception
m_exceptionMessage = message;
m_hadException = true;
}
bool TaskHolder::isRunning() const {
if (this->m_task.expired())
auto task = m_task.lock();
if (!task)
return false;
auto task = this->m_task.lock();
return !task->isFinished();
}
bool TaskHolder::hadException() const {
if (this->m_task.expired())
auto task = m_task.lock();
if (!task)
return false;
auto task = this->m_task.lock();
return !task->hadException();
}
bool TaskHolder::shouldInterrupt() const {
if (this->m_task.expired())
auto task = m_task.lock();
if (!task)
return false;
auto task = this->m_task.lock();
return !task->shouldInterrupt();
}
bool TaskHolder::wasInterrupted() const {
if (this->m_task.expired())
auto task = m_task.lock();
if (!task)
return false;
auto task = this->m_task.lock();
return !task->wasInterrupted();
}
void TaskHolder::interrupt() const {
if (this->m_task.expired())
auto task = m_task.lock();
if (!task)
return;
auto task = this->m_task.lock();
task->interrupt();
}
u32 TaskHolder::getProgress() const {
if (this->m_task.expired())
auto task = m_task.lock();
if (!task)
return false;
// If the max value is 0, the task has no progress
if (task->getMaxValue() == 0)
return 0;
auto task = this->m_task.lock();
// Calculate the progress of the task from 0 to 100
return u32((task->getValue() * 100) / task->getMaxValue());
}
@@ -216,89 +228,120 @@ namespace hex {
log::debug("Initializing task manager thread pool with {} workers.", threadCount);
for (u32 i = 0; i < threadCount; i++)
s_workers.emplace_back(TaskManager::runner);
}
// Create worker threads
for (u32 i = 0; i < threadCount; i++) {
s_workers.emplace_back([](const std::stop_token &stopToken) {
while (true) {
std::shared_ptr<Task> task;
void TaskManager::exit() {
for (auto &task : s_tasks)
task->interrupt();
// Set the thread name to "Idle Task" while waiting for a task
setThreadName("Idle Task");
for (auto &thread : s_workers)
thread.request_stop();
{
// Wait for a task to be added to the queue
std::unique_lock lock(s_queueMutex);
s_jobCondVar.wait(lock, [&] {
return !s_taskQueue.empty() || stopToken.stop_requested();
});
s_jobCondVar.notify_all();
// Check if the thread should exit
if (stopToken.stop_requested())
break;
s_workers.clear();
}
// Grab the next task from the queue
task = std::move(s_taskQueue.front());
s_taskQueue.pop_front();
}
void TaskManager::runner(const std::stop_token &stopToken) {
while (true) {
std::shared_ptr<Task> task;
{
std::unique_lock lock(s_queueMutex);
s_jobCondVar.wait(lock, [&] {
return !s_taskQueue.empty() || stopToken.stop_requested();
});
if (stopToken.stop_requested())
break;
try {
// Set the thread name to the name of the task
setThreadName(Lang(task->m_unlocalizedName));
task = std::move(s_taskQueue.front());
s_taskQueue.pop_front();
}
// Execute the task
task->m_function(*task);
try {
setThreadName(Lang(task->m_unlocalizedName));
task->m_function(*task);
setThreadName("Idle Task");
log::debug("Finished task {}", task->m_unlocalizedName);
} catch (const Task::TaskInterruptor &) {
task->interruption();
} catch (const std::exception &e) {
log::error("Exception in task {}: {}", task->m_unlocalizedName, e.what());
task->exception(e.what());
} catch (...) {
log::error("Exception in task {}", task->m_unlocalizedName);
task->exception("Unknown Exception");
}
log::debug("Task '{}' finished", task->m_unlocalizedName.get());
} catch (const Task::TaskInterruptor &) {
// Handle the task being interrupted by user request
task->interruption();
} catch (const std::exception &e) {
log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what());
task->finish();
// Handle the task throwing an uncaught exception
task->exception(e.what());
} catch (...) {
log::error("Exception in task '{}'", task->m_unlocalizedName.get());
// Handle the task throwing an uncaught exception of unknown type
task->exception("Unknown Exception");
}
task->finish();
}
});
}
}
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
log::debug("Creating task {}", name);
std::unique_lock lock(s_queueMutex);
auto task = std::make_shared<Task>(std::move(name), maxValue, false, std::move(function));
void TaskManager::exit() {
// Interrupt all tasks
for (auto &task : s_tasks) {
task->interrupt();
}
// Ask worker threads to exit after finishing their task
for (auto &thread : s_workers)
thread.request_stop();
// Wake up all the idle worker threads so they can exit
s_jobCondVar.notify_all();
// Wait for all worker threads to exit
s_workers.clear();
s_tasks.clear();
s_taskQueue.clear();
s_deferredCalls.clear();
s_tasksFinishedCallbacks.clear();
}
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, bool background, std::function<void(Task&)> function) {
std::scoped_lock lock(s_queueMutex);
// Construct new task
auto task = std::make_shared<Task>(std::move(name), maxValue, background, std::move(function));
s_tasks.emplace_back(task);
s_taskQueue.emplace_back(task);
// Add task to the queue for the worker to pick up
s_taskQueue.emplace_back(std::move(task));
s_jobCondVar.notify_one();
return TaskHolder(s_tasks.back());
}
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
log::debug("Creating task {}", name);
return createTask(std::move(name), maxValue, false, std::move(function));
}
TaskHolder TaskManager::createBackgroundTask(std::string name, std::function<void(Task &)> function) {
log::debug("Creating background task {}", name);
std::unique_lock lock(s_queueMutex);
auto task = std::make_shared<Task>(std::move(name), 0, true, std::move(function));
s_tasks.emplace_back(task);
s_taskQueue.emplace_back(task);
s_jobCondVar.notify_one();
return TaskHolder(s_tasks.back());
return createTask(std::move(name), 0, true, std::move(function));
}
void TaskManager::collectGarbage() {
{
std::unique_lock lock1(s_queueMutex);
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
std::scoped_lock lock(s_queueMutex);
std::erase_if(s_tasks, [](const auto &task) {
return task->isFinished() && !task->hadException();
});
}
if (s_tasks.empty()) {
std::unique_lock lock2(s_deferredCallsMutex);
std::scoped_lock lock(s_deferredCallsMutex);
for (auto &call : s_tasksFinishedCallbacks)
call();
s_tasksFinishedCallbacks.clear();
@@ -311,7 +354,7 @@ namespace hex {
}
size_t TaskManager::getRunningTaskCount() {
std::unique_lock lock(s_queueMutex);
std::scoped_lock lock(s_queueMutex);
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){
return !task->isBackgroundTask();
@@ -319,7 +362,7 @@ namespace hex {
}
size_t TaskManager::getRunningBackgroundTaskCount() {
std::unique_lock lock(s_queueMutex);
std::scoped_lock lock(s_queueMutex);
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){
return task->isBackgroundTask();

View File

@@ -90,9 +90,9 @@ namespace hex {
theme["styles"][type] = {};
for (const auto &[key, style] : handler.styleMap) {
if (std::holds_alternative<float*>(style.value))
if (std::holds_alternative<float*>(style.value)) {
theme["styles"][type][key] = *std::get<float*>(style.value);
else if (std::holds_alternative<ImVec2*>(style.value)) {
} else if (std::holds_alternative<ImVec2*>(style.value)) {
theme["styles"][type][key] = {
std::get<ImVec2*>(style.value)->x,
std::get<ImVec2*>(style.value)->y
@@ -194,7 +194,7 @@ namespace hex {
s_currTheme = name;
EventManager::post<EventThemeChanged>();
EventThemeChanged::post();
}
const std::string &ThemeManager::getImageTheme() {

View File

@@ -0,0 +1,379 @@
#include <hex/api/tutorial_manager.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <imgui_internal.h>
#include <hex/helpers/utils.hpp>
#include <wolv/utils/core.hpp>
#include <map>
namespace hex {
namespace {
std::map<std::string, TutorialManager::Tutorial> s_tutorials;
decltype(s_tutorials)::iterator s_currentTutorial = s_tutorials.end();
std::map<ImGuiID, std::string> s_highlights;
std::vector<std::pair<ImRect, std::string>> s_highlightDisplays;
class IDStack {
public:
IDStack() {
idStack.push_back(0);
}
void add(const std::string &string) {
const ImGuiID seed = idStack.back();
const ImGuiID id = ImHashStr(string.c_str(), string.length(), seed);
idStack.push_back(id);
}
void add(const void *pointer) {
const ImGuiID seed = idStack.back();
const ImGuiID id = ImHashData(&pointer, sizeof(pointer), seed);
idStack.push_back(id);
}
void add(int value) {
const ImGuiID seed = idStack.back();
const ImGuiID id = ImHashData(&value, sizeof(value), seed);
idStack.push_back(id);
}
ImGuiID get() {
return idStack.back();
}
private:
ImVector<ImGuiID> idStack;
};
}
const std::map<std::string, TutorialManager::Tutorial>& TutorialManager::getTutorials() {
return s_tutorials;
}
std::map<std::string, TutorialManager::Tutorial>::iterator TutorialManager::getCurrentTutorial() {
return s_currentTutorial;
}
TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) {
return s_tutorials.try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second;
}
void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) {
s_currentTutorial = s_tutorials.find(unlocalizedName);
if (s_currentTutorial == s_tutorials.end())
return;
s_currentTutorial->second.start();
}
void TutorialManager::drawHighlights() {
for (const auto &[rect, unlocalizedText] : s_highlightDisplays) {
const auto drawList = ImGui::GetForegroundDrawList();
drawList->PushClipRectFullScreen();
{
auto highlightColor = ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight);
highlightColor.w *= ImSin(ImGui::GetTime() * 6.0F) / 4.0F + 0.75F;
drawList->AddRect(rect.Min - ImVec2(5, 5), rect.Max + ImVec2(5, 5), ImColor(highlightColor), 5.0F, ImDrawFlags_None, 2.0F);
}
{
if (!unlocalizedText.empty()) {
const auto mainWindowPos = ImHexApi::System::getMainWindowPosition();
const auto mainWindowSize = ImHexApi::System::getMainWindowSize();
const auto margin = ImGui::GetStyle().WindowPadding;
ImVec2 windowPos = { rect.Min.x + 20_scaled, rect.Max.y + 10_scaled };
ImVec2 windowSize = { std::max<float>(rect.Max.x - rect.Min.x - 40_scaled, 300_scaled), 0 };
const char* text = Lang(unlocalizedText);
const auto textSize = ImGui::CalcTextSize(text, nullptr, false, windowSize.x - margin.x * 2);
windowSize.y = textSize.y + margin.y * 2;
if (windowPos.y + windowSize.y > mainWindowPos.y + mainWindowSize.y)
windowPos.y = rect.Min.y - windowSize.y - 10_scaled;
if (windowPos.y < mainWindowPos.y)
windowPos.y = mainWindowPos.y + 10_scaled;
auto &style = ImGui::GetStyle();
ImVec2 shadowOffset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist;
drawList->AddRectFilled(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowBg) | 0xFF000000);
drawList->AddRect(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_Border));
drawList->AddShadowRect(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowShadow), style.WindowShadowSize, shadowOffset, ImDrawFlags_ShadowCutOutShapeBackground);
drawList->AddText(nullptr, 0.0F, windowPos + margin, ImGui::GetColorU32(ImGuiCol_Text), text, nullptr, windowSize.x - margin.x * 2);
}
}
drawList->PopClipRect();
}
s_highlightDisplays.clear();
}
void TutorialManager::drawMessageBox(std::optional<Tutorial::Step::Message> message) {
const auto windowStart = ImHexApi::System::getMainWindowPosition() + scaled({ 10, 10 });
const auto windowEnd = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize() - scaled({ 10, 10 });
ImVec2 position = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize() / 2.0F;
ImVec2 pivot = { 0.5F, 0.5F };
if (!message.has_value()) {
message = Tutorial::Step::Message {
Position::None,
"",
"",
false
};
}
if (message->position == Position::None) {
message->position = Position::Bottom | Position::Right;
}
if ((message->position & Position::Top) == Position::Top) {
position.y = windowStart.y;
pivot.y = 0.0F;
}
if ((message->position & Position::Bottom) == Position::Bottom) {
position.y = windowEnd.y;
pivot.y = 1.0F;
}
if ((message->position & Position::Left) == Position::Left) {
position.x = windowStart.x;
pivot.x = 0.0F;
}
if ((message->position & Position::Right) == Position::Right) {
position.x = windowEnd.x;
pivot.x = 1.0F;
}
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
if (ImGui::Begin("##TutorialMessage", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
if (!message->unlocalizedTitle.empty())
ImGuiExt::Header(Lang(message->unlocalizedTitle), true);
if (!message->unlocalizedMessage.empty()) {
ImGui::PushTextWrapPos(300_scaled);
ImGui::TextUnformatted(Lang(message->unlocalizedMessage));
ImGui::PopTextWrapPos();
ImGui::NewLine();
}
ImGui::BeginDisabled(s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_steps.begin());
if (ImGui::ArrowButton("Backwards", ImGuiDir_Left)) {
s_currentTutorial->second.m_currentStep->advance(-1);
}
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::BeginDisabled(!message->allowSkip && s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_latestStep);
if (ImGui::ArrowButton("Forwards", ImGuiDir_Right)) {
s_currentTutorial->second.m_currentStep->advance(1);
}
ImGui::EndDisabled();
}
ImGui::End();
}
void TutorialManager::drawTutorial() {
drawHighlights();
if (s_currentTutorial == s_tutorials.end())
return;
const auto &currentStep = s_currentTutorial->second.m_currentStep;
if (currentStep == s_currentTutorial->second.m_steps.end())
return;
const auto &message = currentStep->m_message;
drawMessageBox(message);
}
void TutorialManager::reset() {
s_tutorials.clear();
s_currentTutorial = s_tutorials.end();
s_highlights.clear();
s_highlightDisplays.clear();
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
auto &newStep = m_steps.emplace_back(this);
m_currentStep = m_steps.end();
m_latestStep = m_currentStep;
return newStep;
}
void TutorialManager::Tutorial::start() {
m_currentStep = m_steps.begin();
m_latestStep = m_currentStep;
if (m_currentStep == m_steps.end())
return;
m_currentStep->addHighlights();
}
void TutorialManager::Tutorial::Step::addHighlights() const {
if (m_onAppear)
m_onAppear();
for (const auto &[text, ids] : m_highlights) {
IDStack idStack;
for (const auto &id : ids) {
std::visit(wolv::util::overloaded {
[&idStack](const Lang &id) {
idStack.add(id.get());
},
[&idStack](const auto &id) {
idStack.add(id);
}
}, id);
}
s_highlights.emplace(idStack.get(), text);
}
}
void TutorialManager::Tutorial::Step::removeHighlights() const {
for (const auto &[text, ids] : m_highlights) {
IDStack idStack;
for (const auto &id : ids) {
std::visit(wolv::util::overloaded {
[&idStack](const Lang &id) {
idStack.add(id.get());
},
[&idStack](const auto &id) {
idStack.add(id);
}
}, id);
}
s_highlights.erase(idStack.get());
}
}
void TutorialManager::Tutorial::Step::advance(i32 steps) const {
m_parent->m_currentStep->removeHighlights();
if (m_parent->m_currentStep == m_parent->m_latestStep && steps > 0)
std::advance(m_parent->m_latestStep, steps);
std::advance(m_parent->m_currentStep, steps);
if (m_parent->m_currentStep != m_parent->m_steps.end())
m_parent->m_currentStep->addHighlights();
else
s_currentTutorial = s_tutorials.end();
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::addHighlight(const UnlocalizedString &unlocalizedText, std::initializer_list<std::variant<Lang, std::string, int>>&& ids) {
m_highlights.emplace_back(
unlocalizedText,
ids
);
return *this;
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::addHighlight(std::initializer_list<std::variant<Lang, std::string, int>>&& ids) {
return this->addHighlight("", std::forward<decltype(ids)>(ids));
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position) {
m_message = Message {
position,
unlocalizedTitle,
unlocalizedMessage,
false
};
return *this;
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::allowSkip() {
if (m_message.has_value()) {
m_message->allowSkip = true;
} else {
m_message = Message {
Position::Bottom | Position::Right,
"",
"",
true
};
}
return *this;
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onAppear(std::function<void()> callback) {
m_onAppear = std::move(callback);
return *this;
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onComplete(std::function<void()> callback) {
m_onComplete = std::move(callback);
return *this;
}
bool TutorialManager::Tutorial::Step::isCurrent() const {
const auto &currentStep = m_parent->m_currentStep;
if (currentStep == m_parent->m_steps.end())
return false;
return &*currentStep == this;
}
void TutorialManager::Tutorial::Step::complete() const {
if (this->isCurrent()) {
this->advance();
if (m_onComplete) {
TaskManager::doLater([this] {
m_onComplete();
});
}
}
}
}
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
const auto element = hex::s_highlights.find(id);
if (element != hex::s_highlights.end()) {
hex::s_highlightDisplays.emplace_back(bb, element->second);
}
}
void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}
void ImGuiTestEngineHook_Log(ImGuiContext*, const char*, ...) {}
const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext*, ImGuiID) { return nullptr; }

View File

@@ -0,0 +1,108 @@
#include <hex/api/workspace_manager.hpp>
#include <hex/api/layout_manager.hpp>
#include <hex/helpers/logger.hpp>
#include <wolv/io/file.hpp>
#include <nlohmann/json.hpp>
#include <imgui.h>
namespace hex {
std::map<std::string, WorkspaceManager::Workspace> WorkspaceManager::s_workspaces;
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_currentWorkspace = s_workspaces.end();
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_previousWorkspace = s_workspaces.end();
void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) {
s_workspaces[name] = Workspace {
.layout = layout.empty() ? LayoutManager::saveToString() : layout,
.path = {}
};
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
if (exportToFile(path / (name + ".hexws")))
break;
}
}
void WorkspaceManager::switchWorkspace(const std::string& name) {
const auto newWorkspace = s_workspaces.find(name);
if (newWorkspace != s_workspaces.end()) {
s_currentWorkspace = newWorkspace;
log::info("Switching to workspace '{}'", name);
}
}
void WorkspaceManager::importFromFile(const std::fs::path& path) {
wolv::io::File file(path, wolv::io::File::Mode::Read);
if (!file.isValid()) {
log::error("Failed to load workspace from file '{}'", path.string());
return;
}
auto content = file.readString();
try {
auto json = nlohmann::json::parse(content.begin(), content.end());
std::string name = json["name"];
std::string layout = json["layout"];
s_workspaces[name] = Workspace {
.layout = std::move(layout),
.path = path
};
} catch (nlohmann::json::exception &e) {
log::error("Failed to load workspace from file '{}': {}", path.string(), e.what());
}
}
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName) {
if (path.empty()) {
if (s_currentWorkspace == s_workspaces.end())
return false;
path = s_currentWorkspace->second.path;
}
if (workspaceName.empty())
workspaceName = s_currentWorkspace->first;
wolv::io::File file(path, wolv::io::File::Mode::Create);
if (!file.isValid())
return false;
nlohmann::json json;
json["name"] = workspaceName;
json["layout"] = LayoutManager::saveToString();
file.writeString(json.dump(4));
return true;
}
void WorkspaceManager::process() {
if (s_previousWorkspace != s_currentWorkspace) {
if (s_previousWorkspace != s_workspaces.end())
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first);
LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str());
s_previousWorkspace = s_currentWorkspace;
}
}
void WorkspaceManager::reset() {
s_workspaces.clear();
s_currentWorkspace = s_workspaces.end();
s_previousWorkspace = s_workspaces.end();
}
}

View File

@@ -6,7 +6,7 @@ namespace hex::dp {
int Attribute::s_idCounter = 1;
Attribute::Attribute(IOType ioType, Type type, std::string unlocalizedName) : m_id(s_idCounter++), m_ioType(ioType), m_type(type), m_unlocalizedName(std::move(unlocalizedName)) {
Attribute::Attribute(IOType ioType, Type type, UnlocalizedString unlocalizedName) : m_id(s_idCounter++), m_ioType(ioType), m_type(type), m_unlocalizedName(std::move(unlocalizedName)) {
}
Attribute::~Attribute() {

View File

@@ -9,8 +9,8 @@ namespace hex::dp {
int Node::s_idCounter = 1;
Node::Node(std::string unlocalizedTitle, std::vector<Attribute> attributes) : m_id(s_idCounter++), m_unlocalizedTitle(std::move(unlocalizedTitle)), m_attributes(std::move(attributes)) {
for (auto &attr : this->m_attributes)
Node::Node(UnlocalizedString unlocalizedTitle, std::vector<Attribute> attributes) : m_id(s_idCounter++), m_unlocalizedTitle(std::move(unlocalizedTitle)), m_attributes(std::move(attributes)) {
for (auto &attr : m_attributes)
attr.setParentNode(this);
}
@@ -18,7 +18,7 @@ namespace hex::dp {
auto attribute = this->getConnectedInputAttribute(index);
if (attribute == nullptr)
throwNodeError(hex::format("Nothing connected to input '{0}'", Lang(this->m_attributes[index].getUnlocalizedName())));
throwNodeError(hex::format("Nothing connected to input '{0}'", Lang(m_attributes[index].getUnlocalizedName())));
if (attribute->getType() != Attribute::Type::Buffer)
throwNodeError("Tried to read buffer from non-buffer attribute");
@@ -141,11 +141,11 @@ namespace hex::dp {
}
void Node::setOverlayData(u64 address, const std::vector<u8> &data) {
if (this->m_overlay == nullptr)
if (m_overlay == nullptr)
throwNodeError("Tried setting overlay data on a node that's not the end of a chain!");
this->m_overlay->setAddress(address);
this->m_overlay->getData() = data;
m_overlay->setAddress(address);
m_overlay->getData() = data;
}
void Node::setIdCounter(int id) {

View File

@@ -110,27 +110,27 @@ namespace hex::crypt {
}
constexpr void reset() {
this->m_value = reflect(m_init, NumBits);
m_value = reflect(m_init, NumBits);
}
constexpr void processBytes(const unsigned char *data, std::size_t size) {
for (std::size_t i = 0; i < size; i++) {
u8 byte;
if (this->m_reflectInput)
if (m_reflectInput)
byte = data[i];
else
byte = reflect(data[i]);
this->m_value = this->m_table[(this->m_value ^ byte) & 0xFFL] ^ (this->m_value >> 8);
m_value = m_table[(m_value ^ byte) & 0xFFL] ^ (m_value >> 8);
}
}
[[nodiscard]]
constexpr u64 checksum() const {
if (this->m_reflectOutput)
return this->m_value ^ m_xorOut;
if (m_reflectOutput)
return m_value ^ m_xorOut;
else
return reflect(this->m_value, NumBits) ^ m_xorOut;
return reflect(m_value, NumBits) ^ m_xorOut;
}
private:

View File

@@ -12,19 +12,19 @@ namespace hex {
}
EncodingFile::EncodingFile(const hex::EncodingFile &other) {
this->m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
this->m_tableContent = other.m_tableContent;
this->m_longestSequence = other.m_longestSequence;
this->m_valid = other.m_valid;
this->m_name = other.m_name;
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_longestSequence = other.m_longestSequence;
m_valid = other.m_valid;
m_name = other.m_name;
}
EncodingFile::EncodingFile(EncodingFile &&other) noexcept {
this->m_mapping = std::move(other.m_mapping);
this->m_tableContent = std::move(other.m_tableContent);
this->m_longestSequence = other.m_longestSequence;
this->m_valid = other.m_valid;
this->m_name = std::move(other.m_name);
m_mapping = std::move(other.m_mapping);
m_tableContent = std::move(other.m_tableContent);
m_longestSequence = other.m_longestSequence;
m_valid = other.m_valid;
m_name = std::move(other.m_name);
}
EncodingFile::EncodingFile(Type type, const std::fs::path &path) : EncodingFile() {
@@ -38,14 +38,14 @@ namespace hex {
}
{
this->m_name = path.stem().string();
this->m_name = wolv::util::replaceStrings(this->m_name, "_", " ");
m_name = path.stem().string();
m_name = wolv::util::replaceStrings(m_name, "_", " ");
if (!this->m_name.empty())
this->m_name[0] = std::toupper(this->m_name[0]);
if (!m_name.empty())
m_name[0] = std::toupper(m_name[0]);
}
this->m_valid = true;
m_valid = true;
}
EncodingFile::EncodingFile(Type type, const std::string &content) : EncodingFile() {
@@ -57,27 +57,27 @@ namespace hex {
return;
}
this->m_name = "Unknown";
this->m_valid = true;
m_name = "Unknown";
m_valid = true;
}
EncodingFile &EncodingFile::operator=(const hex::EncodingFile &other) {
this->m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
this->m_tableContent = other.m_tableContent;
this->m_longestSequence = other.m_longestSequence;
this->m_valid = other.m_valid;
this->m_name = other.m_name;
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_longestSequence = other.m_longestSequence;
m_valid = other.m_valid;
m_name = other.m_name;
return *this;
}
EncodingFile &EncodingFile::operator=(EncodingFile &&other) noexcept {
this->m_mapping = std::move(other.m_mapping);
this->m_tableContent = std::move(other.m_tableContent);
this->m_longestSequence = other.m_longestSequence;
this->m_valid = other.m_valid;
this->m_name = std::move(other.m_name);
m_mapping = std::move(other.m_mapping);
m_tableContent = std::move(other.m_tableContent);
m_longestSequence = other.m_longestSequence;
m_valid = other.m_valid;
m_name = std::move(other.m_name);
return *this;
}
@@ -85,7 +85,7 @@ namespace hex {
std::pair<std::string_view, size_t> EncodingFile::getEncodingFor(std::span<u8> buffer) const {
for (auto riter = this->m_mapping->crbegin(); riter != this->m_mapping->crend(); ++riter) {
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
const auto &[size, mapping] = *riter;
if (size > buffer.size()) continue;
@@ -99,7 +99,7 @@ namespace hex {
}
size_t EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
for (auto riter = this->m_mapping->crbegin(); riter != this->m_mapping->crend(); ++riter) {
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
const auto &[size, mapping] = *riter;
if (size > buffer.size()) continue;
@@ -113,8 +113,8 @@ namespace hex {
}
void EncodingFile::parse(const std::string &content) {
this->m_tableContent = content;
for (const auto &line : splitString(this->m_tableContent, "\n")) {
m_tableContent = content;
for (const auto &line : splitString(m_tableContent, "\n")) {
std::string from, to;
{
@@ -137,13 +137,13 @@ namespace hex {
if (to.empty())
to = " ";
if (!this->m_mapping->contains(fromBytes.size()))
this->m_mapping->insert({ fromBytes.size(), {} });
if (!m_mapping->contains(fromBytes.size()))
m_mapping->insert({ fromBytes.size(), {} });
auto keySize = fromBytes.size();
(*this->m_mapping)[keySize].insert({ std::move(fromBytes), to });
(*m_mapping)[keySize].insert({ std::move(fromBytes), to });
this->m_longestSequence = std::max(this->m_longestSequence, keySize);
m_longestSequence = std::max(m_longestSequence, keySize);
}
}

View File

@@ -375,6 +375,9 @@ namespace hex::fs {
case ImHexPath::Config:
result = appendPath(getConfigPaths(), "config");
break;
case ImHexPath::Backups:
result = appendPath(getDataPaths(), "backups");
break;
case ImHexPath::Encodings:
result = appendPath(getDataPaths(), "encodings");
break;
@@ -420,6 +423,9 @@ namespace hex::fs {
case ImHexPath::Layouts:
result = appendPath(getDataPaths(), "layouts");
break;
case ImHexPath::Workspaces:
result = appendPath(getDataPaths(), "workspaces");
break;
}
// Remove all paths that don't exist if requested

View File

@@ -5,25 +5,25 @@
namespace hex {
HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) {
emscripten_fetch_attr_init(&this->m_attr);
emscripten_fetch_attr_init(&m_attr);
}
HttpRequest::HttpRequest(HttpRequest &&other) noexcept {
this->m_attr = other.m_attr;
m_attr = other.m_attr;
this->m_method = std::move(other.m_method);
this->m_url = std::move(other.m_url);
this->m_headers = std::move(other.m_headers);
this->m_body = std::move(other.m_body);
m_method = std::move(other.m_method);
m_url = std::move(other.m_url);
m_headers = std::move(other.m_headers);
m_body = std::move(other.m_body);
}
HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept {
this->m_attr = other.m_attr;
m_attr = other.m_attr;
this->m_method = std::move(other.m_method);
this->m_url = std::move(other.m_url);
this->m_headers = std::move(other.m_headers);
this->m_body = std::move(other.m_body);
m_method = std::move(other.m_method);
m_url = std::move(other.m_url);
m_headers = std::move(other.m_headers);
m_body = std::move(other.m_body);
return *this;
}

View File

@@ -20,60 +20,60 @@ namespace hex {
curl_global_cleanup();
};
this->m_curl = curl_easy_init();
m_curl = curl_easy_init();
}
HttpRequest::~HttpRequest() {
curl_easy_cleanup(this->m_curl);
curl_easy_cleanup(m_curl);
}
HttpRequest::HttpRequest(HttpRequest &&other) noexcept {
this->m_curl = other.m_curl;
m_curl = other.m_curl;
other.m_curl = nullptr;
this->m_method = std::move(other.m_method);
this->m_url = std::move(other.m_url);
this->m_headers = std::move(other.m_headers);
this->m_body = std::move(other.m_body);
m_method = std::move(other.m_method);
m_url = std::move(other.m_url);
m_headers = std::move(other.m_headers);
m_body = std::move(other.m_body);
}
HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept {
this->m_curl = other.m_curl;
m_curl = other.m_curl;
other.m_curl = nullptr;
this->m_method = std::move(other.m_method);
this->m_url = std::move(other.m_url);
this->m_headers = std::move(other.m_headers);
this->m_body = std::move(other.m_body);
m_method = std::move(other.m_method);
m_url = std::move(other.m_url);
m_headers = std::move(other.m_headers);
m_body = std::move(other.m_body);
return *this;
}
void HttpRequest::setDefaultConfig() {
curl_easy_setopt(this->m_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(this->m_curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
curl_easy_setopt(this->m_curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(this->m_curl, CURLOPT_USERAGENT, "ImHex/1.0");
curl_easy_setopt(this->m_curl, CURLOPT_DEFAULT_PROTOCOL, "https");
curl_easy_setopt(this->m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(this->m_curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(this->m_curl, CURLOPT_TIMEOUT_MS, 0L);
curl_easy_setopt(this->m_curl, CURLOPT_CONNECTTIMEOUT_MS, this->m_timeout);
curl_easy_setopt(this->m_curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(this->m_curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(this->m_curl, CURLOPT_XFERINFODATA, this);
curl_easy_setopt(this->m_curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
curl_easy_setopt(m_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(m_curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(m_curl, CURLOPT_USERAGENT, "ImHex/1.0");
curl_easy_setopt(m_curl, CURLOPT_DEFAULT_PROTOCOL, "https");
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(m_curl, CURLOPT_TIMEOUT_MS, 0L);
curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT_MS, m_timeout);
curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, this);
curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
if (s_proxyState)
curl_easy_setopt(this->m_curl, CURLOPT_PROXY, s_proxyUrl.c_str());
curl_easy_setopt(m_curl, CURLOPT_PROXY, s_proxyUrl.c_str());
}
std::future<HttpRequest::Result<std::vector<u8>>> HttpRequest::downloadFile() {
return std::async(std::launch::async, [this] {
std::vector<u8> response;
curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
return this->executeImpl<std::vector<u8>>(response);
});
@@ -85,8 +85,8 @@ namespace hex {
s_proxyUrl = std::move(proxy);
}
void HttpRequest::setProxyState(bool state) {
s_proxyState = state;
void HttpRequest::setProxyState(bool enabled) {
s_proxyState = enabled;
}
void HttpRequest::checkProxyErrors() {

View File

@@ -62,20 +62,20 @@ namespace hex::log::impl {
return logEntries;
}
void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level) {
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 (impl::isRedirected())
if (isRedirected())
fmt::print(dest, "{0} ", level);
else
fmt::print(dest, ts, "{0} ", level);
fmt::print(dest, "[{0}] ", IMHEX_PROJECT_NAME);
fmt::print(dest, "[{0}] ", projectName);
constexpr static auto ProjectNameLength = std::char_traits<char>::length(IMHEX_PROJECT_NAME);
fmt::print(dest, "{}", std::string(ProjectNameLength > 10 ? 0 : 10 - ProjectNameLength, ' '));
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) {

View File

@@ -112,7 +112,7 @@ namespace hex::magic {
}
std::string getDescription(prv::Provider *provider, size_t size) {
std::vector<u8> buffer(std::min(provider->getSize(), size), 0x00);
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
provider->read(provider->getBaseAddress(), buffer.data(), buffer.size());
return getDescription(buffer);
@@ -135,7 +135,7 @@ namespace hex::magic {
}
std::string getMIMEType(prv::Provider *provider, size_t size) {
std::vector<u8> buffer(std::min(provider->getSize(), size), 0x00);
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
provider->read(provider->getBaseAddress(), buffer.data(), buffer.size());
return getMIMEType(buffer);

View File

@@ -6,9 +6,60 @@
#include <wolv/utils/guards.hpp>
#include <numbers>
namespace hex::gl {
Matrix<float,4,4> GetOrthographicMatrix( float viewWidth, float viewHeight, float nearVal,float farVal, bool actionType)
{
int sign =1;
if (actionType)
sign=-1;
//float left = leftRight.x;
//float right = leftRight.y;
//float down = upDown.x;
//float up = upDown.y;
Matrix<float,4,4> result(0);
//result.updateElement(0,0,sign /(right-left))
result.updateElement(0,0,sign / viewWidth);
//result.updateElement(0,3,sign * (right + left)/(right - left));
//result.updateElement(1,1, sign /(up-down));
result.updateElement(1,1, sign / viewHeight);
//result.updateElement(1,3, sign * (up + down)/(up - down));
result.updateElement(2,2,-sign * 2/(farVal - nearVal));
result.updateElement(3,2,-sign * (farVal + nearVal)/(farVal - nearVal));
result.updateElement(3,3,sign);
return result;
}
Matrix<float,4,4> GetPerspectiveMatrix( float viewWidth, float viewHeight, float nearVal,float farVal, bool actionType)
{
int sign =1;
if (actionType)
sign=-1;
//float left = leftRight.x;
//float right = leftRight.y;
//float down = upDown.x;
//float up = upDown.y;
//T aspect=(right-left)/(top-bottom);
//T f = nearVal/top;
Matrix<float,4,4> result(0);
// T f = 1.0 / tan(fovy / 2.0); tan(fovy / 2.0) = top / near; fovy = 2 * atan2(top,near)
//result.updateElement(0,0,sign * nearVal/(right-left));
//result.updateElement(1,1, sign * nearVal/(up-down));
result.updateElement(0,0,sign * nearVal/viewWidth);
result.updateElement(1,1, sign * nearVal/viewHeight);
result.updateElement(2,2,-sign * (farVal + nearVal)/(farVal - nearVal));
result.updateElement(3,2,-sign * 2*farVal*nearVal/(farVal - nearVal));
result.updateElement(2,3,-sign);
return result;
}
Shader::Shader(std::string_view vertexSource, std::string_view fragmentSource) {
auto vertexShader = glCreateShader(GL_VERTEX_SHADER);
this->compile(vertexShader, vertexSource);
@@ -18,70 +69,71 @@ namespace hex::gl {
ON_SCOPE_EXIT { glDeleteShader(vertexShader); glDeleteShader(fragmentShader); };
this->m_program = glCreateProgram();
m_program = glCreateProgram();
glAttachShader(this->m_program, vertexShader);
glAttachShader(this->m_program, fragmentShader);
glLinkProgram(this->m_program);
glAttachShader(m_program, vertexShader);
glAttachShader(m_program, fragmentShader);
glLinkProgram(m_program);
int result = false;
glGetProgramiv(this->m_program, GL_LINK_STATUS, &result);
glGetProgramiv(m_program, GL_LINK_STATUS, &result);
if (!result) {
std::vector<char> log(512);
glGetShaderInfoLog(this->m_program, log.size(), nullptr, log.data());
glGetShaderInfoLog(m_program, log.size(), nullptr, log.data());
log::error("Failed to link shader: {}", log.data());
}
}
Shader::~Shader() {
if (this->m_program != 0)
glDeleteProgram(this->m_program);
if (m_program != 0)
glDeleteProgram(m_program);
}
Shader::Shader(Shader &&other) noexcept {
this->m_program = other.m_program;
m_program = other.m_program;
other.m_program = 0;
}
Shader& Shader::operator=(Shader &&other) noexcept {
this->m_program = other.m_program;
m_program = other.m_program;
other.m_program = 0;
return *this;
}
void Shader::bind() const {
glUseProgram(this->m_program);
glUseProgram(m_program);
}
void Shader::unbind() const {
glUseProgram(0);
}
void Shader::setUniform(std::string_view name, const int &value) {
glUniform1i(getUniformLocation(name), value);
}
void Shader::setUniform(std::string_view name, const float &value) {
glUniform1f(getUniformLocation(name), value);
}
void Shader::setUniform(std::string_view name, const Vector<float, 3> &value) {
glUniform3f(getUniformLocation(name), value[0], value[1], value[2]);
}
GLint Shader::getUniformLocation(std::string_view name) {
auto uniform = this->m_uniforms.find(name.data());
if (uniform == this->m_uniforms.end()) {
auto location = glGetUniformLocation(this->m_program, name.data());
auto uniform = m_uniforms.find(name.data());
if (uniform == m_uniforms.end()) {
auto location = glGetUniformLocation(m_program, name.data());
if (location == -1) {
log::warn("Uniform '{}' not found in shader", name);
return -1;
}
this->m_uniforms[name.data()] = location;
uniform = this->m_uniforms.find(name.data());
m_uniforms[name.data()] = location;
uniform = m_uniforms.find(name.data());
}
return uniform->second;
}
void Shader::compile(GLuint shader, std::string_view source) {
void Shader::compile(GLuint shader, std::string_view source) const {
auto sourcePtr = source.data();
glShaderSource(shader, 1, &sourcePtr, nullptr);
@@ -98,87 +150,95 @@ namespace hex::gl {
template<typename T>
Buffer<T>::Buffer(BufferType type, std::span<T> data) : m_size(data.size()), m_type(GLuint(type)) {
glGenBuffers(1, &this->m_buffer);
glBindBuffer(this->m_type, this->m_buffer);
glBufferData(this->m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW);
glBindBuffer(this->m_type, 0);
Buffer<T>::Buffer(BufferType type, std::span<const T> data) : m_size(data.size()), m_type(GLuint(type)) {
glGenBuffers(1, &m_buffer);
glBindBuffer(m_type, m_buffer);
glBufferData(m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW);
glBindBuffer(m_type, 0);
}
template<typename T>
Buffer<T>::~Buffer() {
glDeleteBuffers(1, &this->m_buffer);
glDeleteBuffers(1, &m_buffer);
}
template<typename T>
Buffer<T>::Buffer(Buffer &&other) noexcept {
this->m_buffer = other.m_buffer;
this->m_size = other.m_size;
this->m_type = other.m_type;
m_buffer = other.m_buffer;
m_size = other.m_size;
m_type = other.m_type;
other.m_buffer = -1;
}
template<typename T>
Buffer<T>& Buffer<T>::operator=(Buffer &&other) noexcept {
this->m_buffer = other.m_buffer;
this->m_size = other.m_size;
this->m_type = other.m_type;
m_buffer = other.m_buffer;
m_size = other.m_size;
m_type = other.m_type;
other.m_buffer = -1;
return *this;
}
template<typename T>
void Buffer<T>::bind() const {
glBindBuffer(this->m_type, this->m_buffer);
glBindBuffer(m_type, m_buffer);
}
template<typename T>
void Buffer<T>::unbind() const {
glBindBuffer(this->m_type, 0);
glBindBuffer(m_type, 0);
}
template<typename T>
size_t Buffer<T>::getSize() const {
return this->m_size;
return m_size;
}
template<typename T>
void Buffer<T>::draw() const {
switch (this->m_type) {
void Buffer<T>::draw(unsigned primitive) const {
switch (m_type) {
case GL_ARRAY_BUFFER:
glDrawArrays(GL_TRIANGLES, 0, this->m_size);
glDrawArrays(primitive, 0, m_size);
break;
case GL_ELEMENT_ARRAY_BUFFER:
glDrawElements(GL_TRIANGLES, this->m_size, impl::getType<T>(), nullptr);
break;
glDrawElements(primitive, m_size, impl::getType<T>(), nullptr);
break;
}
}
template<typename T>
void Buffer<T>::update(std::span<const T> data) {
glBindBuffer(m_type, m_buffer);
glBufferSubData(m_type, 0, data.size_bytes(), data.data());
glBindBuffer(m_type, 0);
}
template class Buffer<float>;
template class Buffer<u32>;
template class Buffer<u16>;
template class Buffer<u8>;
VertexArray::VertexArray() {
glGenVertexArrays(1, &this->m_array);
glGenVertexArrays(1, &m_array);
}
VertexArray::~VertexArray() {
glDeleteVertexArrays(1, &this->m_array);
glDeleteVertexArrays(1, &m_array);
}
VertexArray::VertexArray(VertexArray &&other) noexcept {
this->m_array = other.m_array;
m_array = other.m_array;
other.m_array = -1;
}
VertexArray& VertexArray::operator=(VertexArray &&other) noexcept {
this->m_array = other.m_array;
m_array = other.m_array;
other.m_array = -1;
return *this;
}
void VertexArray::bind() const {
glBindVertexArray(this->m_array);
glBindVertexArray(m_array);
}
void VertexArray::unbind() const {
@@ -187,8 +247,8 @@ namespace hex::gl {
Texture::Texture(u32 width, u32 height) : m_texture(0), m_width(width), m_height(height) {
glGenTextures(1, &this->m_texture);
glBindTexture(GL_TEXTURE_2D, this->m_texture);
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
@@ -199,26 +259,26 @@ namespace hex::gl {
}
Texture::~Texture() {
if (this->m_texture != 0)
glDeleteTextures(1, &this->m_texture);
if (m_texture != 0)
glDeleteTextures(1, &m_texture);
}
Texture::Texture(Texture &&other) noexcept {
this->m_texture = other.m_texture;
m_texture = other.m_texture;
other.m_texture = -1;
this->m_width = other.m_width;
this->m_height = other.m_height;
m_width = other.m_width;
m_height = other.m_height;
}
Texture& Texture::operator=(Texture &&other) noexcept {
this->m_texture = other.m_texture;
m_texture = other.m_texture;
other.m_texture = -1;
return *this;
}
void Texture::bind() const {
glBindTexture(GL_TEXTURE_2D, this->m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
}
void Texture::unbind() const {
@@ -226,60 +286,59 @@ namespace hex::gl {
}
GLuint Texture::getTexture() const {
return this->m_texture;
return m_texture;
}
u32 Texture::getWidth() const {
return this->m_width;
return m_width;
}
u32 Texture::getHeight() const {
return this->m_height;
return m_height;
}
GLuint Texture::release() {
auto copy = this->m_texture;
this->m_texture = -1;
auto copy = m_texture;
m_texture = -1;
return copy;
}
FrameBuffer::FrameBuffer(u32 width, u32 height) {
glGenFramebuffers(1, &m_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);
FrameBuffer::FrameBuffer() {
glGenFramebuffers(1, &this->m_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer);
glGenRenderbuffers(1, &this->m_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, this->m_renderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1280, 720);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, this->m_renderBuffer);
glGenRenderbuffers(1, &m_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
FrameBuffer::~FrameBuffer() {
glDeleteFramebuffers(1, &this->m_frameBuffer);
glDeleteRenderbuffers(1, &this->m_renderBuffer);
glDeleteFramebuffers(1, &m_frameBuffer);
glDeleteRenderbuffers(1, &m_renderBuffer);
}
FrameBuffer::FrameBuffer(FrameBuffer &&other) noexcept {
this->m_frameBuffer = other.m_frameBuffer;
m_frameBuffer = other.m_frameBuffer;
other.m_frameBuffer = -1;
this->m_renderBuffer = other.m_renderBuffer;
m_renderBuffer = other.m_renderBuffer;
other.m_renderBuffer = -1;
}
FrameBuffer& FrameBuffer::operator=(FrameBuffer &&other) noexcept {
this->m_frameBuffer = other.m_frameBuffer;
m_frameBuffer = other.m_frameBuffer;
other.m_frameBuffer = -1;
this->m_renderBuffer = other.m_renderBuffer;
m_renderBuffer = other.m_renderBuffer;
other.m_renderBuffer = -1;
return *this;
}
void FrameBuffer::bind() const {
glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);
}
void FrameBuffer::unbind() const {
@@ -287,7 +346,7 @@ namespace hex::gl {
}
void FrameBuffer::attachTexture(const Texture &texture) const {
glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);
texture.bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.getTexture(), 0);
@@ -295,4 +354,335 @@ namespace hex::gl {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
AxesVectors::AxesVectors() {
m_vertices.resize(36);
m_colors.resize(48);
m_indices.resize(18);
// Vertices are x,y,z. Colors are RGBA. Indices are for the ends of each segment
// Entries with value zero are unneeded but kept to help keep track of location
// x-axis
//vertices[0]=0.0F; vertices[1]= 0.0F vertices[2] = 0.0F; // shaft base
m_vertices[3] = 1.0F;//vertices[4]= 0.0F vertices[5] = 0.0F; // shaft tip
m_vertices[6] = 0.9F; m_vertices[8] = 0.05F; // arrow base
m_vertices[9] = 0.9F; m_vertices[11]=-0.05F; // arrow base
// y-axis
//vertices[12]=0.0F; vertices[13] = 0.0F; vertices[14]=0.0F;// shaft base
m_vertices[16] = 1.0F;//vertices[17]=0.0F;// shaft tip
m_vertices[18] = 0.05F; m_vertices[19] = 0.9F;//vertices[20]=0.0F;// arrow base
m_vertices[21] =-0.05F; m_vertices[22] = 0.9F;//vertices[23]=0.0F;// arrow base
// z-axis
//vertices[24]=0.0F; vertices[25]=0.0F vertices[26] = 0.0F; // shaft base
m_vertices[29] = 1.0F; // shaft tip
m_vertices[30] = 0.05F; m_vertices[32] = 0.9F; // arrow base
m_vertices[33] =-0.05F; m_vertices[35] = 0.9F; // arrow base
// x-axis colors
m_colors[0] = 0.7F; m_colors[3] = 1.0F;
m_colors[4] = 0.7F; m_colors[7] = 1.0F;
m_colors[8] = 0.7F; m_colors[11] = 1.0F;
m_colors[12] = 0.7F; m_colors[15] = 1.0F;
// y-axis colors
m_colors[17] = 0.7F; m_colors[19] = 1.0F;
m_colors[21] = 0.7F; m_colors[23] = 1.0F;
m_colors[25] = 0.7F; m_colors[27] = 1.0F;
m_colors[29] = 0.7F; m_colors[31] = 1.0F;
// z-axis colors
m_colors[34] = 0.7F; m_colors[35] = 1.0F;
m_colors[38] = 0.7F; m_colors[39] = 1.0F;
m_colors[42] = 0.7F; m_colors[43] = 1.0F;
m_colors[46] = 0.7F; m_colors[47] = 1.0F;
// indices for x
m_indices[0] = 0; m_indices[1] = 1;
m_indices[2] = 2; m_indices[3] = 1;
m_indices[4] = 3; m_indices[5] = 1;
// indices for y
m_indices[6] = 4; m_indices[7] = 5;
m_indices[8] = 6; m_indices[9] = 5;
m_indices[10] = 7; m_indices[11] = 5;
// indices for z
m_indices[12] = 8; m_indices[13] = 9;
m_indices[14] = 10; m_indices[15] = 9;
m_indices[16] = 11; m_indices[17] = 9;
}
AxesBuffers::AxesBuffers(const VertexArray& axesVertexArray, const AxesVectors &axesVectors) {
m_vertices = {};
m_colors = {};
m_indices = {};
axesVertexArray.bind();
m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, axesVectors.getVertices());
m_colors = gl::Buffer<float>(gl::BufferType::Vertex, axesVectors.getColors());
m_indices = gl::Buffer<u8>(gl::BufferType::Index, axesVectors.getIndices());
axesVertexArray.addBuffer(0, m_vertices);
axesVertexArray.addBuffer(1, m_colors, 4);
m_vertices.unbind();
m_colors.unbind();
m_indices.unbind();
axesVertexArray.unbind();
}
GridVectors::GridVectors(int sliceCount) {
m_slices = sliceCount;
m_vertices.resize((m_slices + 1) * (m_slices + 1) * 3);
m_colors.resize((m_slices + 1) * (m_slices + 1) * 4);
m_indices.resize(m_slices * m_slices * 6 + m_slices * 2);
int k = 0;
int l = 0;
for (u32 j = 0; j <= m_slices; ++j) {
float z = 2.0f * float(j) / float(m_slices) - 1.0f;
for (u32 i = 0; i <= m_slices; ++i) {
m_vertices[k ] = 2.0f * float(i) / float(m_slices) - 1.0f;
m_vertices[k + 1] = 0.0f;
m_vertices[k + 2] = z;
k += 3;
m_colors[l ] = 0.5f;
m_colors[l + 1] = 0.5f;
m_colors[l + 2] = 0.5f;
m_colors[l + 3] = 0.3f;
l += 4;
}
}
k = 0;
for (u32 j = 0; j < m_slices; ++j) {
int row1 = j * (m_slices + 1);
int row2 = (j + 1) * (m_slices + 1);
for (u32 i = 0; i < m_slices; ++i) {
m_indices[k ] = row1 + i;
m_indices[k + 1] = row1 + i + 1;
m_indices[k + 2] = row1 + i + 1;
m_indices[k + 3] = row2 + i + 1;
m_indices[k + 4] = row2 + i + 1;
m_indices[k + 5] = row2 + i;
k += 6;
if (i == 0) {
m_indices[k ] = row2 + i;
m_indices[k + 1] = row1 + i;
k += 2;
}
}
}
}
GridBuffers::GridBuffers(const VertexArray& gridVertexArray, const GridVectors &gridVectors) {
m_vertices = {};
m_colors = {};
m_indices = {};
gridVertexArray.bind();
m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, gridVectors.getVertices());
m_indices = gl::Buffer<u8>(gl::BufferType::Index, gridVectors.getIndices());
m_colors = gl::Buffer<float>(gl::BufferType::Vertex, gridVectors.getColors());
gridVertexArray.addBuffer(0, m_vertices);
gridVertexArray.addBuffer(1, m_colors,4);
m_vertices.unbind();
m_colors.unbind();
m_indices.unbind();
gridVertexArray.unbind();
}
hex::gl::LightSourceVectors::LightSourceVectors(int res) {
m_resolution = res;
auto res_sq = m_resolution * m_resolution;
m_radius = 0.05f;
m_vertices.resize((res_sq + 2) * 3);
m_normals.resize((res_sq + 2) * 3);
m_colors.resize((res_sq + 2) * 4);
m_indices.resize(res_sq * 6);
constexpr auto TwoPi = std::numbers::pi_v<float> * 2.0F;
constexpr auto HalfPi = std::numbers::pi_v<float> / 2.0F;
const auto dv = TwoPi / m_resolution;
const auto du = std::numbers::pi_v<float> / (m_resolution + 1);
m_normals[0] = 0;
m_normals[1] = 0;
m_normals[2] = 1;
m_vertices[0] = 0;
m_vertices[1] = 0;
m_vertices[2] = m_radius;
m_colors[0] = 1.0;
m_colors[1] = 1.0;
m_colors[2] = 1.0;
m_colors[3] = 1.0;
// Vertical: pi/2 to -pi/2
for (int i = 0; i < m_resolution; i += 1) {
float u = HalfPi - (i + 1) * du;
float z = std::sin(u);
float xy = std::cos(u);
// Horizontal: 0 to 2pi
for (int j = 0; j < m_resolution; j += 1) {
float v = j * dv;
float x = xy * std::cos(v);
float y = xy * std::sin(v);
i32 n = (i * m_resolution + j + 1) * 3;
m_normals[n] = x;
m_normals[n + 1] = y;
m_normals[n + 2] = z;
m_vertices[n] = m_radius * x;
m_vertices[n + 1] = m_radius * y;
m_vertices[n + 2] = m_radius * z;
n = (i * m_resolution + j + 1) * 4;
m_colors[n] = 1.0f;
m_colors[n + 1] = 1.0f;
m_colors[n + 2] = 1.0f;
m_colors[n + 3] = 1.0f;
}
}
i32 n = ((res_sq + 1) * 3);
m_normals[n ] = 0;
m_normals[n + 1] = 0;
m_normals[n + 2] = -1;
m_vertices[n ] = 0;
m_vertices[n + 1] = 0;
m_vertices[n + 2] = -m_radius;
n = ((res_sq + 1) * 4);
m_colors[n ] = 1.0;
m_colors[n + 1] = 1.0;
m_colors[n + 2] = 1.0;
m_colors[n + 3] = 1.0;
// that was the easy part, indices are a bit more complicated
// and may need some explaining. The RxR grid slices the globe
// into longitudes which are the vertical slices and latitudes
// which are the horizontal slices. The latitudes are all full
// circles except for the poles, so we don't count them as part
// of the grid. That means that there are R+2 latitudes and R
// longitudes.Between consecutive latitudes we have 2*R triangles.
// Since we have R true latitudes there are R-1 spaces between them so
// between the top and the bottom we have 2*R*(R-1) triangles.
// the top and bottom have R triangles each, so we have a total of
// 2*R*(R-1) + 2*R = 2*R*R triangles. Each triangle has 3 vertices,
// so we have 6*R*R indices.
// The North Pole is index 0 and the South Pole is index 6*res*res -1
// The first row of vertices is 1 to res, the second row is res+1 to 2*res etc.
// First, the North Pole
for (int i = 0; i < m_resolution; i += 1) {
m_indices[i * 3] = 0;
m_indices[i * 3 + 1] = i + 1;
if (i == m_resolution - 1)
m_indices[i * 3 + 2] = 1;
else
m_indices[i * 3 + 2] = (i + 2);
}
// Now the spaces between true latitudes
for (int i = 0; i < m_resolution - 1; i += 1) {
// k is the index of the first vertex of the i-th latitude
i32 k = i * m_resolution + 1;
// When we go a full circle we need to connect the last vertex to the first, so
// we do R-1 first because their indices can be computed easily
for (int j = 0; j < m_resolution - 1; j += 1) {
// We store the indices of the array where the vertices were store
// in the triplets that make the triangles. These triplets are stored in
// an array that has indices itself which can be confusing.
// l keeps track of the indices of the array that stores the triplets
// each i brings 6R and each j 6. 3R from the North Pole.
i32 l = (i * m_resolution + j) * 6 + 3 * m_resolution;
m_indices[l ] = k + j;
m_indices[l + 1] = k + j + m_resolution + 1;
m_indices[l + 2] = k + j + 1;
m_indices[l + 3] = k + j;
m_indices[l + 4] = k + j + m_resolution;
m_indices[l + 5] = k + j + m_resolution + 1;
}
// Now the last vertex of the i-th latitude is connected to the first
i32 l = (( i + 1) * m_resolution - 1) * 6 + 3 * m_resolution;
m_indices[l ] = k + m_resolution - 1;
m_indices[l + 1] = k + m_resolution;
m_indices[l + 2] = k;
m_indices[l + 3] = k + m_resolution - 1;
m_indices[l + 4] = k + 2 * m_resolution - 1;
m_indices[l + 5] = k + m_resolution;
}
// Now the South Pole
i32 k = (m_resolution-1) * m_resolution + 1;
i32 l = 3 * m_resolution * ( 2 * m_resolution - 1);
for (int i = 0; i < m_resolution; i += 1) {
if (i == m_resolution -1)
m_indices[l + i * 3] = k;
else
m_indices[l + i * 3] = k + i + 1;
m_indices[l + i * 3 + 1] = k + i;
m_indices[l + i * 3 + 2] = k + m_resolution;
}
}
void LightSourceVectors::moveTo(const Vector<float, 3> &position) {
auto vertexCount = m_vertices.size();
for (unsigned k = 0; k < vertexCount; k += 3) {
m_vertices[k ] = m_radius * m_normals[k ] + position[0];
m_vertices[k + 1] = m_radius * m_normals[k + 1] + position[1];
m_vertices[k + 2] = m_radius * m_normals[k + 2] + position[2];
}
}
LightSourceBuffers::LightSourceBuffers(const VertexArray &sourceVertexArray, const LightSourceVectors &sourceVectors) {
sourceVertexArray.bind();
m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getVertices());
m_indices = gl::Buffer<u16>(gl::BufferType::Index, sourceVectors.getIndices());
m_normals = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getNormals());
m_colors = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getColors());
sourceVertexArray.addBuffer(0, m_vertices);
sourceVertexArray.addBuffer(1, m_normals);
sourceVertexArray.addBuffer(2, m_colors, 4);
m_vertices.unbind();
m_normals.unbind();
m_colors.unbind();
m_indices.unbind();
sourceVertexArray.unbind();
}
void LightSourceBuffers::moveVertices(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) {
sourceVertexArray.bind();
m_vertices.update(sourceVectors.getVertices());
sourceVertexArray.addBuffer(0, m_vertices);
sourceVertexArray.unbind();
}
void LightSourceBuffers::updateColors(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) {
sourceVertexArray.bind();
m_colors.update(sourceVectors.getColors());
sourceVertexArray.addBuffer(2, m_colors, 4);
sourceVertexArray.unbind();
}
}

View File

@@ -33,46 +33,46 @@ namespace hex {
void writeRaw(u64 offset, const void *buffer, size_t size) override {
for (u64 i = 0; i < size; i += 1)
this->m_patches[offset] = static_cast<const u8*>(buffer)[i];
m_patches[offset] = static_cast<const u8*>(buffer)[i];
}
[[nodiscard]] size_t getActualSize() const override {
if (this->m_patches.empty())
[[nodiscard]] u64 getActualSize() const override {
if (m_patches.empty())
return 0;
else
return this->m_patches.rbegin()->first;
return m_patches.rbegin()->first;
}
void resizeRaw(size_t newSize) override {
void resizeRaw(u64 newSize) override {
hex::unused(newSize);
}
void insertRaw(u64 offset, size_t size) override {
void insertRaw(u64 offset, u64 size) override {
std::vector<std::pair<u64, u8>> patchesToMove;
for (auto &[address, value] : this->m_patches) {
for (auto &[address, value] : m_patches) {
if (address > offset)
patchesToMove.emplace_back(address, value);
}
for (const auto &[address, value] : patchesToMove)
this->m_patches.erase(address);
m_patches.erase(address);
for (const auto &[address, value] : patchesToMove)
this->m_patches.insert({ address + size, value });
m_patches.insert({ address + size, value });
}
void removeRaw(u64 offset, size_t size) override {
void removeRaw(u64 offset, u64 size) override {
std::vector<std::pair<u64, u8>> patchesToMove;
for (auto &[address, value] : this->m_patches) {
for (auto &[address, value] : m_patches) {
if (address > offset)
patchesToMove.emplace_back(address, value);
}
for (const auto &[address, value] : patchesToMove)
this->m_patches.erase(address);
m_patches.erase(address);
for (const auto &[address, value] : patchesToMove)
this->m_patches.insert({ address - size, value });
m_patches.insert({ address - size, value });
}
[[nodiscard]] std::string getName() const override {
@@ -82,7 +82,7 @@ namespace hex {
[[nodiscard]] std::string getTypeName() const override { return ""; }
const std::map<u64, u8>& getPatches() const {
return this->m_patches;
return m_patches;
}
private:
std::map<u64, u8> m_patches;
@@ -111,7 +111,7 @@ namespace hex {
std::vector<u64> addresses;
std::vector<u8> values;
for (const auto &[address, value] : this->m_patches) {
for (const auto &[address, value] : m_patches) {
addresses.push_back(address);
values.push_back(value);
}
@@ -161,7 +161,7 @@ namespace hex {
std::vector<u64> addresses;
std::vector<u8> values;
for (const auto &[address, value] : this->m_patches) {
for (const auto &[address, value] : m_patches) {
addresses.push_back(address);
values.push_back(value);
}

View File

@@ -5,12 +5,14 @@
#include <wolv/io/file.hpp>
#include <microtar.h>
namespace hex {
using namespace hex::literals;
Tar::Tar(const std::fs::path &path, Mode mode) {
int tar_error = MTAR_ESUCCESS;
int tarError = MTAR_ESUCCESS;
// Explicitly create file so a short path gets generated
if (mode == Mode::Create) {
@@ -18,24 +20,26 @@ namespace hex {
file.flush();
}
m_ctx = std::make_unique<mtar_t>();
auto shortPath = wolv::io::fs::toShortPath(path);
if (mode == Tar::Mode::Read)
tar_error = mtar_open(&this->m_ctx, shortPath.string().c_str(), "r");
tarError = mtar_open(m_ctx.get(), shortPath.string().c_str(), "r");
else if (mode == Tar::Mode::Write)
tar_error = mtar_open(&this->m_ctx, shortPath.string().c_str(), "a");
tarError = mtar_open(m_ctx.get(), shortPath.string().c_str(), "a");
else if (mode == Tar::Mode::Create)
tar_error = mtar_open(&this->m_ctx, shortPath.string().c_str(), "w");
tarError = mtar_open(m_ctx.get(), shortPath.string().c_str(), "w");
else
tar_error = MTAR_EFAILURE;
tarError = MTAR_EFAILURE;
this->m_path = path;
this->m_valid = (tar_error == MTAR_ESUCCESS);
m_path = path;
m_valid = (tarError == MTAR_ESUCCESS);
if (!this->m_valid) {
this->m_tarOpenErrno = tar_error;
if (!m_valid) {
m_tarOpenErrno = tarError;
// Hopefully this errno corresponds to the file open call in mtar_open
this->m_fileOpenErrno = errno;
m_fileOpenErrno = errno;
}
}
@@ -44,48 +48,46 @@ namespace hex {
}
Tar::Tar(hex::Tar &&other) noexcept {
this->m_ctx = other.m_ctx;
this->m_path = other.m_path;
this->m_valid = other.m_valid;
this->m_tarOpenErrno = other.m_tarOpenErrno;
this->m_fileOpenErrno = other.m_fileOpenErrno;
m_ctx = std::move(other.m_ctx);
m_path = other.m_path;
m_valid = other.m_valid;
m_tarOpenErrno = other.m_tarOpenErrno;
m_fileOpenErrno = other.m_fileOpenErrno;
other.m_ctx = { };
other.m_valid = false;
}
Tar &Tar::operator=(Tar &&other) noexcept {
this->m_ctx = other.m_ctx;
other.m_ctx = { };
m_ctx = std::move(other.m_ctx);
m_path = std::move(other.m_path);
this->m_path = other.m_path;
this->m_valid = other.m_valid;
m_valid = other.m_valid;
other.m_valid = false;
this->m_tarOpenErrno = other.m_tarOpenErrno;
this->m_fileOpenErrno = other.m_fileOpenErrno;
m_tarOpenErrno = other.m_tarOpenErrno;
m_fileOpenErrno = other.m_fileOpenErrno;
return *this;
}
std::vector<std::fs::path> Tar::listEntries(const std::fs::path &basePath) {
std::vector<std::fs::path> Tar::listEntries(const std::fs::path &basePath) const {
std::vector<std::fs::path> result;
const std::string PaxHeaderName = "@PaxHeader";
mtar_header_t header;
while (mtar_read_header(&this->m_ctx, &header) != MTAR_ENULLRECORD) {
while (mtar_read_header(m_ctx.get(), &header) != MTAR_ENULLRECORD) {
std::fs::path path = header.name;
if (header.name != PaxHeaderName && wolv::io::fs::isSubPath(basePath, path)) {
result.emplace_back(header.name);
}
mtar_next(&this->m_ctx);
mtar_next(m_ctx.get());
}
return result;
}
bool Tar::contains(const std::fs::path &path) {
bool Tar::contains(const std::fs::path &path) const {
mtar_header_t header;
auto fixedPath = path.string();
@@ -93,49 +95,49 @@ namespace hex {
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
return mtar_find(&this->m_ctx, fixedPath.c_str(), &header) == MTAR_ESUCCESS;
return mtar_find(m_ctx.get(), fixedPath.c_str(), &header) == MTAR_ESUCCESS;
}
std::string Tar::getOpenErrorString() const {
return hex::format("{}: {}", mtar_strerror(this->m_tarOpenErrno), std::strerror(this->m_fileOpenErrno));
return hex::format("{}: {}", mtar_strerror(m_tarOpenErrno), std::strerror(m_fileOpenErrno));
}
void Tar::close() {
if (this->m_valid) {
mtar_finalize(&this->m_ctx);
mtar_close(&this->m_ctx);
if (m_valid) {
mtar_finalize(m_ctx.get());
mtar_close(m_ctx.get());
}
this->m_ctx = { };
this->m_valid = false;
m_ctx.reset();
m_valid = false;
}
std::vector<u8> Tar::readVector(const std::fs::path &path) {
std::vector<u8> Tar::readVector(const std::fs::path &path) const {
mtar_header_t header;
auto fixedPath = path.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
int ret = mtar_find(&this->m_ctx, fixedPath.c_str(), &header);
int ret = mtar_find(m_ctx.get(), fixedPath.c_str(), &header);
if (ret != MTAR_ESUCCESS){
log::debug("Failed to read vector from path {} in tarred file {}: {}",
path.string(), this->m_path.string(), mtar_strerror(ret));
path.string(), m_path.string(), mtar_strerror(ret));
return {};
}
std::vector<u8> result(header.size, 0x00);
mtar_read_data(&this->m_ctx, result.data(), result.size());
mtar_read_data(m_ctx.get(), result.data(), result.size());
return result;
}
std::string Tar::readString(const std::fs::path &path) {
std::string Tar::readString(const std::fs::path &path) const {
auto result = this->readVector(path);
return { result.begin(), result.end() };
}
void Tar::writeVector(const std::fs::path &path, const std::vector<u8> &data) {
void Tar::writeVector(const std::fs::path &path, const std::vector<u8> &data) const {
if (path.has_parent_path()) {
std::fs::path pathPart;
for (const auto &part : path.parent_path()) {
@@ -145,7 +147,7 @@ namespace hex {
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_write_dir_header(&this->m_ctx, fixedPath.c_str());
mtar_write_dir_header(m_ctx.get(), fixedPath.c_str());
}
}
@@ -153,11 +155,11 @@ namespace hex {
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_write_file_header(&this->m_ctx, fixedPath.c_str(), data.size());
mtar_write_data(&this->m_ctx, data.data(), data.size());
mtar_write_file_header(m_ctx.get(), fixedPath.c_str(), data.size());
mtar_write_data(m_ctx.get(), data.data(), data.size());
}
void Tar::writeString(const std::fs::path &path, const std::string &data) {
void Tar::writeString(const std::fs::path &path, const std::string &data) const {
this->writeVector(path, { data.begin(), data.end() });
}
@@ -175,26 +177,26 @@ namespace hex {
}
}
void Tar::extract(const std::fs::path &path, const std::fs::path &outputPath) {
void Tar::extract(const std::fs::path &path, const std::fs::path &outputPath) const {
mtar_header_t header;
mtar_find(&this->m_ctx, path.string().c_str(), &header);
mtar_find(m_ctx.get(), path.string().c_str(), &header);
writeFile(&this->m_ctx, &header, outputPath);
writeFile(m_ctx.get(), &header, outputPath);
}
void Tar::extractAll(const std::fs::path &outputPath) {
void Tar::extractAll(const std::fs::path &outputPath) const {
mtar_header_t header;
while (mtar_read_header(&this->m_ctx, &header) != MTAR_ENULLRECORD) {
while (mtar_read_header(m_ctx.get(), &header) != MTAR_ENULLRECORD) {
const auto filePath = std::fs::absolute(outputPath / std::fs::path(header.name));
if (filePath.filename() != "@PaxHeader") {
std::fs::create_directories(filePath.parent_path());
writeFile(&this->m_ctx, &header, filePath);
writeFile(m_ctx.get(), &header, filePath);
}
mtar_next(&this->m_ctx);
mtar_next(m_ctx.get());
}
}

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