Compare commits

...

340 Commits

Author SHA1 Message Date
WerWolv
dc8245ce4c build: Bumped version to 1.29.0 2023-05-21 10:44:41 +02:00
WerWolv
1b88b3704d feat: Allow layouts to be deleted again 2023-05-21 10:43:35 +02:00
WerWolv
6dbaac4283 impr: Make sure hex editor popups have their input focused 2023-05-21 10:35:14 +02:00
WerWolv
180a27fdc9 fix: Local keybindings not working correctly 2023-05-21 10:28:55 +02:00
WerWolv
c02465b892 fix: Byte type and Entropy graph being shifted 2023-05-21 10:23:33 +02:00
WerWolv
f503a89f98 fix: Typo in IEEE754 tool 2023-05-20 21:49:45 +02:00
WerWolv
f94e5488d4 fix: Mouse cursor not adjusting anymore properly 2023-05-20 21:36:15 +02:00
WerWolv
87621e9337 impr: Disable some Import... options when the provider isn't writable 2023-05-20 21:23:15 +02:00
WerWolv
3219ba68de impr: Better layout of ImHex subtitle on welcome screen 2023-05-20 21:20:32 +02:00
WerWolv
5d608603cb fix: Selection in diff view being retained 2023-05-20 21:13:40 +02:00
WerWolv
7336c8dddc fix: Cursor types on Windows sometimes getting stuck 2023-05-20 21:10:12 +02:00
WerWolv
c91e3875d4 build: Updated libwolv 2023-05-20 19:27:00 +02:00
WerWolv
0181325b64 build: Updated libwolv 2023-05-20 18:10:26 +02:00
WerWolv
11f55a7561 fix: File handles not being closed correctly 2023-05-20 18:07:15 +02:00
WerWolv
9bb3a92e12 build: Updated libwolv 2023-05-20 15:47:21 +02:00
KOLANICH
3e9ba8c636 build: Add EOLs to the end of all "own" CMakeLists.txt files (#1093)
Text editors on Unix-like OSes add them automatically and it is usually
considered to be a good practice.
2023-05-20 13:38:24 +02:00
KOLANICH
4faebf435c impr: Remove an unused std::mutex in TaskManager::runner (#1088) 2023-05-20 13:38:12 +02:00
KOLANICH
749823e044 build: Move -s additional compiler flags into linker flags, and enable it only when gcc/clang are used. (#1087)
Compiler when compiling doesn't use them in those cases and emit a
warning, which is turned into an error by `-Werror`. Unfortunately,
CPack doesn't expose the logic it uses for stripping binaries.
2023-05-20 13:37:57 +02:00
KOLANICH
608c9e2e7a fix: WASM disassembler not being available in Capstone < 5 (#1086)
This fixes build on Debian with Capstone from packages.
2023-05-20 13:37:45 +02:00
WerWolv
96ee544538 build: Updated libwolv 2023-05-20 13:31:41 +02:00
WerWolv
7ea7c531e2 fix: Logs not being written to disk on Windows 2023-05-20 13:10:35 +02:00
WerWolv
969a37877a fix: Crash on exit 2023-05-20 13:10:24 +02:00
WerWolv
3cb8e37182 fix: Broken file open logic
Closes #1090
2023-05-20 11:52:24 +02:00
paxcut
3e4c4430d5 feat: Greatly improved the IEEE754 tool (#1047)
I just realized one feature request existed about this tool and have
added a comment to it referring this pr. Errors and additions are
described in the fork commit already. I'm not sure if I should repeat
them here again. I have tested the changes thoroughly, but it is always
possible some fringe case was not tested and is incorrect. The tests
were done using the many similar online calculators for IEEE 754
floating point formats.
IEEE 745 floating point tool redesign modeled after 'float toy' web app
(http://evanw.github.io/float-toy/)

Streamlined output using colors and compact layout which can be further
simplified.
Chosen display mode (detailed or simplified) is automatically saved and
set on new sessions.
Edit the binary bits, the integer hexadecimal or the floating point
decimal values and the entire app will update with the change.
Supports the main IEEE745 standard formats (half, single and double
precision) together with custom formats of size <= 64 bits.
Each format choice uses and displays the number of significant decimal
digits defined by the mantissa size.
Added labels to identify the location of each bit box inside the binary
representation.
Satisfies round trip / idempotent (reproducing) conversion property
Added theme colors, radio buttons for display mode and a clear button
that resets the tool.
Removed previously and incorrectly added locale translation to various
labels and languages
Attempted to adhere to code style formatting using existing code as
example.
An effort was made to use preferred variable types and functions from
std namespace when appropriate.
Attempted to document code using comments. 

Not implemented / left to complete at an later time

Arbitrary width and precision formats. 
Extended precision formats.
Shortest string property.
hexadecimal floating point display and conversions.
2023-05-19 21:18:38 +02:00
WerWolv
4ad66365d0 fix: Crash when saving patches
Fixes #1077
2023-05-19 20:01:42 +02:00
WerWolv
39276e123e patterns: Allow bitfield values to be edited 2023-05-19 19:56:55 +02:00
WerWolv
0f9434740f fix: Crash when deleting last environment variable in pattern editor 2023-05-19 17:15:37 +02:00
WerWolv
7ed153a47b patterns: Updated pattern language 2023-05-19 16:14:54 +02:00
WerWolv
c9d673ce40 patterns: Updated pattern language 2023-05-19 15:29:18 +02:00
WerWolv
0be610f1cd patterns: Updated pattern language 2023-05-18 23:20:10 +02:00
WerWolv
f75aa1f1b0 git: Fixed color of docs shield 2023-05-18 11:46:51 +02:00
Justus Garbe
5ef58cdf76 feat: Added bit and byte reverse and bit display data processor nodes (#1082)
#1081
2023-05-18 10:25:59 +02:00
WerWolv
511375feb5 patterns: Updated pattern language 2023-05-18 09:51:24 +02:00
WerWolv
8119929ece fix: Don't wait for services on exit 2023-05-16 14:59:14 +02:00
iTrooz
e1620966e5 feat: Keep only 10 latest logs on exit (#1079)
Co-authored-by: Nik <werwolv98@gmail.com>
2023-05-16 14:45:24 +02:00
WerWolv
a5b0a8614a impr: Automatically re-evaluate pattern when changing provider and code sync is on
Closes #1078
2023-05-16 14:45:04 +02:00
WerWolv
e28f2dfea1 fix: Only 5 patterns per byte being highlighted
Fixes #1076
2023-05-16 14:41:02 +02:00
WerWolv
0dcfeaefb3 fix: Build when git commit hash or branch is not available 2023-05-16 12:02:17 +02:00
WerWolv
3bd4a3ba8b impr: Better JSON error handling in many places 2023-05-16 11:33:00 +02:00
WerWolv
7e9b23de7d impr: Improve network interface error handling 2023-05-16 11:20:46 +02:00
WerWolv
a758676b0d fix: Change network interface port to 31337 2023-05-16 11:01:59 +02:00
WerWolv
636ed0095d feat: Added new imhex/capabilities network interface 2023-05-16 11:01:40 +02:00
WerWolv
14341d611d impr: Handle macOS Open with... correctly 2023-05-15 18:07:49 +02:00
WerWolv
07565eea63 build: Updated libwolv 2023-05-15 18:07:35 +02:00
WerWolv
c006062540 feat: Added basic network interface support 2023-05-15 11:30:24 +02:00
WerWolv
e685d65be8 fix: Scrolling not working correctly on welcome screen
Fixes #1071
2023-05-15 08:33:35 +02:00
WerWolv
ab67e274b2 fix: Hex editor editing value not updating correctly 2023-05-15 08:17:16 +02:00
WerWolv
7656fd7a4e patterns: Updated pattern language 2023-05-15 08:17:02 +02:00
WerWolv
60a2b30e91 fix: Another wrong include on macOS 2023-05-14 22:53:52 +02:00
WerWolv
7ec7e562d1 fix: Wrong editing values in hex editor in some circumstances 2023-05-14 22:52:47 +02:00
WerWolv
c50d52d0a6 fix: Crash when reading from modified bytes 2023-05-14 22:46:19 +02:00
WerWolv
2b765617ce impr: Disallow saving pattern files if they're empty 2023-05-14 22:24:16 +02:00
WerWolv
79cdf51588 fix: Wrong include on macOS 2023-05-14 22:23:05 +02:00
WerWolv
7b048d9b96 fix: Compile on macOS _again_ 2023-05-14 22:03:04 +02:00
WerWolv
d96fc6d41b fix: Compilation on macOS 2023-05-14 21:50:58 +02:00
WerWolv
610f109e2a fix: Crash when too many entries are being logged at once 2023-05-14 21:39:18 +02:00
WerWolv
609afebc55 fix: Updated build script 2023-05-14 20:20:40 +02:00
WerWolv
3bd9ab6349 fix: Hopefully open files now on macOS 2023-05-14 20:20:22 +02:00
WerWolv
5027f36d95 fix: Open with crashing on macOS
#1070
2023-05-14 18:35:35 +02:00
WerWolv
a5498995ff impr: Hopefully allow macOS to open any file type
#1070
2023-05-14 09:58:15 +02:00
WerWolv
f40b5d9811 fix: Search button in numeric find option being active by default 2023-05-13 17:51:16 +02:00
WerWolv
d00fae03a9 fix: Diff table not being cleared when closing providers 2023-05-13 17:50:33 +02:00
WerWolv
3c4e1b2f27 impr: Various cleanup 2023-05-13 17:50:16 +02:00
WerWolv
6e5d6810e7 build: Disable assertions in release builds 2023-05-13 17:49:53 +02:00
WerWolv
4afd5a7905 patterns: Updated pattern language
#1069
2023-05-13 17:21:44 +02:00
WerWolv
6709baa710 patterns: Updated pattern language 2023-05-13 15:43:37 +02:00
WerWolv
aa1bf0b764 impr: Make about page resizable 2023-05-13 12:26:13 +02:00
WerWolv
a7327290ea fix: Only save custom encoding to project file when necessary 2023-05-13 11:17:27 +02:00
WerWolv
857e90a37b fix: ImHex freezing when evaluating patterns 2023-05-13 11:12:38 +02:00
WerWolv
aaeebd3fe9 fix: Various pattern execution race conditions 2023-05-12 15:46:13 +02:00
WerWolv
0a7a190b04 impr: Added tooltips to pattern data view when name or value is too long 2023-05-12 08:49:08 +02:00
WerWolv
21d922113d impr: Make sure to clear selections when all providers are deleted 2023-05-12 08:38:52 +02:00
WerWolv
b0876e1c35 impr: Move some options into a new Extras menu 2023-05-12 08:38:32 +02:00
WerWolv
18bc5de169 fix: Copy-as options not being disabled correctly 2023-05-12 08:38:07 +02:00
WerWolv
0321743f1e fix: Missing save layout localization keys 2023-05-12 08:37:43 +02:00
WerWolv
f2af90fe06 fix: Help hover icon issues 2023-05-11 23:56:51 +02:00
WerWolv
2f511ec4fa impr: Improve frame cap handling 2023-05-11 23:22:06 +02:00
WerWolv
0649e0dcd3 impr: Clean up old layout stuff 2023-05-11 23:21:52 +02:00
WerWolv
0b21e30e44 fix: White border around maximized window on WIndows 2023-05-11 20:49:07 +02:00
WerWolv
ceeaca1a4b feat: Added layout manager to save and restore custom layouts 2023-05-11 18:44:50 +02:00
WerWolv
2da89f4b9b fix: Position of restore layout button being wrong with different scalings 2023-05-11 18:44:33 +02:00
WerWolv
49371398bc fix: Resizing on windows still being janky 2023-05-11 18:43:19 +02:00
iTrooz
2e73d74cea impr: Open .hexproj files as projects when opened though the Open File option (#1061) 2023-05-11 12:38:22 +02:00
WerWolv
688471fd61 build: Updated libfmt to 10.0 2023-05-11 12:06:58 +02:00
WerWolv
ea482ff0f5 fix: Crash when closing providers too quickly 2023-05-11 12:01:06 +02:00
WerWolv
5c9d0e29c2 fix: Left/Right resizing not working correctly on Windows 2023-05-11 12:00:56 +02:00
WerWolv
8f07f0f8ae impr: Better look and feel of the welcome screen close button 2023-05-11 12:00:45 +02:00
WerWolv
4f17a96707 build: Updated libwolv 2023-05-11 10:04:09 +02:00
iTrooz
b0ab8698ec fix: Loading data processor nodes from project thowing errors (#1065)
This will correct the errors `cannot use operator[] with a string
argument with string[..]` when loading a project
2023-05-11 09:57:29 +02:00
WerWolv
ab41899cc2 build: Updated libwolv 2023-05-11 09:54:06 +02:00
WerWolv
50c3cf8272 build: Replace old interval tree in favour of custom libwolv one 2023-05-11 09:27:23 +02:00
WerWolv
1d2b8ac1f3 fix: Closing popups using Esc causing them to re-appear 2023-05-10 22:29:17 +02:00
WerWolv
0b29719fe9 patterns: Updated pattern language 2023-05-10 19:15:01 +02:00
WerWolv
5a6e5d2255 build: Switch to better interval tree implementation 2023-05-07 23:27:43 +02:00
WerWolv
82111617a4 patterns: Updated pattern language 2023-05-06 10:09:33 +02:00
WerWolv
0574387ee1 fix: Properly use absolute paths when compiling magic files 2023-05-06 10:09:23 +02:00
WerWolv
2d1381860d fix: Corrected build issues with GCC 13 2023-05-06 10:07:22 +02:00
classabbyamp
82f5900759 build: Added option to disable update checking (#1036)
This is aimed at use by linux distros, where package updates come from a
central location, and users shouldn't need to worry about updating ImHex
on their own. This disables parts of the ImHex UI that would not be
useful in that case.

Tested and confirmed that this works in both states of the of the
`-DIMHEX_DISABLE_UPDATE_CHECK` switch.
2023-05-05 22:03:45 +02:00
iTrooz
e44eb2aa8e impr: Added more documentation to libimhex (#1052)
This PR adds some documentation. It's actually pretty random, I followed
the function calls I was curious about and commented whenever I wasn't
sure/I thought it needed clarification

You might want to make sure to squash them, because the commits are kind
of a mess, I didn't went through the effort of interactive rebase
2023-05-05 22:02:18 +02:00
iTrooz
34e12e2515 feat: Added a button to restore default layout when there are no views open (#1053)
Co-authored-by: Nik <werwolv98@gmail.com>
2023-05-05 22:00:17 +02:00
iTrooz
980e4cad06 fix: Handle errors in Tar::readVector() (#1059) 2023-05-05 21:57:37 +02:00
WerWolv
5680b90549 fix: Project files failing to save when no custom encoding is loaded 2023-05-04 23:23:44 +02:00
WerWolv
bec655a8c6 impr: Added event logging in debug mode 2023-05-02 20:35:30 +02:00
WerWolv
0c8b3e31e7 fix: Dangerous function call popup not showing up 2023-05-02 20:34:37 +02:00
WerWolv
a33c7135d1 fix: Crash when closing some popups 2023-05-01 14:15:00 +02:00
iTrooz
13a3942f8f impr: Show an error message when ImHex can't open a file (#1050)
This PR does two correlated things:

- Show a generic error message (Failed to open provider) rather than a
file-specific message (Failed to open file) when a provider fails to
open
- Set the error to something more specific when opening a file fails
2023-04-30 18:37:22 +02:00
WerWolv
1bdc1d7241 build: Added CMakePresets.json file to make configuring/building easier 2023-04-30 11:53:51 +02:00
iTrooz
fe9026b68d build: Update my (iTrooz) email in PKGBUILD (#1049) 2023-04-28 18:01:30 +02:00
XorTroll
b0028b0e51 lang: Added Spanish translations (#1042)
This translates ImHex for the Spanish language

(made a new PR since I got a bit messed up on the other one, this was
faster to fix)
2023-04-21 17:44:36 +02:00
WerWolv
f54617e92f patterns: Updated pattern language 2023-04-21 11:02:12 +02:00
WerWolv
306690762c impr: Drastically improve file read speeds 2023-04-21 10:52:10 +02:00
WerWolv
58a0fe8109 patterns: Updated pattern language 2023-04-20 14:32:45 +02:00
WerWolv
1e39f4354f fix: Provider reader being broken 2023-04-20 13:46:44 +02:00
WerWolv
e1b12546da build: Updated dependencies 2023-04-20 10:48:17 +02:00
WerWolv
1b28bf1474 patterns: Updated pattern language
Fixes #1037
2023-04-20 10:41:27 +02:00
WerWolv
8245f3d4c9 build: Updated libwolv 2023-04-19 21:56:34 +02:00
Nik
5fc8e710a5 git: Added vulnearability report info file 2023-04-18 16:15:48 +02:00
Nik
b8636a8589 git: Added Code of Conduct 2023-04-18 16:11:41 +02:00
WerWolv
bb8b4afb85 patterns: Updated pattern language 2023-04-18 10:32:38 +02:00
WerWolv
d5b1ef7875 patterns: Updated pattern language 2023-04-18 10:17:37 +02:00
WerWolv
6c122e5fbe patterns: Updated to new API 2023-04-18 10:06:47 +02:00
WerWolv
7ae814f7fb fix: Various localization issues 2023-04-17 22:18:50 +02:00
WerWolv
bdc51dd8a5 patterns: Updated pattern language 2023-04-17 17:06:52 +02:00
WerWolv
04a5efc7a3 fix: Crash when evaluating patterns to quickly 2023-04-17 17:02:10 +02:00
WerWolv
99a736df27 impr: Replace horrible pattern extra data class with a more modular system 2023-04-17 16:18:48 +02:00
WerWolv
535aeb5e39 impr: Give access to a console in clion in debug mode 2023-04-17 16:06:06 +02:00
WerWolv
07bef10092 patterns: Updated pattern language 2023-04-17 09:22:21 +02:00
WerWolv
c32515bc44 impr: Clean up some more clang-tidy issues 2023-04-16 22:12:35 +02:00
WerWolv
1690cd2740 fix: Various issues with the new popup system 2023-04-16 21:34:29 +02:00
WerWolv
52925c99e8 impr: Default to monitor synchronized FPS limit 2023-04-13 17:12:40 +02:00
WerWolv
1367e9cebe build: Fixed Objective-C compiler flags 2023-04-13 17:07:34 +02:00
WerWolv
3e87022da1 fix: Provider undo stack not being handled correctly 2023-04-13 16:12:00 +02:00
WerWolv
143fe36d35 build: Fixed various build warnings 2023-04-13 16:11:39 +02:00
WerWolv
18d5fd5d3e fix: Commit link in about page not working correctly 2023-04-13 16:10:55 +02:00
WerWolv
2829bf2640 fix: New lines appearing in pattern editor when switching provider 2023-04-13 15:03:50 +02:00
WerWolv
ffafb05d3d fix: Providers with the same name not being selectable in diff view
Fixes #1034
2023-04-13 15:03:14 +02:00
WerWolv
cf72b5ec5c fix: Some shortcuts triggering twice 2023-04-12 19:50:03 +02:00
WerWolv
803b99f2a9 impr: Immensely improve provider read speeds 2023-04-12 19:21:48 +02:00
WerWolv
86b49f34d9 patterns: Updated pattern language
Fixes #1031
2023-04-12 17:18:46 +02:00
WerWolv
7e144b136b patterns: Updated pattern language
Fixes #1030
2023-04-12 16:03:58 +02:00
WerWolv
aa7c5422c0 fix: Make sure provider data is properly cleared when deleting provider 2023-04-11 15:26:18 +02:00
WerWolv
5512585cc5 fix: Make sure docs question request is always properly formatted 2023-04-10 23:05:44 +02:00
WerWolv
013eaae715 fix: Disable multi window support on Linux by default as it causes issues
Fixes #1026
2023-04-10 22:51:21 +02:00
WerWolv
349b5da810 fix: Prevent constants view from growing past the size of the screen
Fixes #1025
2023-04-10 21:30:27 +02:00
WerWolv
e7494be5e7 fix: Last line of patterns going missing in projects with multiple files
Fixes #1012
2023-04-10 16:50:23 +02:00
WerWolv
320629931c fix: Yet another popup centering issue 2023-04-10 14:10:35 +02:00
WerWolv
87d0aae608 feat: Added selection range radio button to various views
Closes #1024
2023-04-10 14:08:21 +02:00
WerWolv
391c8acfe4 impr: Added path tooltips to accept pattern popup 2023-04-10 01:46:55 +02:00
WerWolv
dc77d81e1b feat: Added documentation helper AI 2023-04-10 01:42:53 +02:00
WerWolv
00c9a92977 fix: Localization being broken in the content store 2023-04-10 01:42:28 +02:00
WerWolv
af4dd9f5b0 fix: Popup positioning being wrong 2023-04-09 23:24:48 +02:00
WerWolv
09f1b56964 fix: Popups not being centered correctly 2023-04-09 15:28:48 +02:00
WerWolv
f9a08f5c11 feat: Allow custom alignment to be used in binary sequence search 2023-04-09 15:28:31 +02:00
WerWolv
e79664256a patterns: Updated pattern language 2023-04-09 12:26:13 +02:00
WerWolv
70f3014390 fix: Some more popup rendering issues 2023-04-08 23:34:46 +02:00
WerWolv
b4d0f984a4 build: Updated libwolv 2023-04-08 21:13:19 +02:00
WerWolv
d4ad457af1 fix: Crash when closing provider that was used in view provider 2023-04-08 21:07:57 +02:00
WerWolv
cb5d197700 impr: Added tooltips to icon buttons in bookmarks view 2023-04-08 21:07:41 +02:00
WerWolv
78e66f8959 feat: Added non-ranged and aligned search to sequence and value finder 2023-04-08 20:59:33 +02:00
WerWolv
f562260e42 fix: Build error again 2023-04-08 12:30:38 +02:00
WerWolv
21f38974a8 impr: Use smart pointers to allocate Views 2023-04-08 12:08:45 +02:00
WerWolv
dfca7e923c feat: Make pins in data processor nodes more visible 2023-04-08 11:58:49 +02:00
WerWolv
6913598de4 fix: Build error 2023-04-08 11:58:12 +02:00
WerWolv
ac858b37ed fix: Main Menu items not being clickable at the outer most pixel
Closes #1020
2023-04-08 11:37:42 +02:00
WerWolv
80edaea392 impr: Update all of ImHex to the new popup system 2023-04-08 00:58:53 +02:00
WerWolv
9c9ac23818 feat: Added a much more flexible popup system 2023-04-07 10:21:27 +02:00
WerWolv
51e615095e fix: Make sure placing a type through the Edit menu re-evaluates the pattern 2023-04-06 23:01:45 +02:00
WerWolv
d92e7d19cc fix: Submenu items ignoring enabled flag 2023-04-06 22:33:02 +02:00
WerWolv
248b93f41a patterns: Updated pattern language 2023-04-06 19:23:16 +02:00
WerWolv
c73f33aac2 impr: Added various new events 2023-04-06 17:36:28 +02:00
WerWolv
7c18ad49ae impr: Allow windows that overlap the main window title bar to be movable 2023-04-06 14:58:05 +02:00
WerWolv
5f713882d4 fix: Occasional crash when loading project files 2023-04-06 12:44:25 +02:00
WerWolv
1698f1599b impr: Allow file chooser to open files using double click 2023-04-05 18:33:05 +02:00
WerWolv
24e584c77b fix: Crash when trying to place pattern language variable through the Edit menu
Fixes #1013
2023-04-05 18:29:30 +02:00
WerWolv
a5568d09d8 patterns: Updated pattern language
Fixes #1011
2023-04-05 18:03:30 +02:00
WerWolv
7a4f909c68 fix: Disassembler arch names not correctly corresponding to their ids
Fixes #1010
2023-04-05 18:02:47 +02:00
WerWolv
89aee456c6 patterns: Updated pattern language
Fixes #1011
2023-04-05 07:26:44 +02:00
WerWolv
4cf92103d8 fix: Names of disassembler architectures not corresponding to their actual type
Fixes #1010
2023-04-05 07:20:06 +02:00
WerWolv
e3b1ebb826 patterns: Updated pattern language 2023-04-04 23:42:39 +02:00
WerWolv
3658d8d96e fix: Disassembler not supporting any Capstone 5 features anymore
Fixes #1010
2023-04-04 23:08:10 +02:00
WerWolv
6c047f01f9 git: Fixed release CI not reading version correctly 2023-04-04 21:59:43 +02:00
WerWolv
fc105ecc3d build: Bumped version to 1.28.0 2023-04-04 12:04:22 +02:00
WerWolv
649f6c28bf patterns: Updated pattern language 2023-04-04 11:20:09 +02:00
WerWolv
867972b7f5 patterns: Updated pattern language 2023-04-04 10:23:01 +02:00
WerWolv
efe3227ef2 patterns: Updated pattern language 2023-04-04 09:34:50 +02:00
H1X4
aab8c88a96 feat: allow loading and saving pattern code via events (#1004)
Currently there is no way to save the pattern code progamically from a
plugin unless the builtin plugin is modified to add those events. This
pull request will be adding ability to load and save pattern code from
specified file.
2023-04-01 11:18:52 +02:00
Thomas
af18ca011b fix: Modified bytes visually reverting back after saving (#1003)
Fix #988

Co-authored-by: Nik <werwolv98@gmail.com>
2023-04-01 11:18:03 +02:00
Thomas
24106b860a impr: Added some documentation to providers (#1001) 2023-04-01 11:17:19 +02:00
WerWolv
60efb6973b fix: Filtering of long strings in find view not working correctly 2023-04-01 11:04:07 +02:00
WerWolv
cffd55bdda fix: UTF-16BE search being broken 2023-03-31 22:20:00 +02:00
WerWolv
88e767aaaf fix: Crash when loading big encoding files 2023-03-31 19:56:20 +02:00
WerWolv
d6cda43618 fix: Modified bytes visually reverting back after saving 2023-03-31 19:18:31 +02:00
WerWolv
3b229cd5cb impr: Added path tooltips to entries in the file chooser popup 2023-03-31 19:17:27 +02:00
WerWolv
72c4dbdb2f patterns: Updated pattern language 2023-03-31 13:49:59 +02:00
WerWolv
4da18d3630 fix: Custom encoding and text padding setting not applying to custom encoding column
Actually fixes #1005
2023-03-31 13:49:33 +02:00
WerWolv
2f04cfd5c6 fix: Entering decimal and float values in hex editor cells being broken 2023-03-31 11:34:08 +02:00
WerWolv
8195db6d4c fix: Prevent occasional crash when having ImHex open and connecting to the computer over RDP 2023-03-31 11:08:53 +02:00
WerWolv
722f6315c4 impr: Make sure shortcuts can't be used when popups are open 2023-03-31 11:07:53 +02:00
WerWolv
173ed5475c fix: Remove empty column when ASCII row is off and custom encoding is on
Fixes #1005
2023-03-31 11:07:32 +02:00
WerWolv
1460044e91 impr: Save custom encoding file to project
Fixes #1005
2023-03-31 11:06:51 +02:00
WerWolv
06a7b6e446 patterns: Fixed namespace of hex::prv:: functions 2023-03-28 10:27:49 +02:00
WerWolv
28b7b4b7f1 fix: File -> Open Others... menu not working correctly 2023-03-28 10:13:41 +02:00
WerWolv
8930adf532 patterns: Updated pattern language 2023-03-28 09:29:49 +02:00
WerWolv
f44b8a5618 patterns: Updated pattern language 2023-03-27 22:40:19 +02:00
WerWolv
6a9f79628e impr: Don't try to apply patches if there are none 2023-03-26 12:48:22 +02:00
Jonathan Wright
245c56ba8a build: Added Fedora 38 build (#928)
Co-authored-by: Thomas <hey@itrooz.fr>
2023-03-26 11:37:46 +02:00
WerWolv
98846421f6 build: Update dependencies 2023-03-26 11:23:32 +02:00
WerWolv
0aaeeffff7 build: Fixed capitalization of ImHex in MSI installer 2023-03-26 11:22:50 +02:00
Thomas
c2823facc2 lang: Fix weblate (#997)
This should (hopefully) fix weblate. I tried to fix merge conflicts
using instructions at
https://weblate.werwolv.net/projects/imhex/windows-plugin/#alerts

---------

Co-authored-by: xtex <xtexchooser@duck.com>
2023-03-26 11:03:30 +02:00
Thomas
fabb1596e5 impr: Handle and show NFD errors (#995)
This PR handles errors that NFD might encounter (both in Init() and the
other method to open the dialog), and log them in the logs and in the
GUI

This (among other) fix the crash I had running ImHex as root and opening
a file
2023-03-26 11:02:51 +02:00
Thomas
725e32250b fix: Move config files to XDG_CONFIG_HOME (#993)
This pull request changes Config Directories on Linux to only include
the XDG_CONFIG_HOME directory, as opposed to all directories in
XDG_DATA_DIRS before (introduced in
https://github.com/WerWolv/ImHex/pull/644/files#diff-c1a4d2b63fed168a9a3568944e9cadeae096f2ddcec3649e4a9b2d29fd104be0L162-L166).

Reasons:
- This changes the location of the config file to the standard directory
meant for configurations
- This prevents the config file from being read/written in system
locations, like /usr/share

This PR also includes a migration task that will run on Linux and move
config/GUI dimensions to the new directory

as a bonus, as discussed on discord, it writes the logs to a Data
directory instead of a Config directory
2023-03-26 11:02:23 +02:00
Zaggy1024
5fa264ea18 patterns: Update pattern_language and implement support for new bitfield features (#992)
This requires https://github.com/WerWolv/PatternLanguage/pull/34 to be
merged first, and then this can be amended to update the submodule and
merged to add support for the new features.
2023-03-26 11:01:37 +02:00
WerWolv
5e175b118d build: Updated libwolv 2023-03-25 11:24:24 +01:00
WerWolv
635173e55a impr: Make sure themes are added correctly when downloaded from the store 2023-03-23 20:35:16 +01:00
WerWolv
eb2ed6852c build: Make SSL work when using system curl on WIndows 2023-03-23 20:12:33 +01:00
WerWolv
2296766746 build: Allow building with capstone 4.X again 2023-03-23 16:45:00 +01:00
WerWolv
13be499510 build: Pull in latest version of libyara and libcurl 2023-03-23 16:30:55 +01:00
WerWolv
fec5c567e1 ui: Improve look and feel of content store 2023-03-23 13:32:47 +01:00
WerWolv
8ef863cae1 fix: Progress not working with with new http wrapper 2023-03-23 13:32:35 +01:00
WerWolv
9463105172 fix: Header memory leak in http requests class 2023-03-23 12:08:33 +01:00
WerWolv
bb4819bce4 sys: Fixed http request stack overflow 2023-03-23 11:43:07 +01:00
WerWolv
15be24db62 sys: Updated to use the new HttpRequest helper instead of Net 2023-03-23 11:23:07 +01:00
WerWolv
e7e2af9f91 patterns: Updated pattern language 2023-03-23 09:41:32 +01:00
WerWolv
8c5fd021f7 api: Hook up new http wrapper to the rest of ImHex 2023-03-22 23:05:18 +01:00
WerWolv
1a1ba19770 api: Added new, more flexible curl http wrapper 2023-03-22 21:48:14 +01:00
WerWolv
f95214d8fe patterns: Updated pattern language 2023-03-22 17:43:45 +01:00
WerWolv
631cfce2f8 impr: Added tooltip informing about font size if no custom font was selected 2023-03-22 16:30:49 +01:00
WerWolv
45649264f9 patterns: Updated pattern language 2023-03-22 13:12:57 +01:00
WerWolv
0fd3cb0c4a fix: Don't jump to previous editing position when selecting new region in hex editor
Potenially fixes issues mentioned in #924
2023-03-22 13:11:09 +01:00
Jacob Creedon
3cfec69020 feat: Added additional CRC hash types (#991)
This adds some common CRC types.

---------

Signed-off-by: Jacob Creedon <jcreedon@gmail.com>
2023-03-22 10:53:57 +01:00
WerWolv
cec62d23b0 fix: Window resizing causing freezes in some cases 2023-03-21 22:39:35 +01:00
WerWolv
f3f0dda3d4 fix: Properly clear valid region when switching to a different provider 2023-03-21 16:11:40 +01:00
WerWolv
be16b66ac0 fix: Make sure files don't get truncated when using Save As on itself
Fixes #987
2023-03-21 15:42:10 +01:00
WerWolv
b9059aaa01 fix: Make find process in the find view more easily cancelable 2023-03-21 15:37:49 +01:00
WerWolv
57a62d0544 impr: Clean up entire API and added doc comments 2023-03-21 15:33:43 +01:00
WerWolv
d82f0e952f fix: Custom data inspector rows not being writable correctly 2023-03-21 13:16:22 +01:00
Thomas
8731b7582b impr: Display a more detailed errors when opening a raw disk provider failed (#970)
PR title is self explaining

I may modify other providers implementations to display a detailed error
message later

I'm not sure how to deal with other locales because the format changed.
Before, I had to add and comment the key in all locale files, now I'm
not so sure.
2023-03-21 10:33:00 +01:00
WerWolv
e6959dc572 patterns: Updated pattern language
Fixes #954
2023-03-21 10:31:13 +01:00
WerWolv
a36b4d65e3 build: Make sure commit hash and branch end up in nightly builds 2023-03-21 10:14:09 +01:00
WerWolv
060ff56f9d impr: Improve file reading performance if opening of files is slow 2023-03-21 09:47:42 +01:00
WerWolv
0a0c0c0d07 feat: Added bytes swapper tool 2023-03-20 22:25:27 +01:00
WerWolv
17c4e405a6 impr: Update the command palette for the modern ages 2023-03-20 17:05:26 +01:00
WerWolv
53afa6ea43 fix: Crash on exit 2023-03-20 15:18:48 +01:00
WerWolv
a182e8daf2 patterns: Updated pattern language
Fixes #983
2023-03-20 15:12:27 +01:00
WerWolv
a4dfaba03f fix: All menu item shortcuts being global 2023-03-20 15:12:12 +01:00
WerWolv
6e23560e80 feat: Added all menu items to command palette 2023-03-20 14:11:43 +01:00
WerWolv
39e8d557e8 sys: Completely revamped main menu item system 2023-03-20 13:11:43 +01:00
WerWolv
677c989664 feat: Allow custom data inspector rows to be edited 2023-03-20 08:30:34 +01:00
WerWolv
c9342d90fb fix: Prevent new line from appearing on every pl code save
Fixes #982
2023-03-20 08:29:00 +01:00
WerWolv
0693bb8d51 git: Fixed documentation shield style 2023-03-18 16:10:39 +01:00
WerWolv
5cd3c305d0 git: Added documentation link to readme 2023-03-18 16:09:45 +01:00
WerWolv
e00b59c393 fix: Properly discard reopened stderr 2023-03-18 11:47:42 +01:00
WerWolv
7c1e33dde6 sys: Prevent libraries from printing garbage to the console through stderr 2023-03-18 11:35:01 +01:00
WerWolv
367bd76046 ui: Mae sure all theme scaling values are scaled correctly 2023-03-18 10:52:50 +01:00
WerWolv
1a1bf98905 impr: Look for magic files recursively 2023-03-17 21:18:28 +01:00
WerWolv
4c1a24058c ui: Fixed various scaling inconsistencies on higher scaling factors 2023-03-17 19:58:08 +01:00
WerWolv
294e95caf8 fix: Store page not clearing nodes and themes section correctly 2023-03-17 17:55:39 +01:00
WerWolv
031884c327 patterns: Updated pattern language
Fixes #979
2023-03-17 17:28:17 +01:00
WerWolv
466dacaab4 ui: Improve the look and feel of the information view 2023-03-17 17:07:39 +01:00
WerWolv
1f8645fd43 fix: Occasional crash when multiple threads are reading data from a file provider 2023-03-17 11:43:50 +01:00
WerWolv
880568cc60 impr: Better find view result filter speeds 2023-03-17 11:32:08 +01:00
WerWolv
f10fb56042 impr: Drastically improve file reading performance 2023-03-17 11:31:50 +01:00
WerWolv
64be6d89ee fix: Moving cursor around using arrow keys behaving weirdly 2023-03-17 09:17:44 +01:00
WerWolv
5442c32a3f impr: Allow non-continuous provider values to be saved 2023-03-17 08:38:38 +01:00
WerWolv
4ee53701e6 impr: Allow Regex find strategy specify string type and minimum length 2023-03-17 08:16:13 +01:00
WerWolv
5097a223e3 impr: Added default saveAs implementation for all providers 2023-03-17 08:15:43 +01:00
WerWolv
7cdba75bef fix: Crash when not making a valid selection in provider load interfaces 2023-03-16 16:48:15 +01:00
WerWolv
0312027ca8 impr: Modernize look and feel of bookmarks 2023-03-16 14:40:26 +01:00
WerWolv
c726c96286 impr: Make comment field in bookmark tooltip more readable 2023-03-16 13:35:29 +01:00
WerWolv
5a2b2e0813 feat: Make yara match list sortable 2023-03-16 13:35:09 +01:00
WerWolv
4271b2e9fd fix: Yara view filtering out all but one match 2023-03-14 17:02:59 +01:00
WerWolv
13ef4c04d1 patterns: Updated pattern language
Closes #961
2023-03-14 14:41:32 +01:00
WerWolv
96c3bb1e38 feat: Limit recent files to 5 files, add option to disable saving them
Closes #950
2023-03-14 14:07:18 +01:00
WerWolv
a0b36925ed fix: Custom styles not being scaled correctly 2023-03-14 13:19:04 +01:00
WerWolv
daadc2ea71 impr: Attempt loading of full unicode plane from font if possible 2023-03-14 12:30:28 +01:00
WerWolv
ec2934b4b8 fix: Advancing to next row when editing bytes loading wrong value
Fixes #973
2023-03-14 10:24:25 +01:00
WerWolv
3a840c4ced impr: Properly display custom encoding characters that are split between lines 2023-03-14 09:35:43 +01:00
WerWolv
bd190d2b65 patterns: Updated pattern language 2023-03-13 11:36:11 +01:00
qux-bbb
9b05a36529 fix: Find view string filters filtering for some wrong characters (#972)
`\r` and `\n` need to be filtered.
2023-03-13 11:06:30 +01:00
WerWolv
d9a498e8ec build: Make sure libwolv is compiled with -fPIC 2023-03-13 10:39:34 +01:00
WerWolv
7d86b277a7 build: Updated curl and libyara 2023-03-13 10:24:56 +01:00
WerWolv
d463b3235e tests: Fixed building of tests 2023-03-13 10:24:48 +01:00
WerWolv
5a8433ede4 build: Updated libwolv 2023-03-13 09:31:24 +01:00
WerWolv
00a5fd2d7c sys: Fixed more build issues 2023-03-13 09:25:07 +01:00
WerWolv
55f9faea10 sys: Updated more code to libwolv 2023-03-13 08:58:08 +01:00
WerWolv
fb2e668589 sys: Moved more functions to libwolv 2023-03-12 18:43:05 +01:00
WerWolv
0dafb3d230 sys: Replaced many helper functions with libwolv 2023-03-12 18:27:33 +01:00
qux-bbb
e958934a22 fix: String search not including string at end of data (#963)
Before:  

![before](https://user-images.githubusercontent.com/18598419/222937056-fec74305-21a3-4bbf-a439-e8df7031bca9.png)

After:  

![after](https://user-images.githubusercontent.com/18598419/222937069-a04cb748-4266-4fbb-8182-727bb8858329.png)
2023-03-11 14:39:50 +01:00
Fenrisfulsur
069221757f feat: Added chunk based entropy analysis to information view (#933)
Issue: https://github.com/WerWolv/ImHex/issues/522

Implementation of chunk based entropy analysis in diagram.hpp available
from the data information view and in the pattern language.

---------

Co-authored-by: WerWolv <werwolv98@gmail.com>
2023-03-10 16:06:18 +01:00
WerWolv
505c1bc274 build: Fix Fedora build errors 2023-03-10 14:46:15 +01:00
qux-bbb
cdd5d33e89 feat: Make CTRL + N automatically create a memory provider (#966)
"CTRL + N" can open a mem_file directly.
2023-03-07 16:05:03 +01:00
WerWolv
f661f4d1d6 fix: GDB provider not working nicely anymore 2023-03-07 16:04:04 +01:00
Mimi1337
f435191585 fix: Dockerfile syntax error (#945) 2023-02-21 11:29:21 +01:00
WerWolv
00c2d7ea71 patterns: Updated pattern language 2023-02-20 11:35:33 +01:00
WerWolv
cddcc1e85d patterns: Updated pattern language 2023-02-19 10:49:57 +01:00
WerWolv
91928b45d8 fix: Try to fix build again 2023-02-19 10:25:39 +01:00
WerWolv
277c83e6d8 fix: Uninitialized value build issue 2023-02-19 09:18:17 +01:00
WerWolv
0017cd2e40 feat: Added binary hex cell visualizer
Closes #939
2023-02-18 22:20:02 +01:00
WerWolv
774803492c fix: Editing binary value in data inspector not working correctly
Fixes #941
2023-02-18 21:44:43 +01:00
WerWolv
83a9655772 patterns: Updated pattern language 2023-02-17 20:30:41 +01:00
WerWolv
58324b4539 build: Fixed linking against execinfo 2023-02-17 17:56:27 +01:00
WerWolv
09b7794d71 build: Added option to disable stack traces 2023-02-17 17:52:10 +01:00
WerWolv
9e3fe9beb1 patterns: Updated pattern language 2023-02-17 17:35:41 +01:00
WerWolv
ff525fe750 impr: Properly sort choose file dialog entries
Fixes #938
2023-02-17 14:59:19 +01:00
WerWolv
94977ad216 patterns: Updated pattern language 2023-02-17 14:55:56 +01:00
WerWolv
64e34e42b8 patterns: Fixed highlighting of custom sections 2023-02-17 14:53:15 +01:00
WerWolv
21dc65f42a impr: Added comments everywhere in main application 2023-02-17 12:03:53 +01:00
WerWolv
279e085887 impr: Don't create a new font texture for no reason 2023-02-17 12:03:00 +01:00
WerWolv
d84dfa2c42 feat: Allow custom CA Certs to be loaded
Fixes #907
2023-02-17 12:01:46 +01:00
WerWolv
bf8089dc7e impr: Don't memory map files, never keep a write handle open for long
Closes #592
2023-02-17 10:26:09 +01:00
WerWolv
e48761b5c0 fix: Plot lines being invisible 2023-02-17 10:02:43 +01:00
WerWolv
35437c0300 patterns: Added rotation and scale sliders to 3D visualizer 2023-02-16 23:24:24 +01:00
WerWolv
6cecc12d04 patterns: Updated pattern language 2023-02-16 22:20:03 +01:00
WerWolv
dec4231d49 impr: Make sure fonts don't get blurry on non-integer scalings 2023-02-16 20:53:58 +01:00
WerWolv
fb1d12ebf3 sys: Remove some problematic (and rarely used) scaling settings 2023-02-16 20:19:55 +01:00
WerWolv
b19276a3e9 patterns: Added match keyword to syntax highlighting 2023-02-16 19:10:08 +01:00
WerWolv
5ccbfc1ff8 feat: Allow themes and nodes to be downloaded from the content store 2023-02-16 18:55:21 +01:00
WerWolv
d3d6a8a838 patterns: Updated pattern language 2023-02-16 18:23:49 +01:00
WerWolv
ac83bbeb0e feat: Added a theme manager view to make it easier to make new themes 2023-02-16 18:06:40 +01:00
WerWolv
8cb76a26c1 fix: Font being scaled too big 2023-02-16 16:29:41 +01:00
WerWolv
20da22d59e sys: Make sure scrolling counts as an input event 2023-02-16 08:53:23 +01:00
WerWolv
d9fa4b452c impr: Clean up diff view 2023-02-16 08:53:05 +01:00
WerWolv
6216d72aa6 fix: Format string compile issues 2023-02-15 22:22:13 +01:00
WerWolv
851f132188 impr: Make Text Editor word selector also consider underlines 2023-02-15 17:12:16 +01:00
WerWolv
3067ff08ec feat: Greatly improved diff view
Fixes #631
2023-02-15 17:01:36 +01:00
WerWolv
07fd86fc9d git: Split release workflows into multiple steps 2023-02-15 10:21:56 +01:00
WerWolv
504037c115 build: Bumped version to 1.27.1 2023-02-15 09:39:23 +01:00
WerWolv
0fad21a980 patterns: Updated pattern language 2023-02-15 09:39:16 +01:00
WerWolv
8afd698284 impr: Correct some ugly code 2023-02-14 15:43:44 +01:00
WerWolv
9ec7b90192 patterns: Updated pattern language 2023-02-14 15:10:52 +01:00
WerWolv
08f0fff34b patterns: Updated pattern language 2023-02-14 12:53:37 +01:00
WerWolv
2c1073eda9 fix: Safety backup restore popup getting hidden by tip of the day 2023-02-14 11:45:32 +01:00
WerWolv
c9348f0651 patterns: Updated pattern language 2023-02-13 23:27:12 +01:00
WerWolv
accb461c08 impr: Better word select and delete in text editor
Closes #931
2023-02-13 10:21:57 +01:00
Thomas
75adcc0a96 build: Fix Arch package name in release CI (#929)
* Fix Arch package name in release CI

* update email address
2023-02-13 09:05:52 +01:00
WerWolv
61ce88ba9b build: Fix build on systems that have no backtrace or execinfo
Fixes #932
2023-02-13 08:27:08 +01:00
WerWolv
4b451fd1d3 patterns: Fixed pattern data rows not being selectable when color column is hidden 2023-02-13 08:26:43 +01:00
WerWolv
a87190960f build: Fixed one more missing imhex target name 2023-02-12 22:08:35 +01:00
WerWolv
45558027a6 git: Fixed release workflow 2023-02-12 21:55:22 +01:00
WerWolv
e426606542 build: Fixed flatpak build issues 2023-02-12 21:55:09 +01:00
219 changed files with 13291 additions and 7214 deletions

68
.clang-tidy Normal file
View File

@@ -0,0 +1,68 @@
# Generated from CLion Inspection settings
---
Checks: '-*,
mpi-*,
bugprone-*,
-bugprone-signal-handler,
-bugprone-narrowing-conversions,
-bugprone-redundant-branch-condition,
-bugprone-exception-escape,
-bugprone-shared-ptr-array-mismatch,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-signed-char-misuse,
-bugprone-unhandled-exception-at-new,
-bugprone-infinite-loop,
-bugprone-easily-swappable-parameters,
cert-err52-cpp,
cert-err60-cpp,
cert-err34-c,
cert-str34-c,
cert-dcl21-cpp,
cert-msc50-cpp,
cert-msc51-cpp,
cert-dcl58-cpp,
cert-flp30-c,
cppcoreguidelines-avoid-const-or-ref-data-members,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-slicing,
cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-narrowing-conversions,
google-default-arguments,
google-runtime-operator,
google-explicit-constructor,
hicpp-multiway-paths-covered,
hicpp-exception-baseclass,
misc-*,
-misc-definitions-in-headers,
-misc-unused-parameters,
-misc-unused-alias-decls,
-misc-use-anonymous-namespace,
-misc-misleading-identifier,
-misc-confusable-identifiers,
-misc-misleading-bidirectional,
-misc-static-assert,
-misc-no-recursion,
-misc-const-correctness,
modernize-*,
-modernize-use-trailing-return-type,
openmp-use-default-none,
performance-*,
-performance-no-int-to-ptr,
portability-*,
-portability-restrict-system-includes,
readability-*,
-readability-redundant-preprocessor,
-readability-named-parameter,
-readability-function-size,
-readability-use-anyofallof,
-readability-identifier-length,
-readability-magic-numbers,
-readability-braces-around-statements,
-readability-suspicious-call-argument,
-readability-isolate-declaration,
-readability-else-after-return,
-readability-redundant-access-specifiers,
-readability-function-cognitive-complexity,
-readability-identifier-naming,
-readability-qualified-auto'

View File

@@ -70,19 +70,22 @@ jobs:
mkdir -p build
cd build
cmake -G "MinGW Makefiles" \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
-DCREATE_PACKAGE=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
cmake -G "MinGW Makefiles" \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
-DCREATE_PACKAGE=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
..
mingw32-make -j4 install
cpack
mv imhex-*.msi ../imhex-${{env.IMHEX_VERSION}}-Windows-x86_64.msi
mv ImHex-*.msi ../imhex-${{env.IMHEX_VERSION}}-Windows-x86_64.msi
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
@@ -179,12 +182,15 @@ jobs:
mkdir build
cd build
cmake \
-DBUILD_SHARED_LIBS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
cmake \
-DBUILD_SHARED_LIBS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
..
make -j 4 install
@@ -193,23 +199,26 @@ jobs:
run: |
mkdir -p build
cd build
CC=$(brew --prefix gcc@12)/bin/gcc-12 \
CXX=$(brew --prefix gcc@12)/bin/g++-12 \
OBJC=$(brew --prefix llvm)/bin/clang \
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \
MACOSX_DEPLOYMENT_TARGET="10.10" \
cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCREATE_BUNDLE=ON \
-DCREATE_PACKAGE=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DCMAKE_OSX_DEPLOYMENT_TARGET="10.10" \
-DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64" \
CC=$(brew --prefix gcc@12)/bin/gcc-12 \
CXX=$(brew --prefix gcc@12)/bin/g++-12 \
OBJC=$(brew --prefix llvm)/bin/clang \
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \
MACOSX_DEPLOYMENT_TARGET="10.10" \
cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCREATE_BUNDLE=ON \
-DCREATE_PACKAGE=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="10.10" \
-DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64" \
..
make -j4 package
@@ -254,13 +263,16 @@ jobs:
run: |
mkdir -p build
cd build
CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
..
make -j 4 install DESTDIR=DebDir
@@ -324,15 +336,18 @@ jobs:
run: |
mkdir -p build-appimage
cd build-appimage
CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
-DIMHEX_PLUGINS_IN_SHARE=ON \
-DIMHEX_USE_BUNDLED_CA=ON \
CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
-DIMHEX_PLUGINS_IN_SHARE=ON \
-DIMHEX_USE_BUNDLED_CA=ON \
..
make -j 4 install DESTDIR=AppDir
@@ -399,18 +414,21 @@ jobs:
run: |
mkdir -p build
cd build
CC=gcc CXX=g++ cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DUSE_SYSTEM_CURL=ON \
-DUSE_SYSTEM_FMT=ON \
-DUSE_SYSTEM_YARA=ON \
-DUSE_SYSTEM_NLOHMANN_JSON=ON \
-DUSE_SYSTEM_CAPSTONE=OFF \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
CC=gcc CXX=g++ cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DUSE_SYSTEM_CURL=ON \
-DUSE_SYSTEM_FMT=ON \
-DUSE_SYSTEM_YARA=ON \
-DUSE_SYSTEM_NLOHMANN_JSON=ON \
-DUSE_SYSTEM_CAPSTONE=OFF \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
..
make -j 4 install DESTDIR=installDir
@@ -456,6 +474,10 @@ jobs:
mock_release: rawhide
release_num: rawhide
mock_config: fedora-rawhide
- name: Fedora
mock_release: f38
release_num: 38
mock_config: fedora-38
- name: Fedora
mock_release: f37
release_num: 37
@@ -515,7 +537,7 @@ jobs:
- name: 🗜️ Create tarball from sources with dependencies
run: tar --exclude-vcs -czf $GITHUB_WORKSPACE/imhex-$IMHEX_VERSION.tar.gz ImHex
- name: "✒️ Modify spec file: set version, use latest pattern language, enable online build"
- name: ✒️ Modify spec file
run: |
sed -i \
-e 's/Version: [0-9]*\.[0-9]*\.[0-9]*$/Version: ${{env.IMHEX_VERSION}}/g' \
@@ -546,9 +568,9 @@ jobs:
uses: actions/cache@v3
with:
path: /var/cache/mock
key: ${{ matrix.mock_release }}-${{secrets.CACHE_VERSION }}-mock-${{ github.run_id }}
key: ${{ matrix.mock_release }}-${{ secrets.CACHE_VERSION }}-mock-${{ github.run_id }}
restore-keys: |
${{ matrix.mock_release }}-${{secrets.CACHE_VERSION }}-mock-
${{ matrix.mock_release }}-${{ secrets.CACHE_VERSION }}-mock-
# Fedora cmake build (in imhex.spec)
- name: 📦 Build RPM

View File

@@ -9,9 +9,66 @@ on:
workflow_dispatch:
jobs:
release-common:
release-update-repos:
runs-on: ubuntu-latest
name: Release Common
name: Release Update Repos
steps:
- name: 📜 Verify version and set version variable
run: |
project_version=`cat ImHex/VERSION`
tag_version="${{github.event.release.tag_name}}"
tag_version="${tag_version:1}"
if [ "$project_version" != "$tag_version" ]; then
echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release"
exit 1
fi
echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV
- name: 🎫 Create PatternLanguage release
uses: ncipollo/release-action@v1
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
tag: ImHex-v${{ env.IMHEX_VERSION }}
repo: PatternLanguage
token: ${{ secrets.RELEASE_TOKEN }}
- name: 🎫 Create ImHex-Patterns release
uses: ncipollo/release-action@v1
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
tag: ImHex-v${{ env.IMHEX_VERSION }}
repo: ImHex-Patterns
token: ${{ secrets.RELEASE_TOKEN }}
- name: ✉️ Update C++ Plugin Template
uses: peter-evans/repository-dispatch@v2
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
token: ${{ secrets.RELEASE_TOKEN }}
repository: WerWolv/ImHex-Cpp-Plugin-Template
event-type: update_submodule
- name: ✉️ Update Rust Plugin Template
uses: peter-evans/repository-dispatch@v2
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
token: ${{ secrets.RELEASE_TOKEN }}
repository: WerWolv/ImHex-Rust-Plugin-Template
event-type: update_submodule
release-upload-artifacts:
runs-on: ubuntu-latest
name: Release Upload Artifacts
steps:
- name: 🧰 Checkout
@@ -60,8 +117,8 @@ jobs:
- name: 🟩 Rename artifacts when needed
run: |
mv "Windows Portable x86_64.zip" imhex-${{env.IMHEX_VERSION}}-Windows-Portable-x86_64.zip
mv "Windows Portable NoGPU x86_64.zip" imhex-${{env.IMHEX_VERSION}}-Windows-Portable-NoGPU-x86_64.zip
mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
- name: ⬆️ Upload everything to release
uses: softprops/action-gh-release@v1
@@ -71,15 +128,14 @@ jobs:
- name: ✒️ Prepare PKGBUILD
run: |
cp ImHex/dist/Arch/PKGBUILD .
hash=`md5sum imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst | cut -d ' ' -f 1`
hash=`md5sum imhex-${{env.IMHEX_VERSION}}-ArchLinux.pkg.tar.zst-x86_64 | cut -d ' ' -f 1`
sed -i 's/%version%/${{env.IMHEX_VERSION}}/g' PKGBUILD
sed -i 's/%version%/${{ env.IMHEX_VERSION }}/g' PKGBUILD
sed -i "s/(SKIP)/($hash)/g" PKGBUILD
- name: ⬆️ Publish AUR package
# I couldn't make the condition in the env directly for some reason
env:
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
if: "${{ env.AUR_SSH_PRIVATE_KEY != '' }}"
@@ -91,58 +147,18 @@ jobs:
commit_username: iTrooz
commit_email: itrooz@protonmail.com
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message: Bump to version ${{env.IMHEX_VERSION}}
commit_message: Bump to version ${{ env.IMHEX_VERSION }}
ssh_keyscan_types: rsa,dsa,ecdsa,ed25519
- name: 🎫 Create PatternLanguage release
uses: ncipollo/release-action@v1
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
tag: ImHex-v${{env.IMHEX_VERSION}}
repo: PatternLanguage
token: ${{ secrets.RELEASE_TOKEN }}
- name: 🎫 Create ImHex-Patterns release
uses: ncipollo/release-action@v1
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
tag: ImHex-v${{env.IMHEX_VERSION}}
repo: ImHex-Patterns
token: ${{ secrets.RELEASE_TOKEN }}
- name: ✉️ Update C++ Plugin Template
uses: peter-evans/repository-dispatch@v2
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
token: ${{ secrets.RELEASE_TOKEN }}
repository: WerWolv/ImHex-Cpp-Plugin-Template
event_type: update_submodule
- name: ✉️ Update Rust Plugin Template
uses: peter-evans/repository-dispatch@v2
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
token: ${{ secrets.RELEASE_TOKEN }}
repository: WerWolv/ImHex-Rust-Plugin-Template
event_type: update_submodule
release-windows:
name: Release Windows
needs: release-common
runs-on: windows-2022
release-update-winget:
name: Release update winget package
needs: release-upload-artifacts
runs-on: windows-latest
steps:
- name: ⬇️ Download dependencies
shell: pwsh
run: |
iwr https://github.com/microsoft/winget-create/releases/download/v1.1.2.0/wingetcreate.exe -OutFile wingetcreate.exe
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
- name: ⬆️ Update winget manifest
shell: pwsh
env:

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ venv/
*.mgc
imgui.ini
.DS_Store
./CMakeUserPresets.json

3
.gitmodules vendored
View File

@@ -29,3 +29,6 @@
[submodule "lib/external/pattern_language"]
path = lib/external/pattern_language
url = https://github.com/WerWolv/PatternLanguage
[submodule "lib/external/libwolv"]
path = lib/external/libwolv
url = https://github.com/WerWolv/libwolv

View File

@@ -9,6 +9,8 @@ option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF)
option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF)
option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF)
option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals" OFF)
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
option(IMHEX_DISABLE_UPDATE_CHECK "Disables built-in update check" OFF)
# Basic compiler and cmake configurations
set(CMAKE_CXX_STANDARD 23)
@@ -40,7 +42,7 @@ set(PLUGINS
# Add various defines
detectOS()
detectArch()
addVersionDefines()
addDefines()
configurePackingResources()
setUninstallTarget()
addBundledLibraries()
@@ -48,7 +50,7 @@ addBundledLibraries()
# Add ImHex sources
add_subdirectory(lib/libimhex)
add_subdirectory(main)
add_custom_target(imhex ALL DEPENDS main libimhex)
add_custom_target(imhex_all ALL DEPENDS main libimhex)
# Add unit tests
enable_testing()

47
CMakePresets.json Normal file
View File

@@ -0,0 +1,47 @@
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "base",
"displayName": "Base",
"description": "Base configuration for all builds",
"hidden": true,
"binaryDir": "${sourceDir}/build/${presetName}",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_C_COMPILER_LAUNCHER": "ccache",
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
"CMAKE_C_FLAGS": "-fuse-ld=lld",
"CMAKE_CXX_FLAGS": "-fuse-ld=lld",
"IMHEX_PATTERNS_PULL_MASTER": "ON",
"CMAKE_INSTALL_PREFIX": "./install",
"USE_SYSTEM_CURL": "ON"
}
},
{
"name": "x86_64",
"displayName": "x86_64 Build",
"description": "x86_64 build",
"inherits": [ "base" ]
}
],
"buildPresets": [
{
"name": "x86_64",
"description": "x86_64 build",
"configurePreset": "x86_64",
"targets": [ "imhex_all" ]
}
],
"testPresets": [
]
}

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
Discord @WerWolv#1337.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -25,6 +25,9 @@
<a title="Translation" href="https://weblate.werwolv.net/projects/imhex/">
<img alt="Translation" src="https://img.shields.io/weblate/progress/imhex?logo=weblate&logoColor=%23FFFFFF&server=https%3A%2F%2Fweblate.werwolv.net&style=for-the-badge">
</a>
<a title="Documentation" href="https://imhex.werwolv.net/docs">
<img alt="Documentation" src="https://img.shields.io/badge/Docs-Available-brightgreen?logo=gitbook&logoColor=%23FFFFFF&style=for-the-badge">
</a>
</p>
## Supporting

10
SECURITY.md Normal file
View File

@@ -0,0 +1,10 @@
# Security Policy
## Supported Versions
Supported is generally only the latest version that can be downloaded from the Release tab. If you have issues, you might get instructed to use the Nightly release version instead.
If you built ImHex yourself and experience issues that are not present in the version built by GitHub, you're on your own.
## Reporting a Vulnerability
Any critical vulnearabilities can be reported through Discord @WerWolv#1337

View File

@@ -1 +1 @@
1.27.0
1.29.0

View File

@@ -1,37 +1,58 @@
include(FetchContent)
if(IMHEX_STRIP_RELEASE)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(CPACK_STRIP_FILES TRUE)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_link_options($<$<CONFIG:RELEASE>:-s>)
endif()
endif()
macro(addVersionDefines)
macro(addDefines)
if (NOT IMHEX_VERSION)
message(FATAL_ERROR "IMHEX_VERSION is not defined")
endif ()
if (IS_DIRECTORY "${CMAKE_SOURCE_DIR}/.git")
if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_HASH_SHORT AND DEFINED IMHEX_COMMIT_BRANCH)
add_compile_definitions(
GIT_COMMIT_HASH_LONG="${IMHEX_COMMIT_HASH_LONG}"
IMHEX_COMMIT_HASH_SHORT="${IMHEX_COMMIT_HASH_SHORT}"
GIT_BRANCH="${IMHEX_COMMIT_BRANCH}"
)
else()
# Get the current working branch
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE RESULT_BRANCH
)
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND git log -1 --format=%h
COMMAND git log -1 --format=%h --abbrev=7
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_VARIABLE GIT_COMMIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE RESULT_HASH_SHORT
)
add_compile_definitions(GIT_COMMIT_HASH="${GIT_COMMIT_HASH}" GIT_BRANCH="${GIT_BRANCH}")
execute_process(
COMMAND git log -1 --format=%H
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH_LONG
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE RESULT_HASH_LONG
)
if (RESULT_BRANCH EQUAL 0 AND RESULT_HASH_LONG EQUAL 0 AND RESULT_HASH_SHORT EQUAL 0)
add_compile_definitions(
GIT_COMMIT_HASH_SHORT="${GIT_COMMIT_HASH_SHORT}"
GIT_COMMIT_HASH_LONG="${GIT_COMMIT_HASH_LONG}"
GIT_BRANCH="${GIT_BRANCH}")
endif ()
endif ()
set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} -DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR} -DPROJECT_VERSION_PATCH=${PROJECT_VERSION_PATCH} ")
@@ -39,17 +60,23 @@ macro(addVersionDefines)
set(IMHEX_VERSION_STRING ${IMHEX_VERSION})
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING})
add_compile_definitions(NDEBUG)
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-Debug)
add_compile_definitions(DEBUG _GLIBCXX_DEBUG _GLIBCXX_VERBOSE)
elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-RelWithDebInfo)
add_compile_definitions(NDEBUG)
elseif (CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-MinSizeRel)
add_compile_definitions(NDEBUG)
endif ()
add_compile_definitions(IMHEX_VERSION="${IMHEX_VERSION_STRING}")
if (NOT IMHEX_DISABLE_UPDATE_CHECK)
add_compile_definitions(HEX_UPDATE_CHECK)
endif()
endmacro()
# Detect current OS / System
@@ -60,7 +87,9 @@ macro(detectOS)
set(CMAKE_INSTALL_LIBDIR ".")
set(PLUGINS_INSTALL_LOCATION "plugins")
SET(IMHEX_USE_BUNDLED_CA ON)
if (NOT USE_SYSTEM_CURL)
SET(IMHEX_USE_BUNDLED_CA ON)
endif ()
elseif (APPLE)
add_compile_definitions(OS_MACOS)
set(CMAKE_INSTALL_BINDIR ".")
@@ -114,7 +143,7 @@ macro(configurePackingResources)
if (CREATE_PACKAGE)
set(CPACK_GENERATOR "WIX")
set(CPACK_PACKAGE_NAME "imhex")
set(CPACK_PACKAGE_NAME "ImHex")
set(CPACK_PACKAGE_VENDOR "WerWolv")
set(CPACK_WIX_UPGRADE_GUID "05000E99-9659-42FD-A1CF-05C554B39285")
set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/dist/windows/icon.ico")
@@ -139,7 +168,7 @@ macro(configurePackingResources)
set(MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/dist/macos/Info.plist.in")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.WerWolv.ImHex")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${GIT_COMMIT_HASH}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${GIT_COMMIT_HASH_SHORT}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
string(TIMESTAMP CURR_YEAR "%Y")
@@ -185,7 +214,7 @@ macro(createPackage)
endif ()
endif ()
add_dependencies(imhex ${plugin})
add_dependencies(imhex_all ${plugin})
endif ()
endforeach()
@@ -256,7 +285,7 @@ macro(createPackage)
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
# Fix rpath
add_custom_command(TARGET imhex POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>)
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>)
# 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")
@@ -269,7 +298,7 @@ macro(createPackage)
install(FILES $<TARGET_FILE:main> DESTINATION "${IMHEX_BUNDLE_PATH}")
# Update library references to make the bundle portable
postprocess_bundle(imhex main)
postprocess_bundle(imhex_all main)
# Enforce DragNDrop packaging.
set(CPACK_GENERATOR "DragNDrop")
@@ -384,7 +413,7 @@ endfunction()
macro(setupCompilerWarnings target)
set(IMHEX_COMMON_FLAGS "-Wall -Wextra -Wpedantic -Werror")
set(IMHEX_C_FLAGS "${IMHEX_COMMON_FLAGS} -Wno-restrict -Wno-stringop-overread -Wno-stringop-overflow")
set(IMHEX_C_FLAGS "${IMHEX_COMMON_FLAGS} -Wno-restrict -Wno-stringop-overread -Wno-stringop-overflow -Wno-array-bounds -Wno-dangling-reference")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${IMHEX_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${IMHEX_C_FLAGS}")
@@ -416,8 +445,13 @@ macro(addBundledLibraries)
add_subdirectory(${EXTERN_LIBS_FOLDER}/microtar EXCLUDE_FROM_ALL)
set_target_properties(microtar PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${EXTERN_LIBS_FOLDER}/intervaltree EXCLUDE_FROM_ALL)
set_target_properties(intervaltree PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${EXTERN_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(XDGPP_INCLUDE_DIRS "${EXTERN_LIBS_FOLDER}/xdgpp")
set(CURL_USE_MBEDTLS ON)
@@ -459,6 +493,7 @@ macro(addBundledLibraries)
if(NOT USE_SYSTEM_CURL)
add_subdirectory(${EXTERN_LIBS_FOLDER}/curl EXCLUDE_FROM_ALL)
set_target_properties(libcurl PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_options(libcurl PRIVATE -Wno-deprecated-declarations)
set(LIBCURL_LIBRARIES libcurl)
else()
find_package(PkgConfig REQUIRED)
@@ -515,25 +550,27 @@ macro(addBundledLibraries)
set(MAGIC_INCLUDE_DIRS ${MAGIC_INCLUDEDIR})
endif()
if (WIN32)
message(STATUS "StackWalk enabled!")
set(LIBBACKTRACE_LIBRARIES DbgHelp.lib)
else ()
find_package(Backtrace)
if (${Backtrace_FOUND})
message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}")
if (NOT IMHEX_DISABLE_STACKTRACE)
if (WIN32)
message(STATUS "StackWalk enabled!")
set(LIBBACKTRACE_LIBRARIES DbgHelp.lib)
else ()
find_package(Backtrace)
if (${Backtrace_FOUND})
message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}")
if (Backtrace_HEADER STREQUAL "execinfo.h")
set(LIBBACKTRACE_LIBRARIES)
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(HEX_HAS_EXECINFO)
elseif (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES backtrace)
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(HEX_HAS_BACKTRACE)
endif ()
endif()
if (Backtrace_HEADER STREQUAL "execinfo.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(HEX_HAS_EXECINFO)
elseif (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(HEX_HAS_BACKTRACE)
endif ()
endif()
endif ()
endif ()
endmacro()

2
dist/Arch/PKGBUILD vendored
View File

@@ -1,4 +1,4 @@
# Maintainer: iTrooz_ <itrooz at protonmail dot com>
# Maintainer: iTrooz_ <aur at itrooz dot fr>
# Contributor: Morten Linderud <foxboron@archlinux.org>
pkgname=imhex-bin

2
dist/Dockerfile vendored
View File

@@ -1,6 +1,6 @@
FROM archlinux:latest
LABEL maintainer="hey@werwolv.net" = WerWolv
LABEL maintainer="hey@werwolv.net WerWolv"
# Install dependencies
RUN pacman -Syy --needed --noconfirm

8
dist/rpm/imhex.spec vendored
View File

@@ -4,7 +4,7 @@ Release: 0%{?dist}
Summary: A hex editor for reverse engineers and programmers
License: GPL-2.0-only AND Zlib AND MIT AND Apache-2.0
# imhex is gplv2. capstone is custom. nativefiledialog is Zlib. intervaltree is MIT
# imhex is gplv2. capstone is custom. nativefiledialog is Zlib.
# see license dir for full breakdown
URL: https://imhex.werwolv.net/
# We need the archive with deps bundled
@@ -36,9 +36,6 @@ Provides: bundled(imgui)
Provides: bundled(libromfs)
Provides: bundled(microtar)
Provides: bundled(libpl)
# ImHex modified upstream intervaltree so we have to package it
# https://github.com/ekg/intervaltree
Provides: bundled(intervaltree) = 0.1
Provides: bundled(xdgpp)
# ftbfs on these arches. armv7hl might compile when capstone 5.x
@@ -115,7 +112,6 @@ cp -a lib/external/nativefiledialog/LICENSE %{buildroot}%{
cp -a lib/external/capstone/LICENSE.TXT %{buildroot}%{_datadir}/licenses/%{name}/capstone-LICENSE
cp -a lib/external/capstone/suite/regress/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/capstone-regress-LICENSE
cp -a lib/external/microtar/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/microtar-LICENSE
cp -a lib/external/pattern_language/external/intervaltree/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/pattern-language-intervaltree-LICENSE
cp -a lib/external/xdgpp/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/xdgpp-LICENSE
@@ -131,5 +127,3 @@ cp -a lib/external/xdgpp/LICENSE %{buildroot}%{
%changelog
* Thu Jan 01 1970 ImHex GitHub CI - 0.0.0-0
- Build Package

View File

@@ -33,10 +33,9 @@ add_library(imgui OBJECT
source/fonts/unifont_font.c
)
add_compile_definitions(IMGUI_IMPL_OPENGL_LOADER_GLAD)
target_compile_definitions(imgui PUBLIC IMGUI_IMPL_OPENGL_LOADER_GLAD)
target_compile_options(imgui PRIVATE -Wno-stringop-overflow)
target_include_directories(imgui PUBLIC include ${FREETYPE_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS} ${OpenGL_INCLUDE_DIRS})
target_link_directories(imgui PUBLIC ${GLFW_LIBRARY_DIRS} ${OpenGL_LIBRARY_DIRS})
target_link_libraries(imgui PUBLIC Freetype::Freetype ${GLFW_LIBRARIES} ${OPENGL_LIBRARIES})

View File

@@ -314,7 +314,7 @@ TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates &aFrom) cons
while (cindex > 0 && isspace(line[cindex].mChar))
--cindex;
auto cstart = (PaletteIndex)line[cindex].mColorIndex;
auto cstart = line[cindex].mChar;
while (cindex > 0) {
auto c = line[cindex].mChar;
if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx
@@ -323,8 +323,15 @@ TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates &aFrom) cons
cindex++;
break;
}
if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex)
if (isalnum(cstart) || cstart == '_') {
if (!isalnum(c) && c != '_') {
cindex++;
break;
}
} else {
break;
}
}
--cindex;
}
@@ -632,10 +639,24 @@ void TextEditor::HandleKeyboardInputs() {
MoveEnd(shift);
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
Delete();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
auto wordStart = GetCursorPosition();
MoveRight();
auto wordEnd = FindWordEnd(GetCursorPosition());
SetSelection(wordStart, wordEnd);
Backspace();
}
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
Backspace();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) {
auto wordEnd = GetCursorPosition();
MoveLeft();
auto wordStart = FindWordStart(GetCursorPosition());
SetSelection(wordStart, wordEnd);
Backspace();
}
else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
mOverwrite ^= true;
mOverwrite = !mOverwrite;
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
Copy();
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))

View File

@@ -750,12 +750,41 @@ static void ImGui_ImplGlfw_UpdateMouseCursor()
else
{
// IMHEX PATCH BEGIN
if (!bd->BorderlessWindow) {
/*if (!bd->BorderlessWindow) {*/
// Show OS mouse cursor
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
#if defined(_WIN32)
switch (imgui_cursor) {
case ImGuiMouseCursor_Hand:
SetCursor(LoadCursor(nullptr, IDC_HAND));
break;
case ImGuiMouseCursor_ResizeEW:
SetCursor(LoadCursor(nullptr, IDC_SIZEWE));
break;
case ImGuiMouseCursor_ResizeNS:
SetCursor(LoadCursor(nullptr, IDC_SIZENS));
break;
case ImGuiMouseCursor_ResizeNWSE:
SetCursor(LoadCursor(nullptr, IDC_SIZENWSE));
break;
case ImGuiMouseCursor_ResizeNESW:
SetCursor(LoadCursor(nullptr, IDC_SIZENESW));
break;
case ImGuiMouseCursor_ResizeAll:
SetCursor(LoadCursor(nullptr, IDC_SIZEALL));
break;
case ImGuiMouseCursor_NotAllowed:
SetCursor(LoadCursor(nullptr, IDC_NO));
break;
case ImGuiMouseCursor_TextInput:
SetCursor(LoadCursor(nullptr, IDC_IBEAM));
break;
}
#else
glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
#endif
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
/*}*/
// IMHEX PATCH END
}
}
@@ -820,7 +849,12 @@ static void ImGui_ImplGlfw_UpdateMonitors()
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
int monitors_count = 0;
GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count);
platform_io.Monitors.resize(0);
// IMHEX PATCH BEGIN
if (monitors_count > 0)
platform_io.Monitors.resize(0);
// IMHEX PATCH END
for (int n = 0; n < monitors_count; n++)
{
ImGuiPlatformMonitor monitor;

View File

@@ -1565,8 +1565,8 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc
width_excess += items[n].Width - width_rounded;
items[n].Width = width_rounded;
}
while (width_excess > 0.0f)
for (int n = 0; n < count; n++)
while (width_excess >= 1.0f)
for (int n = 0; n < count && width_excess >= 1.0f; n++)
if (items[n].Width + 1.0f <= items[n].InitialWidth)
{
items[n].Width += 1.0f;
@@ -7638,7 +7638,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section
// With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore
if (width_excess > 0.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible))
if (width_excess >= 1.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible))
{
int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount);
int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0);

View File

@@ -1,9 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(intervaltree)
set(CMAKE_CXX_STANDARD20)
add_library(intervaltree INTERFACE)
target_include_directories(intervaltree INTERFACE include)
target_compile_options(intervaltree INTERFACE "-DUSE_INTERVAL_TREE_NAMESPACE")

View File

@@ -1,19 +0,0 @@
Copyright (c) 2011 Erik Garrison
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,37 +0,0 @@
# intervaltree
## Overview
An interval tree can be used to efficiently find a set of numeric intervals overlapping or containing another interval.
This library provides a basic implementation of an interval tree using C++ templates, allowing the insertion of arbitrary types into the tree.
## Usage
Add `#include "IntervalTree.h"` to the source files in which you will use the interval tree.
To make an IntervalTree to contain objects of class T, use:
```c++
vector<Interval<T> > intervals;
T a, b, c;
intervals.push_back(Interval<T>(2, 10, a));
intervals.push_back(Interval<T>(3, 4, b));
intervals.push_back(Interval<T>(20, 100, c));
IntervalTree<T> tree;
tree = IntervalTree<T>(intervals);
```
Now, it's possible to query the tree and obtain a set of intervals which are contained within the start and stop coordinates.
```c++
vector<Interval<T> > results;
tree.findContained(start, stop, results);
cout << "found " << results.size() << " overlapping intervals" << endl;
```
The function IntervalTree::findOverlapping provides a method to find all those intervals which are contained or partially overlap the interval (start, stop).
### Author: Erik Garrison <erik.garrison@gmail.com>
### License: MIT

View File

@@ -1,325 +0,0 @@
#ifndef __INTERVAL_TREE_H
#define __INTERVAL_TREE_H
#include <vector>
#include <algorithm>
#include <iostream>
#include <memory>
#include <cassert>
#include <limits>
#ifdef USE_INTERVAL_TREE_NAMESPACE
namespace interval_tree {
#endif
template <class Scalar, typename Value>
class Interval {
public:
Scalar start;
Scalar stop;
Value value;
Interval(const Scalar& s, const Scalar& e, const Value& v)
: start(std::min(s, e))
, stop(std::max(s, e))
, value(v)
{}
};
template <class Scalar, typename Value>
Value intervalStart(const Interval<Scalar,Value>& i) {
return i.start;
}
template <class Scalar, typename Value>
Value intervalStop(const Interval<Scalar, Value>& i) {
return i.stop;
}
template <class Scalar, typename Value>
std::ostream& operator<<(std::ostream& out, const Interval<Scalar, Value>& i) {
out << "Interval(" << i.start << ", " << i.stop << "): " << i.value;
return out;
}
template <class Scalar, class Value>
class IntervalTree {
public:
typedef Interval<Scalar, Value> interval;
typedef std::vector<interval> interval_vector;
struct IntervalStartCmp {
bool operator()(const interval& a, const interval& b) {
return a.start < b.start;
}
};
struct IntervalStopCmp {
bool operator()(const interval& a, const interval& b) {
return a.stop < b.stop;
}
};
IntervalTree()
: left(nullptr)
, right(nullptr)
, center(0)
{}
~IntervalTree() = default;
std::unique_ptr<IntervalTree> clone() const {
return std::unique_ptr<IntervalTree>(new IntervalTree(*this));
}
IntervalTree(const IntervalTree& other)
: intervals(other.intervals),
left(other.left ? other.left->clone() : nullptr),
right(other.right ? other.right->clone() : nullptr),
center(other.center)
{}
IntervalTree& operator=(IntervalTree&&) = default;
IntervalTree(IntervalTree&&) = default;
IntervalTree& operator=(const IntervalTree& other) {
center = other.center;
intervals = other.intervals;
left = other.left ? other.left->clone() : nullptr;
right = other.right ? other.right->clone() : nullptr;
return *this;
}
IntervalTree(
interval_vector&& ivals,
std::size_t depth = 16,
std::size_t minbucket = 64,
std::size_t maxbucket = 512,
Scalar leftextent = 0,
Scalar rightextent = 0)
: left(nullptr)
, right(nullptr)
{
--depth;
const auto minmaxStop = std::minmax_element(ivals.begin(), ivals.end(),
IntervalStopCmp());
const auto minmaxStart = std::minmax_element(ivals.begin(), ivals.end(),
IntervalStartCmp());
if (!ivals.empty()) {
center = (minmaxStart.first->start + minmaxStop.second->stop) / 2;
}
if (leftextent == 0 && rightextent == 0) {
// sort intervals by start
std::sort(ivals.begin(), ivals.end(), IntervalStartCmp());
} else {
assert(std::is_sorted(ivals.begin(), ivals.end(), IntervalStartCmp()));
}
if (depth == 0 || (ivals.size() < minbucket && ivals.size() < maxbucket)) {
std::sort(ivals.begin(), ivals.end(), IntervalStartCmp());
intervals = std::move(ivals);
assert(is_valid().first);
return;
} else {
Scalar leftp = 0;
Scalar rightp = 0;
if (leftextent || rightextent) {
leftp = leftextent;
rightp = rightextent;
} else {
leftp = ivals.front().start;
rightp = std::max_element(ivals.begin(), ivals.end(),
IntervalStopCmp())->stop;
}
interval_vector lefts;
interval_vector rights;
for (typename interval_vector::const_iterator i = ivals.begin();
i != ivals.end(); ++i) {
const interval& interval = *i;
if (interval.stop < center) {
lefts.push_back(interval);
} else if (interval.start > center) {
rights.push_back(interval);
} else {
assert(interval.start <= center);
assert(center <= interval.stop);
intervals.push_back(interval);
}
}
if (!lefts.empty()) {
left.reset(new IntervalTree(std::move(lefts),
depth, minbucket, maxbucket,
leftp, center));
}
if (!rights.empty()) {
right.reset(new IntervalTree(std::move(rights),
depth, minbucket, maxbucket,
center, rightp));
}
}
assert(is_valid().first);
}
// Call f on all intervals near the range [start, stop]:
template <class UnaryFunction>
void visit_near(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
if (!intervals.empty() && ! (stop < intervals.front().start)) {
for (auto & i : intervals) {
f(i);
}
}
if (left && start <= center) {
left->visit_near(start, stop, f);
}
if (right && stop >= center) {
right->visit_near(start, stop, f);
}
}
// Call f on all intervals crossing pos
template <class UnaryFunction>
void visit_overlapping(const Scalar& pos, UnaryFunction f) const {
visit_overlapping(pos, pos, f);
}
// Call f on all intervals overlapping [start, stop]
template <class UnaryFunction>
void visit_overlapping(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
auto filterF = [&](const interval& interval) {
if (interval.stop >= start && interval.start <= stop) {
// Only apply f if overlapping
f(interval);
}
};
visit_near(start, stop, filterF);
}
// Call f on all intervals contained within [start, stop]
template <class UnaryFunction>
void visit_contained(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
auto filterF = [&](const interval& interval) {
if (start <= interval.start && interval.stop <= stop) {
f(interval);
}
};
visit_near(start, stop, filterF);
}
interval_vector findOverlapping(const Scalar& start, const Scalar& stop) const {
interval_vector result;
visit_overlapping(start, stop,
[&](const interval& interval) {
result.emplace_back(interval);
});
return result;
}
interval_vector findContained(const Scalar& start, const Scalar& stop) const {
interval_vector result;
visit_contained(start, stop,
[&](const interval& interval) {
result.push_back(interval);
});
return result;
}
bool empty() const {
if (left && !left->empty()) {
return false;
}
if (!intervals.empty()) {
return false;
}
if (right && !right->empty()) {
return false;
}
return true;
}
template <class UnaryFunction>
void visit_all(UnaryFunction f) const {
if (left) {
left->visit_all(f);
}
std::for_each(intervals.begin(), intervals.end(), f);
if (right) {
right->visit_all(f);
}
}
std::pair<Scalar, Scalar> extentBruitForce() const {
struct Extent {
std::pair<Scalar, Scalar> x = {std::numeric_limits<Scalar>::max(),
std::numeric_limits<Scalar>::min() };
void operator()(const interval & interval) {
x.first = std::min(x.first, interval.start);
x.second = std::max(x.second, interval.stop);
}
};
Extent extent;
visit_all([&](const interval & interval) { extent(interval); });
return extent.x;
}
// Check all constraints.
// If first is false, second is invalid.
std::pair<bool, std::pair<Scalar, Scalar>> is_valid() const {
const auto minmaxStop = std::minmax_element(intervals.begin(), intervals.end(),
IntervalStopCmp());
const auto minmaxStart = std::minmax_element(intervals.begin(), intervals.end(),
IntervalStartCmp());
std::pair<bool, std::pair<Scalar, Scalar>> result = {true, { std::numeric_limits<Scalar>::max(),
std::numeric_limits<Scalar>::min() }};
if (!intervals.empty()) {
result.second.first = std::min(result.second.first, minmaxStart.first->start);
result.second.second = std::min(result.second.second, minmaxStop.second->stop);
}
if (left) {
auto valid = left->is_valid();
result.first &= valid.first;
result.second.first = std::min(result.second.first, valid.second.first);
result.second.second = std::min(result.second.second, valid.second.second);
if (!result.first) { return result; }
if (valid.second.second >= center) {
result.first = false;
return result;
}
}
if (right) {
auto valid = right->is_valid();
result.first &= valid.first;
result.second.first = std::min(result.second.first, valid.second.first);
result.second.second = std::min(result.second.second, valid.second.second);
if (!result.first) { return result; }
if (valid.second.first <= center) {
result.first = false;
return result;
}
}
if (!std::is_sorted(intervals.begin(), intervals.end(), IntervalStartCmp())) {
result.first = false;
}
return result;
}
void clear() {
left.reset();
right.reset();
intervals.clear();
center = 0;
}
private:
interval_vector intervals;
std::unique_ptr<IntervalTree> left;
std::unique_ptr<IntervalTree> right;
Scalar center;
};
#ifdef USE_INTERVAL_TREE_NAMESPACE
}
#endif
#endif

1
lib/external/libwolv vendored Submodule

Submodule lib/external/libwolv added at 433d230331

View File

@@ -34,6 +34,8 @@ set(LIBYARA_INCLUDES
${LIBYARA_SOURCE_PATH}/include/yara/types.h
${LIBYARA_SOURCE_PATH}/include/yara/utils.h
${LIBYARA_SOURCE_PATH}/crypto.h
${LIBYARA_SOURCE_PATH}/tlshc/tlsh_impl.h
${LIBYARA_SOURCE_PATH}/tlshc/tlsh_util.h
)
set(LIBYARA_SOURCE
@@ -66,6 +68,7 @@ set(LIBYARA_SOURCE
${LIBYARA_SOURCE_PATH}/scan.c
${LIBYARA_SOURCE_PATH}/scanner.c
${LIBYARA_SOURCE_PATH}/sizedstr.c
${LIBYARA_SOURCE_PATH}/simple_str.c
${LIBYARA_SOURCE_PATH}/stack.c
${LIBYARA_SOURCE_PATH}/stopwatch.c
${LIBYARA_SOURCE_PATH}/strutils.c
@@ -78,6 +81,9 @@ set(LIBYARA_SOURCE
${LIBYARA_SOURCE_PATH}/hex_grammar.c
${LIBYARA_SOURCE_PATH}/re_grammar.c
${LIBYARA_SOURCE_PATH}/proc/none.c
${LIBYARA_SOURCE_PATH}/tlshc/tlsh.c
${LIBYARA_SOURCE_PATH}/tlshc/tlsh_impl.c
${LIBYARA_SOURCE_PATH}/tlshc/tlsh_util.c
)
set(LIBYARA_MODULES
@@ -91,6 +97,7 @@ set(LIBYARA_MODULES
${LIBYARA_SOURCE_PATH}/modules/math/math.c
${LIBYARA_SOURCE_PATH}/modules/pe/pe.c
${LIBYARA_SOURCE_PATH}/modules/pe/pe_utils.c
${LIBYARA_SOURCE_PATH}/modules/string/string.c
${LIBYARA_SOURCE_PATH}/modules/tests/tests.c
${LIBYARA_SOURCE_PATH}/modules/time/time.c
)
@@ -101,17 +108,13 @@ add_library(libyara STATIC ${LIBYARA_SOURCE} ${LIBYARA_INCLUDES} ${LIBYARA_MODUL
set_property(TARGET libyara PROPERTY POSITION_INDEPENDENT_CODE ON)
# Add mbedtls crypto wrappers
target_compile_definitions(libyara PRIVATE HAVE_MBEDTLS)
target_compile_definitions(libyara PRIVATE
HAVE_MBEDTLS
USE_NO_PROC BUCKETS_256 CHECKSUM_3B
HASH_MODULE DOTNET_MODULE MAGIC_MODULE MACHO_MODULE DEX_MODULE
)
target_compile_definitions(libyara PRIVATE USE_NO_PROC)
target_compile_definitions(libyara PRIVATE HASH_MODULE)
target_compile_definitions(libyara PRIVATE DOTNET_MODULE)
target_compile_definitions(libyara PRIVATE MAGIC_MODULE)
target_compile_definitions(libyara PRIVATE MACHO_MODULE)
target_compile_definitions(libyara PRIVATE DEX_MODULE)
target_compile_options(libyara PRIVATE "-Wno-shift-count-overflow")
target_compile_options(libyara PRIVATE -Wno-shift-count-overflow -Wno-stringop-overflow)
target_include_directories(
libyara
@@ -128,6 +131,6 @@ else ()
endif ()
include(GNUInstallDirs)
configure_file(${LIBYARA_SOURCE_PATH}/yara.pc.in
${LIBYARA_SOURCE_PATH}/yara.pc @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/yara/yara.pc.in
${CMAKE_CURRENT_SOURCE_DIR}/yara/yara.pc @ONLY)
set(CMAKE_STATIC_LIBRARY_PREFIX "")

View File

@@ -5,39 +5,39 @@ set(CMAKE_CXX_STANDARD 23)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
set(LIBIMHEX_SOURCES
source/api/event.cpp
source/api/imhex_api.cpp
source/api/content_registry.cpp
source/api/task.cpp
source/api/keybinding.cpp
source/api/plugin_manager.cpp
source/api/localization.cpp
source/api/project_file_manager.cpp
source/api/theme_manager.cpp
source/api/event.cpp
source/api/imhex_api.cpp
source/api/content_registry.cpp
source/api/task.cpp
source/api/keybinding.cpp
source/api/plugin_manager.cpp
source/api/localization.cpp
source/api/project_file_manager.cpp
source/api/theme_manager.cpp
source/api/layout_manager.cpp
source/data_processor/attribute.cpp
source/data_processor/link.cpp
source/data_processor/node.cpp
source/data_processor/attribute.cpp
source/data_processor/link.cpp
source/data_processor/node.cpp
source/helpers/utils.cpp
source/helpers/fs.cpp
source/helpers/magic.cpp
source/helpers/crypto.cpp
source/helpers/net.cpp
source/helpers/opengl.cpp
source/helpers/file.cpp
source/helpers/socket.cpp
source/helpers/patches.cpp
source/helpers/encoding_file.cpp
source/helpers/logger.cpp
source/helpers/stacktrace.cpp
source/helpers/tar.cpp
source/helpers/utils.cpp
source/helpers/fs.cpp
source/helpers/magic.cpp
source/helpers/crypto.cpp
source/helpers/http_requests.cpp
source/helpers/opengl.cpp
source/helpers/patches.cpp
source/helpers/encoding_file.cpp
source/helpers/logger.cpp
source/helpers/stacktrace.cpp
source/helpers/tar.cpp
source/providers/provider.cpp
source/providers/provider.cpp
source/ui/imgui_imhex_extensions.cpp
source/ui/view.cpp
)
source/ui/imgui_imhex_extensions.cpp
source/ui/view.cpp
source/ui/popup.cpp
)
if (APPLE)
set(OSX_11_0_SDK_PATH /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk)
@@ -49,9 +49,7 @@ if (APPLE)
endif ()
endif ()
set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES}
source/helpers/fs_macos.m
source/helpers/utils_macos.m)
set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES} source/helpers/utils_macos.m)
endif ()
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
@@ -72,4 +70,4 @@ elseif (APPLE)
endif ()
target_link_libraries(libimhex PRIVATE ${FMT_LIBRARIES})
target_link_libraries(libimhex PUBLIC dl imgui ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} libpl intervaltree ${MINIAUDIO_LIBRARIES})
target_link_libraries(libimhex PUBLIC dl imgui ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} libpl ${MINIAUDIO_LIBRARIES} libwolv-utils libwolv-io libwolv-hash libwolv-net libwolv-containers)

View File

@@ -1,7 +1,4 @@
#pragma once
#include <hex/helpers/types.hpp>
#include <hex/helpers/intrinsics.hpp>
constexpr static const auto ImHexApiURL = "https://api.werwolv.net/imhex";
constexpr static const auto GitHubApiURL = "https://api.github.com/repos/WerWolv/ImHex";
#include <hex/helpers/intrinsics.hpp>

View File

@@ -12,6 +12,7 @@
#include <span>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <vector>
@@ -41,87 +42,206 @@ namespace hex {
/* Settings Registry. Allows adding of new entries into the ImHex preferences window. */
namespace Settings {
using Callback = std::function<bool(const std::string &, nlohmann::json &)>;
struct Entry {
std::string name;
bool requiresRestart;
Callback callback;
};
namespace impl {
using Callback = std::function<bool(const std::string &, nlohmann::json &)>;
struct Category {
std::string name;
size_t slot = 0;
struct Entry {
std::string name;
bool requiresRestart;
Callback callback;
};
bool operator<(const Category &other) const {
return name < other.name;
}
struct Category {
std::string name;
size_t slot = 0;
explicit operator const std::string &() const {
return name;
}
};
bool operator<(const Category &other) const {
return name < other.name;
}
void load();
void store();
void clear();
explicit operator const std::string &() const {
return name;
}
};
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback, bool requiresRestart = false);
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback, bool requiresRestart = false);
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const Callback &callback, bool requiresRestart = false);
void load();
void store();
void clear();
std::map<Category, std::vector<Entry>> &getEntries();
std::map<std::string, std::string> &getCategoryDescriptions();
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
nlohmann::json &getSettingsData();
}
/**
* @brief Adds a new integer setting entry
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @param callback The callback that will be called when the settings item in the preferences window is rendered
* @param requiresRestart Whether the setting requires a restart to take effect
*/
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const impl::Callback &callback, bool requiresRestart = false);
/**
* @brief Adds a new string setting entry
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @param callback The callback that will be called when the settings item in the preferences window is rendered
* @param requiresRestart Whether the setting requires a restart to take effect
*/
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const impl::Callback &callback, bool requiresRestart = false);
/**
* @brief Adds a new string list setting entry
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @param callback The callback that will be called when the settings item in the preferences window is rendered
* @param requiresRestart Whether the setting requires a restart to take effect
*/
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const impl::Callback &callback, bool requiresRestart = false);
/**
* @brief Adds a description to a given category
* @param unlocalizedCategory The name of the category
* @param unlocalizedCategoryDescription The description of the category
*/
void addCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription);
/**
* @brief Writes a integer value to the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param value The value to write
*/
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value);
/**
* @brief Writes a string value to the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param value The value to write
*/
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value);
/**
* @brief Writes a string list value to the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param value The value to write
*/
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &value);
/**
* @brief Reads an integer value from the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @return The value of the setting
*/
i64 read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue);
/**
* @brief Reads a string value from the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @return The value of the setting
*/
std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue);
/**
* @brief Reads a string list value from the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @return The value of the setting
*/
std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue = {});
std::map<Category, std::vector<Entry>> &getEntries();
std::map<std::string, std::string> &getCategoryDescriptions();
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
nlohmann::json &getSettingsData();
}
/* Command Palette Command Registry. Allows adding of new commands to the command palette */
namespace CommandPaletteCommands {
enum class Type : u32
{
enum class Type : u32 {
SymbolCommand,
KeywordCommand
};
using DisplayCallback = std::function<std::string(std::string)>;
using ExecuteCallback = std::function<void(std::string)>;
namespace impl {
struct Entry {
Type type;
std::string command;
std::string unlocalizedDescription;
DisplayCallback displayCallback;
ExecuteCallback executeCallback;
};
struct QueryResult {
std::string name;
std::function<void(std::string)> callback;
};
using DisplayCallback = std::function<std::string(std::string)>;
using ExecuteCallback = std::function<void(std::string)>;
using QueryCallback = std::function<std::vector<QueryResult>(std::string)>;
struct Entry {
Type type;
std::string command;
std::string unlocalizedDescription;
DisplayCallback displayCallback;
ExecuteCallback executeCallback;
};
struct Handler {
Type type;
std::string command;
QueryCallback queryCallback;
DisplayCallback displayCallback;
};
std::vector<impl::Entry> &getEntries();
std::vector<impl::Handler> &getHandlers();
}
/**
* @brief Adds a new command to the command palette
* @param type The type of the command
* @param command The command to add
* @param unlocalizedDescription The description of the command
* @param displayCallback The callback that will be called when the command is displayed in the command palette
* @param executeCallback The callback that will be called when the command is executed
*/
void add(
Type type,
const std::string &command,
const std::string &unlocalizedDescription,
const DisplayCallback &displayCallback,
const ExecuteCallback &executeCallback = [](auto) {});
std::vector<Entry> &getEntries();
const impl::DisplayCallback &displayCallback,
const impl::ExecuteCallback &executeCallback = [](auto) {});
/**
* @brief Adds a new command handler to the command palette
* @param type The type of the command
* @param command The command to add
* @param unlocalizedDescription The description of the command
* @param queryCallback The callback that will be called when the command palette wants to load the name and callback items
* @param displayCallback The callback that will be called when the command is displayed in the command palette
*/
void addHandler(
Type type,
const std::string &command,
const impl::QueryCallback &queryCallback,
const impl::DisplayCallback &displayCallback);
}
/* Pattern Language Function Registry. Allows adding of new functions that may be used inside the pattern language */
namespace PatternLanguage {
using VisualizerFunctionCallback = std::function<void(pl::ptrn::Pattern&, pl::ptrn::Iteratable&, bool, std::span<const pl::core::Token::Literal>)>;
namespace impl {
using VisualizerFunctionCallback = std::function<void(pl::ptrn::Pattern&, pl::ptrn::IIterable&, bool, std::span<const pl::core::Token::Literal>)>;
struct FunctionDefinition {
pl::api::Namespace ns;
std::string name;
@@ -138,20 +258,64 @@ namespace hex {
};
std::map<std::string, Visualizer> &getVisualizers();
std::map<std::string, pl::api::PragmaHandler> &getPragmas();
std::vector<impl::FunctionDefinition> &getFunctions();
}
/**
* @brief Provides access to the current provider's pattern language runtime
* @return Runtime
*/
pl::PatternLanguage& getRuntime();
/**
* @brief Provides access to the current provider's pattern language runtime's lock
* @return Lock
*/
std::mutex& getRuntimeLock();
/**
* @brief Configures the pattern language runtime using ImHex's default settings
* @param runtime The pattern language runtime to configure
* @param provider The provider to use for data access
*/
void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider);
/**
* @brief Adds a new pragma to the pattern language
* @param name The name of the pragma
* @param handler The handler that will be called when the pragma is encountered
*/
void addPragma(const std::string &name, const pl::api::PragmaHandler &handler);
/**
* @brief Adds a new function to the pattern language
* @param ns The namespace of the function
* @param name The name of the function
* @param parameterCount The amount of parameters the function takes
* @param func The function callback
*/
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
/**
* @brief Adds a new dangerous function to the pattern language
* @note Dangerous functions are functions that require the user to explicitly allow them to be used
* @param ns The namespace of the function
* @param name The name of the function
* @param parameterCount The amount of parameters the function takes
* @param func The function callback
*/
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
void addVisualizer(const std::string &name, const VisualizerFunctionCallback &func, u32 parameterCount);
std::map<std::string, pl::api::PragmaHandler> &getPragmas();
std::vector<impl::FunctionDefinition> &getFunctions();
/**
* @brief Adds a new visualizer to the pattern language
* @note Visualizers are extensions to the [[hex::visualize]] attribute, used to visualize data
* @param name The name of the visualizer
* @param func The function callback
* @param parameterCount The amount of parameters the function takes
*/
void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &func, u32 parameterCount);
}
@@ -160,19 +324,28 @@ namespace hex {
namespace impl {
void add(View *view);
void add(std::unique_ptr<View> &&view);
std::map<std::string, std::unique_ptr<View>> &getEntries();
}
/**
* @brief Adds a new view to ImHex
* @tparam T The custom view class that extends View
* @tparam Args Arguments types
* @param args Arguments passed to the constructor of the view
*/
template<std::derived_from<View> T, typename... Args>
void add(Args &&...args) {
return impl::add(new T(std::forward<Args>(args)...));
return impl::add(std::make_unique<T>(std::forward<Args>(args)...));
}
std::map<std::string, View *> &getEntries();
View *getViewByName(const std::string &unlocalizedName);
/**
* @brief Gets a view by its unlocalized name
* @param unlocalizedName The unlocalized name of the view
* @return The view if it exists, nullptr otherwise
*/
View* getViewByName(const std::string &unlocalizedName);
}
/* Tools Registry. Allows adding new entries to the tools window */
@@ -188,11 +361,16 @@ namespace hex {
bool detached;
};
std::vector<impl::Entry> &getEntries();
}
/**
* @brief Adds a new tool to the tools window
* @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);
std::vector<impl::Entry> &getEntries();
}
/* Data Inspector Registry. Allows adding of new types to the data inspector */
@@ -219,12 +397,28 @@ namespace hex {
std::optional<impl::EditingFunction> editingFunction;
};
std::vector<impl::Entry> &getEntries();
}
/**
* @brief Adds a new entry to the data inspector
* @param unlocalizedName The unlocalized name of the entry
* @param requiredSize The minimum required number of bytes available for the entry to appear
* @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 std::string &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction = std::nullopt);
std::vector<impl::Entry> &getEntries();
/**
* @brief Adds a new entry to the data inspector
* @param unlocalizedName The unlocalized name of the entry
* @param requiredSize The minimum required number of bytes available for the entry to appear
* @param maxSize The maximum number of bytes to read from the data
* @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);
}
/* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */
@@ -242,15 +436,24 @@ namespace hex {
void add(const Entry &entry);
std::vector<impl::Entry> &getEntries();
}
/**
* @brief Adds a new node to the data processor
* @tparam T The custom node class that extends dp::Node
* @tparam Args Arguments types
* @param unlocalizedCategory The unlocalized category name of the node
* @param unlocalizedName The unlocalized name of the node
* @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) {
add(impl::Entry {
unlocalizedCategory.c_str(),
unlocalizedName.c_str(),
[=] {
[=, ...args = std::forward<Args>(args)] mutable {
auto node = std::make_unique<T>(std::forward<Args>(args)...);
node->setUnlocalizedName(unlocalizedName);
return node;
@@ -258,18 +461,29 @@ namespace hex {
});
}
/**
* @brief Adds a separator to the data processor right click menu
*/
void addSeparator();
std::vector<impl::Entry> &getEntries();
}
/* Language Registry. Allows together with the LangEntry class and the _lang user defined literal to add new languages */
namespace Language {
/**
* @brief Loads localization information from json data
* @param data The language data
*/
void addLocalization(const nlohmann::json &data);
std::map<std::string, std::string> &getLanguages();
std::map<std::string, std::vector<LanguageDefinition>> &getLanguageDefinitions();
namespace impl {
std::map<std::string, std::string> &getLanguages();
std::map<std::string, std::vector<LanguageDefinition>> &getLanguageDefinitions();
}
}
/* Interface Registry. Allows adding new items to various interfaces */
@@ -277,22 +491,20 @@ namespace hex {
namespace impl {
using DrawCallback = std::function<void()>;
using LayoutFunction = std::function<void(u32)>;
using ClickCallback = std::function<void()>;
struct Layout {
std::string unlocalizedName;
LayoutFunction callback;
};
using DrawCallback = std::function<void()>;
using MenuCallback = std::function<void()>;
using EnabledCallback = std::function<bool()>;
using ClickCallback = std::function<void()>;
struct MainMenuItem {
std::string unlocalizedName;
};
struct MenuItem {
std::string unlocalizedName;
DrawCallback callback;
std::vector<std::string> unlocalizedNames;
Shortcut shortcut;
MenuCallback callback;
EnabledCallback enabledCallback;
};
struct SidebarItem {
@@ -306,29 +518,89 @@ namespace hex {
ClickCallback callback;
};
constexpr static auto SeparatorValue = "$SEPARATOR$";
constexpr static auto SubMenuValue = "$SUBMENU$";
std::multimap<u32, impl::MainMenuItem> &getMainMenuItems();
std::multimap<u32, impl::MenuItem> &getMenuItems();
std::vector<impl::DrawCallback> &getWelcomeScreenEntries();
std::vector<impl::DrawCallback> &getFooterItems();
std::vector<impl::DrawCallback> &getToolbarItems();
std::vector<impl::SidebarItem> &getSidebarItems();
std::vector<impl::TitleBarButton> &getTitleBarButtons();
}
/**
* @brief Adds a new top-level main menu entry
* @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 addMenuItem(const std::string &unlocalizedMainMenuName, u32 priority, const impl::DrawCallback &function);
/**
* @brief Adds a new main menu entry
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
* @param priority The priority of the entry. Lower values are displayed first
* @param shortcut The shortcut to use for the entry
* @param function The function to call when the entry is clicked
* @param enabledCallback The function to call to determine if the entry is enabled
* @param 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);
/**
* @brief Adds a new main menu sub-menu entry
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
* @param priority The priority of the entry. Lower values are displayed first
* @param function The function to call when the entry is clicked
* @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 addMenuItemSubMenu(std::vector<std::string> 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);
/**
* @brief Adds a new welcome screen entry
* @param function The function to call to draw the entry
*/
void addWelcomeScreenEntry(const impl::DrawCallback &function);
/**
* @brief Adds a new footer item
* @param function The function to call to draw the item
*/
void addFooterItem(const impl::DrawCallback &function);
/**
* @brief Adds a new toolbar item
* @param function The function to call to draw the item
*/
void addToolbarItem(const impl::DrawCallback &function);
/**
* @brief Adds a new sidebar item
* @param icon The icon to use for the item
* @param function The function to call to draw the item
*/
void addSidebarItem(const std::string &icon, const impl::DrawCallback &function);
/**
* @brief Adds a new title bar button
* @param icon The icon to use for the button
* @param unlocalizedTooltip The unlocalized tooltip to use for the button
* @param function The function to call when the button is clicked
*/
void addTitleBarButton(const std::string &icon, const std::string &unlocalizedTooltip, const impl::ClickCallback &function);
void addLayout(const std::string &unlocalizedName, const impl::LayoutFunction &function);
std::multimap<u32, impl::MainMenuItem> &getMainMenuItems();
std::multimap<u32, impl::MenuItem> &getMenuItems();
std::vector<impl::DrawCallback> &getWelcomeScreenEntries();
std::vector<impl::DrawCallback> &getFooterItems();
std::vector<impl::DrawCallback> &getToolbarItems();
std::vector<impl::SidebarItem> &getSidebarItems();
std::vector<impl::TitleBarButton> &getTitleBarButtons();
std::vector<impl::Layout> &getLayouts();
}
/* Provider Registry. Allows adding new data providers to be created from the UI */
@@ -338,8 +610,15 @@ namespace hex {
void addProviderName(const std::string &unlocalizedName);
std::vector<std::string> &getEntries();
}
/**
* @brief Adds a new provider to the list of providers
* @tparam T The provider type that extends hex::prv::Provider
* @param addToList Whether to display the provider in the Other Providers list in the welcome screen and File menu
*/
template<std::derived_from<hex::prv::Provider> T>
void add(bool addToList = true) {
auto typeName = T().getTypeName();
@@ -359,10 +638,9 @@ namespace hex {
impl::addProviderName(typeName);
}
std::vector<std::string> &getEntries();
}
/* Data Formatter Registry. Allows adding formatters that are used in the Copy-As menu for example */
namespace DataFormatter {
namespace impl {
@@ -373,32 +651,45 @@ namespace hex {
Callback callback;
};
std::vector<impl::Entry> &getEntries();
}
void add(const std::string &unlocalizedName, const impl::Callback &callback);
std::vector<impl::Entry> &getEntries();
/**
* @brief Adds a new data formatter
* @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);
}
/* File Handler Registry. Allows adding handlers for opening files specific file types */
namespace FileHandler {
namespace impl {
using Callback = std::function<bool(std::filesystem::path)>;
using Callback = std::function<bool(std::fs::path)>;
struct Entry {
std::vector<std::string> extensions;
Callback callback;
};
std::vector<impl::Entry> &getEntries();
}
/**
* @brief Adds a new file handler
* @param extensions The file extensions to handle
* @param callback The function to call to handle the file
*/
void add(const std::vector<std::string> &extensions, const impl::Callback &callback);
std::vector<impl::Entry> &getEntries();
}
/* Hex Editor Registry. Allows adding new functionality to the hex editor */
namespace HexEditor {
class DataVisualizer {
@@ -417,7 +708,8 @@ namespace hex {
protected:
const static int TextInputFlags;
bool drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const;
bool drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const;
bool drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const;
private:
u16 m_bytesPerCell;
u16 m_maxCharsPerCell;
@@ -431,6 +723,12 @@ namespace hex {
}
/**
* @brief Adds a new cell data visualizer
* @tparam T The data visualizer type that extends hex::DataVisualizer
* @param unlocalizedName The unlocalized name of the data visualizer
* @param args The arguments to pass to the constructor of the data visualizer
*/
template<std::derived_from<DataVisualizer> T, typename... Args>
void addDataVisualizer(const std::string &unlocalizedName, Args &&...args) {
return impl::addDataVisualizer(unlocalizedName, new T(std::forward<Args>(args)...));
@@ -438,6 +736,7 @@ namespace hex {
}
/* Hash Registry. Allows adding new hashes to the Hash view */
namespace Hashes {
class Hash {
@@ -503,12 +802,47 @@ namespace hex {
void add(Hash* hash);
}
/**
* @brief Adds a new hash
* @tparam T The hash type that extends hex::Hash
* @param args The arguments to pass to the constructor of the hash
*/
template<typename T, typename ... Args>
void add(Args && ... args) {
impl::add(new T(std::forward<Args>(args)...));
}
}
namespace BackgroundServices {
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);
}
namespace CommunicationInterface {
namespace impl {
using NetworkCallback = std::function<nlohmann::json(const nlohmann::json &)>;
std::map<std::string, NetworkCallback> &getNetworkEndpoints();
}
void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback);
}
}
}

View File

@@ -9,73 +9,110 @@
#include <hex/api/imhex_api.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#define EVENT_DEF(event_name, ...) \
struct event_name final : public hex::Event<__VA_ARGS__> { \
constexpr static auto id = [] { return hex::EventId(); }(); \
explicit event_name(Callback func) noexcept : Event(std::move(func)) { } \
#include <wolv/types/type_name.hpp>
#define EVENT_DEF_IMPL(event_name, should_log, ...) \
struct event_name final : public hex::impl::Event<__VA_ARGS__> { \
constexpr static auto Id = [] { return hex::impl::EventId(); }(); \
constexpr static auto ShouldLog = (should_log); \
explicit event_name(Callback func) noexcept : Event(std::move(func)) { } \
}
#define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, true, __VA_ARGS__)
#define EVENT_DEF_NO_LOG(event_name, ...) EVENT_DEF_IMPL(event_name, false, __VA_ARGS__)
struct GLFWwindow;
namespace hex {
class EventId {
public:
explicit constexpr EventId(const char *func = __builtin_FUNCTION(), u32 line = __builtin_LINE()) {
this->m_hash = line ^ 987654321;
for (auto c : std::string_view(func)) {
this->m_hash = (this->m_hash >> 5) | (this->m_hash << 27);
this->m_hash ^= c;
namespace impl {
class EventId {
public:
explicit constexpr EventId(const char *func = __builtin_FUNCTION(), u32 line = __builtin_LINE()) {
this->m_hash = line ^ 987654321;
for (auto c : std::string_view(func)) {
this->m_hash = (this->m_hash >> 5) | (this->m_hash << 27);
this->m_hash ^= c;
}
}
}
constexpr bool operator==(const EventId &rhs) const = default;
constexpr bool operator==(const EventId &rhs) const = default;
private:
u32 m_hash;
};
private:
u32 m_hash;
};
struct EventBase {
EventBase() noexcept = default;
};
struct EventBase {
EventBase() noexcept = default;
};
template<typename... Params>
struct Event : public EventBase {
using Callback = std::function<void(Params...)>;
template<typename... Params>
struct Event : public EventBase {
using Callback = std::function<void(Params...)>;
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
void operator()(Params... params) const noexcept {
this->m_func(params...);
}
void operator()(Params... params) const noexcept {
this->m_func(params...);
}
private:
Callback m_func;
};
private:
Callback m_func;
};
}
/**
* @brief The EventManager allows subscribing to and posting events to different parts of the program.
* To create a new event, use the EVENT_DEF macro. This will create a new event type with the given name and parameters
*/
class EventManager {
public:
using EventList = std::list<std::pair<EventId, EventBase *>>;
using EventList = std::list<std::pair<impl::EventId, impl::EventBase *>>;
/**
* @brief Subscribes to an event
* @tparam E Event
* @param function Function to call when the event is posted
* @return Token to unsubscribe from the event
*/
template<typename E>
static EventList::iterator subscribe(typename E::Callback function) {
return s_events.insert(s_events.end(), std::make_pair(E::id, new E(function)));
return s_events.insert(s_events.end(), std::make_pair(E::Id, new E(function)));
}
/**
* @brief Subscribes to an event
* @tparam E Event
* @param token Unique token to register the event to. Later required to unsubscribe again
* @param function Function to call when the event is posted
*/
template<typename E>
static void subscribe(void *token, typename E::Callback function) {
s_tokenStore.insert(std::make_pair(token, subscribe<E>(function)));
}
static void unsubscribe(EventList::iterator iter) noexcept {
s_events.remove(*iter);
/**
* @brief Unsubscribes from an event
* @param token Token returned by subscribe
*/
static void unsubscribe(const EventList::iterator &token) noexcept {
s_events.erase(token);
}
/**
* @brief Unsubscribes from an event
* @tparam E Event
* @param token Token passed to subscribe
*/
template<typename E>
static void unsubscribe(void *token) noexcept {
auto iter = std::find_if(s_tokenStore.begin(), s_tokenStore.end(), [&](auto &item) {
return item.first == token && item.second->first == E::id;
return item.first == token && item.second->first == E::Id;
});
if (iter != s_tokenStore.end()) {
@@ -85,14 +122,28 @@ namespace hex {
}
/**
* @brief Posts an event to all subscribers of it
* @tparam E Event
* @param args Arguments to pass to the event
*/
template<typename E>
static void post(auto &&...args) noexcept {
for (const auto &[id, event] : s_events) {
if (id == E::id)
if (id == E::Id) {
(*static_cast<E *const>(event))(std::forward<decltype(args)>(args)...);
}
}
#if defined (DEBUG)
if (E::ShouldLog)
log::debug("Event posted: '{}'", wolv::type::getTypeName<E>());
#endif
}
/**
* @brief Unsubscribe all subscribers from all events
*/
static void clear() noexcept {
s_events.clear();
s_tokenStore.clear();
@@ -112,33 +163,61 @@ namespace hex {
EVENT_DEF(EventSettingsChanged);
EVENT_DEF(EventAbnormalTermination, int);
EVENT_DEF(EventOSThemeChanged);
/**
* @brief Called when the provider is created.
* This event is responsible for (optionally) initializing the provider and calling EventProviderOpened
* (although the event can also be called manually without problem)
*/
EVENT_DEF(EventProviderCreated, prv::Provider *);
EVENT_DEF(EventProviderChanged, prv::Provider *, prv::Provider *);
/**
* @brief Called as a continuation of EventProviderCreated
* this event is normally called immediately after EventProviderCreated successfully initialized the provider.
* If no initialization (Provider::skipLoadInterface() has been set), this event should be called manually
* If skipLoadInterface failed, this event is not called
*
* @note this is not related to Provider::open()
*/
EVENT_DEF(EventProviderOpened, prv::Provider *);
EVENT_DEF(EventProviderClosing, prv::Provider *, bool *);
EVENT_DEF(EventProviderClosed, prv::Provider *);
EVENT_DEF(EventProviderDeleted, prv::Provider *);
EVENT_DEF(EventFrameBegin);
EVENT_DEF(EventFrameEnd);
EVENT_DEF(EventProviderSaved, prv::Provider *);
EVENT_DEF(EventWindowInitialized);
EVENT_DEF(EventSetTaskBarIconState, u32, u32, u32);
EVENT_DEF(EventBookmarkCreated, ImHexApi::Bookmarks::Entry&);
EVENT_DEF(EventPatchCreated, u64, u8, u8);
EVENT_DEF(EventPatternExecuted, const std::string&);
EVENT_DEF(EventPatternEditorChanged, const std::string&);
EVENT_DEF(EventStoreContentDownloaded, const std::fs::path&);
EVENT_DEF(EventStoreContentRemoved, const std::fs::path&);
EVENT_DEF(EventImHexClosing);
EVENT_DEF_NO_LOG(EventFrameBegin);
EVENT_DEF_NO_LOG(EventFrameEnd);
EVENT_DEF_NO_LOG(EventSetTaskBarIconState, u32, u32, u32);
EVENT_DEF(RequestOpenWindow, std::string);
EVENT_DEF(RequestSelectionChange, Region);
EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t);
EVENT_DEF(RequestSetPatternLanguageCode, std::string);
EVENT_DEF(RequestLoadPatternLanguageFile, std::fs::path);
EVENT_DEF(RequestSavePatternLanguageFile, std::fs::path);
EVENT_DEF(RequestUpdateWindowTitle);
EVENT_DEF(RequestCloseImHex, bool);
EVENT_DEF(RequestRestartImHex);
EVENT_DEF(RequestOpenFile, std::fs::path);
EVENT_DEF(RequestChangeTheme, std::string);
EVENT_DEF(RequestOpenPopup, std::string);
/**
* @brief Creates a provider from it's unlocalized name, and add it to the provider list
*/
EVENT_DEF(RequestCreateProvider, std::string, bool, hex::prv::Provider **);
EVENT_DEF(RequestInitThemeHandlers);
EVENT_DEF(RequestShowInfoPopup, std::string);
EVENT_DEF(RequestShowErrorPopup, std::string);
EVENT_DEF(RequestShowFatalErrorPopup, std::string);
EVENT_DEF(RequestShowYesNoQuestionPopup, std::string, std::function<void()>, std::function<void()>);
EVENT_DEF(RequestShowFileChooserPopup, std::vector<std::fs::path>, std::vector<nfdfilteritem_t>, std::function<void(std::fs::path)>, bool);
EVENT_DEF(RequestOpenInfoPopup, const std::string);
EVENT_DEF(RequestOpenErrorPopup, const std::string);
EVENT_DEF(RequestOpenFatalPopup, const std::string);
}

View File

@@ -8,12 +8,12 @@
#include <vector>
#include <variant>
#include <map>
#include <filesystem>
#include <hex/helpers/concepts.hpp>
#include <hex/api/task.hpp>
#include <hex/api/keybinding.hpp>
#include <wolv/io/fs.hpp>
using ImGuiID = unsigned int;
struct ImVec2;
@@ -25,13 +25,7 @@ namespace hex {
namespace ImHexApi {
namespace Common {
void closeImHex(bool noQuestions = false);
void restartImHex();
}
/* Functions to query information from the Hex Editor and interact with it */
namespace HexEditor {
using TooltipFunction = std::function<void(u64, const u8*, size_t)>;
@@ -86,32 +80,132 @@ namespace hex {
void setCurrentSelection(std::optional<ProviderRegion> region);
}
/**
* @brief Adds a background color highlighting to the Hex Editor
* @param region The region to highlight
* @param color The color to use for the highlighting
* @return Unique ID used to remove the highlighting again later
*/
u32 addBackgroundHighlight(const Region &region, color_t color);
/**
* @brief Removes a background color highlighting from the Hex Editor
* @param id The ID of the highlighting to remove
*/
void removeBackgroundHighlight(u32 id);
/**
* @brief Adds a foreground color highlighting to the Hex Editor
* @param region The region to highlight
* @param color The color to use for the highlighting
* @return Unique ID used to remove the highlighting again later
*/
u32 addForegroundHighlight(const Region &region, color_t color);
/**
* @brief Removes a foreground color highlighting from the Hex Editor
* @param id The ID of the highlighting to remove
*/
void removeForegroundHighlight(u32 id);
/**
* @brief Adds a hover tooltip to the Hex Editor
* @param region The region to add the tooltip to
* @param value Text to display in the tooltip
* @param color The color of the tooltip
* @return Unique ID used to remove the tooltip again later
*/
u32 addTooltip(Region region, std::string value, color_t color);
/**
* @brief Removes a hover tooltip from the Hex Editor
* @param id The ID of the tooltip to remove
*/
void removeTooltip(u32 id);
/**
* @brief Adds a background color highlighting to the Hex Editor using a callback function
* @param function Function that draws the highlighting based on the hovered region
* @return Unique ID used to remove the highlighting again later
*/
u32 addTooltipProvider(TooltipFunction function);
/**
* @brief Removes a background color highlighting from the Hex Editor
* @param id The ID of the highlighting to remove
*/
void removeTooltipProvider(u32 id);
/**
* @brief Adds a background color highlighting to the Hex Editor using a callback function
* @param function Function that draws the highlighting based on the hovered region
* @return Unique ID used to remove the highlighting again later
*/
u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function);
/**
* @brief Removes a background color highlighting from the Hex Editor
* @param id The ID of the highlighting to remove
*/
void removeBackgroundHighlightingProvider(u32 id);
/**
* @brief Adds a foreground color highlighting to the Hex Editor using a callback function
* @param function Function that draws the highlighting based on the hovered region
* @return Unique ID used to remove the highlighting again later
*/
u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function);
/**
* @brief Removes a foreground color highlighting from the Hex Editor
* @param id The ID of the highlighting to remove
*/
void removeForegroundHighlightingProvider(u32 id);
/**
* @brief Checks if there's a valid selection in the Hex Editor right now
*/
bool isSelectionValid();
/**
* @brief Clears the current selection in the Hex Editor
*/
void clearSelection();
/**
* @brief Gets the current selection in the Hex Editor
* @return The current selection
*/
std::optional<ProviderRegion> getSelection();
/**
* @brief Sets the current selection in the Hex Editor
* @param region The region to select
* @param provider The provider to select the region in
*/
void setSelection(const Region &region, prv::Provider *provider = nullptr);
/**
* @brief Sets the current selection in the Hex Editor
* @param region The region to select
*/
void setSelection(const ProviderRegion &region);
/**
* @brief Sets the current selection in the Hex Editor
* @param address The address to select
* @param size The size of the selection
* @param provider The provider to select the region in
*/
void setSelection(u64 address, size_t size, prv::Provider *provider = nullptr);
}
/* Functions to interact with Bookmarks */
namespace Bookmarks {
struct Entry {
@@ -123,10 +217,23 @@ namespace hex {
bool locked;
};
/**
* @brief Adds a new bookmark
* @param address The address of the bookmark
* @param size The size of the bookmark
* @param name The name of the bookmark
* @param comment The comment of the bookmark
* @param color The color of the bookmark or 0x00 for the default color
*/
void add(u64 address, size_t size, const std::string &name, const std::string &comment, color_t color = 0x00000000);
}
/**
* Helper methods about the providers
* @note the "current provider" or "currently selected provider" refers to the currently selected provider in the UI;
* the provider the user is actually editing.
*/
namespace Provider {
namespace impl {
@@ -136,30 +243,82 @@ namespace hex {
}
/**
* @brief Gets the currently selected data provider
* @return The currently selected data provider, or nullptr is there is none
*/
prv::Provider *get();
/**
* @brief Gets a list of all currently loaded data providers
* @return The currently loaded data providers
*/
const std::vector<prv::Provider *> &getProviders();
/**
* @brief Sets the currently selected data provider
* @param index Index of the provider to select
*/
void setCurrentProvider(u32 index);
/**
* @brief Checks whether the currently selected data provider is valid
* @return Whether the currently selected data provider is valid
*/
bool isValid();
/**
* @brief Marks the **currently selected** data provider as dirty
*/
void markDirty();
/**
* @brief Marks **all data providers** as clean
*/
void resetDirty();
/**
* @brief Checks whether **any of the data providers** is dirty
* @return Whether any data provider is dirty
*/
bool isDirty();
/**
* @brief Adds a newly created provider to the list of providers, and mark it as the selected one.
* @param provider The provider to add
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
*/
void add(prv::Provider *provider, bool skipLoadInterface = false);
/**
* @brief Creates a new provider and adds it to the list of providers
* @tparam T The type of the provider to create
* @param args Arguments to pass to the provider's constructor
*/
template<std::derived_from<prv::Provider> T>
void add(auto &&...args) {
add(new T(std::forward<decltype(args)>(args)...));
}
/**
* @brief Removes a provider from the list of providers
* @param provider The provider to remove
* @param noQuestions Whether to skip asking the user for confirmation
*/
void remove(prv::Provider *provider, bool noQuestions = false);
/**
* @brief Creates a new provider using its unlocalized name and add it to the list of providers
* @param unlocalizedName The unlocalized name of the provider to create
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
*/
prv::Provider* createProvider(const std::string &unlocalizedName, bool skipLoadInterface = false);
}
/* Functions to interact with various ImHex system settings */
namespace System {
namespace impl {
@@ -175,7 +334,7 @@ namespace hex {
void setBorderlessWindowMode(bool enabled);
void setCustomFontPath(const std::filesystem::path &path);
void setCustomFontPath(const std::fs::path &path);
void setFontSize(float size);
void setGPUVendor(const std::string &vendor);
@@ -203,37 +362,148 @@ namespace hex {
Error
};
/**
* @brief Closes ImHex
* @param noQuestions Whether to skip asking the user for confirmation
*/
void closeImHex(bool noQuestions = false);
/**
* @brief Restarts ImHex
*/
void restartImHex();
/**
* @brief Sets the progress bar in the task bar
* @param state The state of the progress bar
* @param type The type of the progress bar progress
* @param progress The progress of the progress bar
*/
void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress);
/**
* @brief Gets the current program arguments
* @return The current program arguments
*/
const ProgramArguments &getProgramArguments();
/**
* @brief Gets a program argument
* @param index The index of the argument to get
* @return The argument at the given index
*/
std::optional<std::u8string> getProgramArgument(int index);
/**
* @brief Gets the current target FPS
* @return The current target FPS
*/
float getTargetFPS();
/**
* @brief Sets the target FPS
* @param fps The target FPS
*/
void setTargetFPS(float fps);
/**
* @brief Gets the current global scale
* @return The current global scale
*/
float getGlobalScale();
/**
* @brief Gets the current native scale
* @return The current native scale
*/
float getNativeScale();
/**
* @brief Gets the current main window position
* @return Position of the main window
*/
ImVec2 getMainWindowPosition();
/**
* @brief Gets the current main window size
* @return Size of the main window
*/
ImVec2 getMainWindowSize();
/**
* @brief Gets the current main dock space ID
* @return ID of the main dock space
*/
ImGuiID getMainDockSpaceId();
/**
* @brief Checks if borderless window mode is enabled currently
* @return Whether borderless window mode is enabled
*/
bool isBorderlessWindowModeEnabled();
/**
* @brief Gets the init arguments passed to ImHex from the splash screen
* @return Init arguments
*/
std::map<std::string, std::string> &getInitArguments();
constexpr static float DefaultFontSize = 13.0;
/**
* @brief Gets the current custom font path
* @return The current custom font path
*/
const std::filesystem::path &getCustomFontPath();
/**
* @brief Gets the current font size
* @return The current font size
*/
float getFontSize();
/**
* @brief Sets if ImHex should follow the system theme
* @param enabled Whether to follow the system theme
*/
void enableSystemThemeDetection(bool enabled);
/**
* @brief Checks if ImHex follows the system theme
* @return Whether ImHex follows the system theme
*/
bool usesSystemThemeDetection();
/**
* @brief Gets the currently set additional folder paths
* @return The currently set additional folder paths
*/
const std::vector<std::filesystem::path> &getAdditionalFolderPaths();
/**
* @brief Sets the additional folder paths
* @param paths The additional folder paths
*/
void setAdditionalFolderPaths(const std::vector<std::filesystem::path> &paths);
/**
* @brief Gets the current GPU vendor
* @return The current GPU vendor
*/
const std::string &getGPUVendor();
/**
* @brief Checks if ImHex is running in portable mode
* @return Whether ImHex is running in portable mode
*/
bool isPortableVersion();
}

View File

@@ -5,7 +5,9 @@
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <string>
struct ImGuiWindow;
@@ -136,15 +138,31 @@ namespace hex {
auto operator<=>(const Key &) const = default;
[[nodiscard]] constexpr u32 getKeyCode() const { return this->m_key; }
private:
u32 m_key;
};
constexpr static auto CTRL = Key(static_cast<Keys>(0x0100'0000));
constexpr static auto ALT = Key(static_cast<Keys>(0x0200'0000));
constexpr static auto SHIFT = Key(static_cast<Keys>(0x0400'0000));
constexpr static auto SUPER = Key(static_cast<Keys>(0x0800'0000));
constexpr static auto CurrentView = Key(static_cast<Keys>(0x1000'0000));
#if defined (OS_MACOS)
constexpr static auto CTRLCMD = SUPER;
#else
constexpr static auto CTRLCMD = CTRL;
#endif
class Shortcut {
public:
Shortcut() = default;
Shortcut(Keys key) : m_keys({ key }) { }
const static inline auto None = Keys(0);
Shortcut operator+(const Key &other) const {
Shortcut result = *this;
result.m_keys.insert(other);
@@ -166,6 +184,173 @@ namespace hex {
return this->m_keys == other.m_keys;
}
bool isLocal() const {
return this->m_keys.contains(CurrentView);
}
std::string toString() const {
std::string result;
#if defined(OS_MACOS)
constexpr static auto CTRL_NAME = "CTRL";
constexpr static auto ALT_NAME = "OPT";
constexpr static auto SHIFT_NAME = "SHIFT";
constexpr static auto SUPER_NAME = "CMD";
#else
constexpr static auto CTRL_NAME = "CTRL";
constexpr static auto ALT_NAME = "ALT";
constexpr static auto SHIFT_NAME = "SHIFT";
constexpr static auto SUPER_NAME = "SUPER";
#endif
constexpr static auto Concatination = " + ";
auto keys = this->m_keys;
if (keys.erase(CTRL) > 0) {
result += CTRL_NAME;
result += Concatination;
}
if (keys.erase(ALT) > 0) {
result += ALT_NAME;
result += Concatination;
}
if (keys.erase(SHIFT) > 0) {
result += SHIFT_NAME;
result += Concatination;
}
if (keys.erase(SUPER) > 0) {
result += SUPER_NAME;
result += Concatination;
}
keys.erase(CurrentView);
for (const auto &key : keys) {
switch (Keys(key.getKeyCode())) {
case Keys::Space: result += "SPACE"; break;
case Keys::Apostrophe: result += "'"; break;
case Keys::Comma: result += ","; break;
case Keys::Minus: result += "-"; break;
case Keys::Period: result += "."; break;
case Keys::Slash: result += "/"; break;
case Keys::Num0: result += "0"; break;
case Keys::Num1: result += "1"; break;
case Keys::Num2: result += "2"; break;
case Keys::Num3: result += "3"; break;
case Keys::Num4: result += "4"; break;
case Keys::Num5: result += "5"; break;
case Keys::Num6: result += "6"; break;
case Keys::Num7: result += "7"; break;
case Keys::Num8: result += "8"; break;
case Keys::Num9: result += "9"; break;
case Keys::Semicolon: result += ";"; break;
case Keys::Equals: result += "="; break;
case Keys::A: result += "A"; break;
case Keys::B: result += "B"; break;
case Keys::C: result += "C"; break;
case Keys::D: result += "D"; break;
case Keys::E: result += "E"; break;
case Keys::F: result += "F"; break;
case Keys::G: result += "G"; break;
case Keys::H: result += "H"; break;
case Keys::I: result += "I"; break;
case Keys::J: result += "J"; break;
case Keys::K: result += "K"; break;
case Keys::L: result += "L"; break;
case Keys::M: result += "M"; break;
case Keys::N: result += "N"; break;
case Keys::O: result += "O"; break;
case Keys::P: result += "P"; break;
case Keys::Q: result += "Q"; break;
case Keys::R: result += "R"; break;
case Keys::S: result += "S"; break;
case Keys::T: result += "T"; break;
case Keys::U: result += "U"; break;
case Keys::V: result += "V"; break;
case Keys::W: result += "W"; break;
case Keys::X: result += "X"; break;
case Keys::Y: result += "Y"; break;
case Keys::Z: result += "Z"; break;
case Keys::LeftBracket: result += "["; break;
case Keys::Backslash: result += "\\"; break;
case Keys::RightBracket: result += "]"; break;
case Keys::GraveAccent: result += "`"; break;
case Keys::World1: result += "WORLD1"; break;
case Keys::World2: result += "WORLD2"; break;
case Keys::Escape: result += "ESC"; break;
case Keys::Enter: result += "ENTER"; break;
case Keys::Tab: result += "TAB"; break;
case Keys::Backspace: result += "BACKSPACE"; break;
case Keys::Insert: result += "INSERT"; break;
case Keys::Delete: result += "DELETE"; break;
case Keys::Right: result += "RIGHT"; break;
case Keys::Left: result += "LEFT"; break;
case Keys::Down: result += "DOWN"; break;
case Keys::Up: result += "UP"; break;
case Keys::PageUp: result += "PAGEUP"; break;
case Keys::PageDown: result += "PAGEDOWN"; break;
case Keys::Home: result += "HOME"; break;
case Keys::End: result += "END"; break;
case Keys::CapsLock: result += "CAPSLOCK"; break;
case Keys::ScrollLock: result += "SCROLLLOCK"; break;
case Keys::NumLock: result += "NUMLOCK"; break;
case Keys::PrintScreen: result += "PRINTSCREEN"; break;
case Keys::Pause: result += "PAUSE"; break;
case Keys::F1: result += "F1"; break;
case Keys::F2: result += "F2"; break;
case Keys::F3: result += "F3"; break;
case Keys::F4: result += "F4"; break;
case Keys::F5: result += "F5"; break;
case Keys::F6: result += "F6"; break;
case Keys::F7: result += "F7"; break;
case Keys::F8: result += "F8"; break;
case Keys::F9: result += "F9"; break;
case Keys::F10: result += "F10"; break;
case Keys::F11: result += "F11"; break;
case Keys::F12: result += "F12"; break;
case Keys::F13: result += "F13"; break;
case Keys::F14: result += "F14"; break;
case Keys::F15: result += "F15"; break;
case Keys::F16: result += "F16"; break;
case Keys::F17: result += "F17"; break;
case Keys::F18: result += "F18"; break;
case Keys::F19: result += "F19"; break;
case Keys::F20: result += "F20"; break;
case Keys::F21: result += "F21"; break;
case Keys::F22: result += "F22"; break;
case Keys::F23: result += "F23"; break;
case Keys::F24: result += "F24"; break;
case Keys::F25: result += "F25"; break;
case Keys::KeyPad0: result += "KP0"; break;
case Keys::KeyPad1: result += "KP1"; break;
case Keys::KeyPad2: result += "KP2"; break;
case Keys::KeyPad3: result += "KP3"; break;
case Keys::KeyPad4: result += "KP4"; break;
case Keys::KeyPad5: result += "KP5"; break;
case Keys::KeyPad6: result += "KP6"; break;
case Keys::KeyPad7: result += "KP7"; break;
case Keys::KeyPad8: result += "KP8"; break;
case Keys::KeyPad9: result += "KP9"; break;
case Keys::KeyPadDecimal: result += "KPDECIMAL"; break;
case Keys::KeyPadDivide: result += "KPDIVIDE"; break;
case Keys::KeyPadMultiply: result += "KPMULTIPLY"; break;
case Keys::KeyPadSubtract: result += "KPSUBTRACT"; break;
case Keys::KeyPadAdd: result += "KPADD"; break;
case Keys::KeyPadEnter: result += "KPENTER"; break;
case Keys::KeyPadEqual: result += "KPEQUAL"; break;
case Keys::Menu: result += "MENU"; break;
default:
continue;
}
result += " + ";
}
if (result.ends_with(" + "))
result = result.substr(0, result.size() - 3);
return result;
}
private:
friend Shortcut operator+(const Key &lhs, const Key &rhs);
@@ -179,37 +364,54 @@ namespace hex {
return result;
}
constexpr static auto CTRL = Key(static_cast<Keys>(0x1000'0000));
constexpr static auto ALT = Key(static_cast<Keys>(0x2000'0000));
constexpr static auto SHIFT = Key(static_cast<Keys>(0x4000'0000));
constexpr static auto SUPER = Key(static_cast<Keys>(0x8000'0000));
#if defined(OS_MACOS)
constexpr static auto CTRLCMD = SUPER;
constexpr static auto CTRL_NAME = "CTRL";
constexpr static auto ALT_NAME = "OPT";
constexpr static auto SHIFT_NAME = "SHIFT";
constexpr static auto SUPER_NAME = "CMD";
constexpr static auto CTRLCMD_NAME = SUPER_NAME;
#else
constexpr static auto CTRLCMD = CTRL;
constexpr static auto CTRL_NAME = "CTRL";
constexpr static auto ALT_NAME = "ALT";
constexpr static auto SHIFT_NAME = "SHIFT";
constexpr static auto SUPER_NAME = "SUPER";
constexpr static auto CTRLCMD_NAME = CTRL_NAME;
#endif
/**
* @brief The ShortcutManager handles global and view-specific shortcuts.
* New shortcuts can be constructed using the + operator on Key objects. For example: CTRL + ALT + Keys::A
*/
class ShortcutManager {
public:
/**
* @brief Add a global shortcut. Global shortcuts can be triggered regardless of what view is currently focused
* @param shortcut The shortcut to add.
* @param callback The callback to call when the shortcut is triggered.
*/
static void addGlobalShortcut(const Shortcut &shortcut, const std::function<void()> &callback);
/**
* @brief Add a view-specific shortcut. View-specific shortcuts can only be triggered when the specified view is focused.
* @param view The view to add the shortcut to.
* @param shortcut The shortcut to add.
* @param callback The callback to call when the shortcut is triggered.
*/
static void addShortcut(View *view, const Shortcut &shortcut, const std::function<void()> &callback);
static void process(View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode);
/**
* @brief Process a key event. This should be called from the main loop.
* @param currentView Current view to process
* @param ctrl Whether the CTRL key is pressed
* @param alt Whether the ALT key is pressed
* @param shift Whether the SHIFT key is pressed
* @param super Whether the SUPER key is pressed
* @param focused Whether the current view is focused
* @param keyCode The key code of the key that was pressed
*/
static void process(const std::unique_ptr<View> &currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode);
/**
* @brief Process a key event. This should be called from the main loop.
* @param ctrl Whether the CTRL key is pressed
* @param alt Whether the ALT key is pressed
* @param shift Whether the SHIFT key is pressed
* @param super Whether the SUPER key is pressed
* @param keyCode The key code of the key that was pressed
*/
static void processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode);
/**
* @brief Clear all shortcuts
*/
static void clearShortcuts();
private:

View File

@@ -0,0 +1,39 @@
#pragma once
#include <hex.hpp>
#include <hex/helpers/fs.hpp>
#include <string>
#include <variant>
#include <nlohmann/json_fwd.hpp>
#include <imgui.h>
namespace hex {
class LayoutManager {
public:
struct Layout {
std::string name;
std::fs::path path;
};
static void save(const std::string &name);
static void load(const std::fs::path &path);
static void loadString(const std::string &content);
static std::vector<Layout> getLayouts();
static void process();
static void reload();
static void reset();
private:
LayoutManager() = default;
static std::optional<std::fs::path> s_layoutPathToLoad;
static std::optional<std::string> s_layoutStringToLoad;
static std::vector<Layout> s_layouts;
};
}

View File

@@ -5,6 +5,8 @@
#include <string>
#include <string_view>
#include <fmt/format.h>
namespace hex {
class LanguageDefinition {
@@ -56,4 +58,12 @@ namespace hex {
return LangEntry(string);
}
}
}
template<>
struct fmt::formatter<hex::LangEntry> : fmt::formatter<std::string_view> {
template<typename FormatContext>
auto format(const hex::LangEntry &entry, FormatContext &ctx) {
return fmt::formatter<std::string_view>::format(entry, ctx);
}
};

View File

@@ -14,42 +14,104 @@
namespace hex {
/**
* @brief Project file manager
*
* The project file manager is used to load and store project files. It is used by all features of ImHex
* that want to store any data to a Project File.
*
*/
class ProjectFile {
public:
struct Handler {
using Function = std::function<bool(const std::fs::path &, Tar &tar)>;
std::fs::path basePath;
bool required;
Function load, store;
std::fs::path basePath; //< Base path for where to store the files in the project file
bool required; //< If true, ImHex will display an error if this handler fails to load or store data
Function load, store; //< Functions to load and store data
};
struct ProviderHandler {
using Function = std::function<bool(prv::Provider *provider, const std::fs::path &, Tar &tar)>;
std::fs::path basePath;
bool required;
Function load, store;
std::fs::path basePath; //< Base path for where to store the files in the project file
bool required; //< If true, ImHex will display an error if this handler fails to load or store data
Function load, store; //< Functions to load and store data
};
/**
* @brief Load a project file
*
* @param filePath Path to the project file
* @return true if the project file was loaded successfully
* @return false if the project file was not loaded successfully
*/
static bool load(const std::fs::path &filePath);
/**
* @brief Store a project file
*
* @param filePath Path to the project file
* @return true if the project file was stored successfully
* @return false if the project file was not stored successfully
*/
static bool store(std::optional<std::fs::path> filePath = std::nullopt);
/**
* @brief Check if a project file is currently loaded
*
* @return true if a project file is currently loaded
* @return false if no project file is currently loaded
*/
static bool hasPath();
/**
* @brief Clear the currently loaded project file
*/
static void clearPath();
/**
* @brief Get the path to the currently loaded project file
* @return Path to the currently loaded project file
*/
static std::fs::path getPath();
/**
* @brief Set the path to the currently loaded project file
* @param path Path to the currently loaded project file
*/
static void setPath(const std::fs::path &path);
/**
* @brief Register a handler for storing and loading global data from a project file
*
* @param handler The handler to register
*/
static void registerHandler(const Handler &handler) {
getHandlers().push_back(handler);
}
/**
* @brief Register a handler for storing and loading per-provider data from a project file
*
* @param handler The handler to register
*/
static void registerPerProviderHandler(const ProviderHandler &handler) {
getProviderHandlers().push_back(handler);
}
/**
* @brief Get the list of registered handlers
* @return List of registered handlers
*/
static std::vector<Handler>& getHandlers() {
return s_handlers;
}
/**
* @brief Get the list of registered per-provider handlers
* @return List of registered per-provider handlers
*/
static std::vector<ProviderHandler>& getProviderHandlers() {
return s_providerHandlers;
}

View File

@@ -17,6 +17,9 @@ namespace hex {
class TaskHolder;
class TaskManager;
/**
* @brief A type representing a running asynchronous task
*/
class Task {
public:
Task() = default;
@@ -26,14 +29,38 @@ namespace hex {
Task(Task &&other) noexcept;
~Task();
/**
* @brief Updates the current process value of the task
* @param value Current value
*/
void update(u64 value = 0);
/**
* @brief Sets the maximum value of the task
* @param value Maximum value of the task
*/
void setMaxValue(u64 value);
/**
* @brief Interrupts the task
* For regular Tasks, this just throws an exception to stop the task.
* If a custom interrupt callback is set, an exception is thrown and the callback is called.
*/
void interrupt();
/**
* @brief Sets a callback that is called when the task is interrupted
* @param callback Callback to be called
*/
void setInterruptCallback(std::function<void()> callback);
[[nodiscard]] bool isBackgroundTask() const;
[[nodiscard]] bool isFinished() const;
[[nodiscard]] bool hadException() const;
[[nodiscard]] bool wasInterrupted() const;
[[nodiscard]] bool shouldInterrupt() const;
void clearException();
[[nodiscard]] std::string getExceptionMessage() const;
@@ -41,10 +68,6 @@ namespace hex {
[[nodiscard]] u64 getValue() const;
[[nodiscard]] u64 getMaxValue() const;
void interrupt();
void setInterruptCallback(std::function<void()> callback);
private:
void finish();
void interruption();
@@ -72,6 +95,9 @@ namespace hex {
friend class TaskManager;
};
/**
* @brief A type holding a weak reference to a Task
*/
class TaskHolder {
public:
TaskHolder() = default;
@@ -82,11 +108,16 @@ namespace hex {
[[nodiscard]] bool wasInterrupted() const;
[[nodiscard]] bool shouldInterrupt() const;
[[nodiscard]] u32 getProgress() const;
void interrupt();
private:
std::weak_ptr<Task> m_task;
};
/**
* @brief The Task Manager is responsible for running and managing asynchronous tasks
*/
class TaskManager {
public:
TaskManager() = delete;
@@ -96,19 +127,45 @@ namespace hex {
constexpr static auto NoProgress = 0;
/**
* @brief Creates a new asynchronous task that gets displayed in the Task Manager in the footer
* @param name Name of the task
* @param maxValue Maximum value of the task
* @param function Function to be executed
* @return A TaskHolder holding a weak reference to the task
*/
static TaskHolder createTask(std::string name, u64 maxValue, std::function<void(Task &)> function);
/**
* @brief Creates a new asynchronous task that does not get displayed in the Task Manager
* @param name Name of the task
* @param function Function to be executed
* @return A TaskHolder holding a weak reference to the task
*/
static TaskHolder createBackgroundTask(std::string name, std::function<void(Task &)> function);
/**
* @brief Creates a new synchronous task that will execute the given function at the start of the next frame
* @param function Function to be executed
*/
static void doLater(const std::function<void()> &function);
/**
* @brief Creates a callback that will be executed when all tasks are finished
* @param function Function to be executed
*/
static void runWhenTasksFinished(const std::function<void()> &function);
static void collectGarbage();
static size_t getRunningTaskCount();
static size_t getRunningBackgroundTaskCount();
static std::list<std::shared_ptr<Task>> &getRunningTasks();
static void doLater(const std::function<void()> &function);
static std::list<std::shared_ptr<Task>> &getRunningTasks();
static void runDeferredCalls();
static void runWhenTasksFinished(const std::function<void()> &function);
private:
static std::mutex s_deferredCallsMutex, s_tasksFinishedMutex;

View File

@@ -4,34 +4,92 @@
#include <hex/helpers/fs.hpp>
#include <string>
#include <variant>
#include <nlohmann/json_fwd.hpp>
#include <imgui.h>
namespace hex::api {
namespace hex {
/**
* @brief The Theme Manager takes care of loading and applying themes
*/
class ThemeManager {
public:
constexpr static auto NativeTheme = "Native";
using ColorMap = std::map<std::string, u32>;
struct Style {
std::variant<ImVec2*, float*> value;
float min;
float max;
bool needsScaling;
};
using StyleMap = std::map<std::string, Style>;
/**
* @brief Changes the current theme to the one with the given name
* @param name Name of the theme to change to
*/
static void changeTheme(std::string name);
/**
* @brief Adds a theme from json data
* @param content JSON data of the theme
*/
static void addTheme(const std::string &content);
static void addThemeHandler(const std::string &name, const std::function<void(std::string, std::string)> &handler);
static void addStyleHandler(const std::string &name, const std::function<void(std::string, std::string)> &handler);
/**
* @brief Adds a theme handler to handle color values loaded from a theme file
* @param name Name of the handler
* @param colorMap Map of color names to their respective constants
* @param getFunction Function to get the color value of a constant
* @param setFunction Function to set the color value of a constant
*/
static void addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction);
/**
* @brief Adds a style handler to handle style values loaded from a theme file
* @param name Name of the handler
* @param styleMap Map of style names to their respective constants
*/
static void addStyleHandler(const std::string &name, const StyleMap &styleMap);
static std::vector<std::string> getThemeNames();
static const std::string &getThemeImagePostfix();
static std::optional<ImColor> parseColorString(const std::string &colorString);
static nlohmann::json exportCurrentTheme(const std::string &name);
static void reset();
public:
struct ThemeHandler {
ColorMap colorMap;
std::function<ImColor(u32)> getFunction;
std::function<void(u32, ImColor)> setFunction;
};
struct StyleHandler {
StyleMap styleMap;
};
static std::map<std::string, ThemeHandler>& getThemeHandlers() { return s_themeHandlers; }
static std::map<std::string, StyleHandler>& getStyleHandlers() { return s_styleHandlers; }
private:
ThemeManager() = default;
static std::map<std::string, nlohmann::json> s_themes;
static std::map<std::string, std::function<void(std::string, std::string)>> s_themeHandlers, s_styleHandlers;
static std::map<std::string, ThemeHandler> s_themeHandlers;
static std::map<std::string, StyleHandler> s_styleHandlers;
static std::string s_imagePostfix;
static std::string s_currTheme;
};
}

View File

@@ -0,0 +1,2 @@
constexpr static const auto ImHexApiURL = "https://api.werwolv.net/imhex";
constexpr static const auto GitHubApiURL = "https://api.github.com/repos/WerWolv/ImHex";

View File

@@ -5,6 +5,7 @@
#include <hex/data_processor/attribute.hpp>
#include <set>
#include <span>
#include <string_view>
#include <vector>
@@ -77,7 +78,7 @@ namespace hex::dp {
const i128& getIntegerOnInput(u32 index);
const long double& getFloatOnInput(u32 index);
void setBufferOnOutput(u32 index, const std::vector<u8> &data);
void setBufferOnOutput(u32 index, std::span<const u8> data);
void setIntegerOnOutput(u32 index, i128 integer);
void setFloatOnOutput(u32 index, long double floatingPoint);

View File

@@ -16,7 +16,7 @@ namespace hex {
concept has_size = sizeof(T) == Size;
template<typename T>
class Cloneable {
class ICloneable {
public:
[[nodiscard]] virtual std::unique_ptr<T> clone() const = 0;
};

View File

@@ -8,25 +8,28 @@ namespace hex {
enum class Architecture : i32
{
ARM,
ARM64,
MIPS,
X86,
PPC,
SPARC,
SYSZ,
XCORE,
M68K,
TMS320C64X,
M680X,
EVM,
MOS65XX,
WASM,
BPF,
RISCV,
ARM = CS_ARCH_ARM,
ARM64 = CS_ARCH_ARM64,
MIPS = CS_ARCH_MIPS,
X86 = CS_ARCH_X86,
PPC = CS_ARCH_PPC,
SPARC = CS_ARCH_SPARC,
SYSZ = CS_ARCH_SYSZ,
XCORE = CS_ARCH_XCORE,
M68K = CS_ARCH_M68K,
TMS320C64X = CS_ARCH_TMS320C64X,
M680X = CS_ARCH_M680X,
EVM = CS_ARCH_EVM,
MAX,
MIN = ARM
#if CS_API_MAJOR >= 5
WASM = CS_ARCH_WASM,
RISCV = CS_ARCH_RISCV,
MOS65XX = CS_ARCH_MOS65XX,
BPF = CS_ARCH_BPF,
#endif
MAX = CS_ARCH_MAX,
MIN = ARM
};
class Disassembler {
@@ -39,7 +42,31 @@ namespace hex {
return cs_support(toCapstoneArchitecture(architecture));
}
constexpr static const char *const ArchitectureNames[] = { "ARM32", "ARM64", "MIPS", "x86", "PowerPC", "Sparc", "SystemZ", "XCore", "68K", "TMS320C64x", "680X", "Ethereum", "MOS65XX", "WebAssembly", "Berkeley Packet Filter", "RISC-V" };
constexpr static auto ArchitectureNames = [](){
std::array<const char *, static_cast<u32>(Architecture::MAX)> names = { };
names[CS_ARCH_ARM] = "ARM";
names[CS_ARCH_ARM64] = "AArch64";
names[CS_ARCH_MIPS] = "MIPS";
names[CS_ARCH_X86] = "Intel x86";
names[CS_ARCH_PPC] = "PowerPC";
names[CS_ARCH_SPARC] = "SPARC";
names[CS_ARCH_SYSZ] = "SystemZ";
names[CS_ARCH_XCORE] = "XCore";
names[CS_ARCH_M68K] = "Motorola 68K";
names[CS_ARCH_TMS320C64X] = "TMS320C64x";
names[CS_ARCH_M680X] = "M680X";
names[CS_ARCH_EVM] = "Ethereum Virtual Machine";
#if CS_API_MAJOR >= 5
names[CS_ARCH_WASM] = "WebAssembly";
names[CS_ARCH_RISCV] = "RISC-V";
names[CS_ARCH_MOS65XX] = "MOS Technology 65xx";
names[CS_ARCH_BPF] = "Berkeley Packet Filter";
#endif
return names;
}();
static inline i32 getArchitectureSupportedCount() {
static i32 supportedCount = -1;

View File

@@ -8,7 +8,8 @@
#include <span>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/file.hpp>
#include <wolv/io/file.hpp>
namespace hex {
@@ -21,17 +22,22 @@ namespace hex {
EncodingFile() = default;
EncodingFile(Type type, const std::fs::path &path);
EncodingFile(Type type, const std::string &path);
[[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]] bool valid() const { return this->m_valid; }
[[nodiscard]] const std::string& getTableContent() const { return this->m_tableContent; }
private:
void parseThingyFile(fs::File &file);
void parse(const std::string &content);
bool m_valid = false;
std::string m_tableContent;
std::map<size_t, std::map<std::vector<u8>, std::string>> m_mapping;
size_t m_longestSequence = 0;
};

View File

@@ -1,73 +0,0 @@
#pragma once
#include <hex.hpp>
#include <cstdio>
#include <string>
#include <vector>
#include <hex/helpers/fs.hpp>
#if defined(OS_MACOS)
#define off64_t off_t
#define fopen64 fopen
#define fseeko64 fseek
#define ftello64 ftell
#define ftruncate64 ftruncate
#endif
namespace hex::fs {
class File {
public:
enum class Mode
{
Read,
Write,
Create
};
explicit File(const std::fs::path &path, Mode mode) noexcept;
File() noexcept;
File(const File &) = delete;
File(File &&other) noexcept;
~File();
File &operator=(File &&other) noexcept;
[[nodiscard]] bool isValid() const {
return this->m_file != nullptr && fs::exists(this->m_path) && !fs::isDirectory(this->m_path);
}
void seek(u64 offset);
void close();
size_t readBuffer(u8 *buffer, size_t size);
std::vector<u8> readBytes(size_t numBytes = 0);
std::string readString(size_t numBytes = 0);
std::u8string readU8String(size_t numBytes = 0);
void write(const u8 *buffer, size_t size);
void write(const std::vector<u8> &bytes);
void write(const std::string &string);
void write(const std::u8string &string);
[[nodiscard]] size_t getSize() const;
void setSize(u64 size);
void flush();
bool remove();
auto getHandle() { return this->m_file; }
const std::fs::path &getPath() { return this->m_path; }
void disableBuffering();
private:
FILE *m_file;
std::fs::path m_path;
};
}

View File

@@ -10,80 +10,17 @@
#include <nfd.hpp>
namespace std::fs {
using namespace std::filesystem;
}
#include <wolv/io/fs.hpp>
namespace hex::fs {
[[maybe_unused]]
static inline bool exists(const std::fs::path &path) {
std::error_code error;
return std::filesystem::exists(path, error) && !error;
}
[[maybe_unused]]
static inline bool createDirectories(const std::fs::path &path) {
std::error_code error;
return std::filesystem::create_directories(path, error) && !error;
}
[[maybe_unused]]
static inline bool isRegularFile(const std::fs::path &path) {
std::error_code error;
return std::filesystem::is_regular_file(path, error) && !error;
}
[[maybe_unused]]
static inline bool copyFile(const std::fs::path &from, const std::fs::path &to, std::fs::copy_options = std::fs::copy_options::none) {
std::error_code error;
return std::filesystem::copy_file(from, to, error) && !error;
}
[[maybe_unused]]
static inline bool isDirectory(const std::fs::path &path) {
std::error_code error;
return std::filesystem::is_directory(path, error) && !error;
}
[[maybe_unused]]
static inline bool remove(const std::fs::path &path) {
std::error_code error;
return std::filesystem::remove(path, error) && !error;
}
[[maybe_unused]]
static inline bool removeAll(const std::fs::path &path) {
std::error_code error;
return std::filesystem::remove_all(path, error) && !error;
}
[[maybe_unused]]
static inline uintmax_t getFileSize(const std::fs::path &path) {
std::error_code error;
auto size = std::filesystem::file_size(path, error);
if (error) return 0;
else return size;
}
static inline bool isSubPath(const std::fs::path& base, const std::fs::path& destination) {
const auto relative = std::fs::relative(destination, base).u8string();
return relative.size() == 1 || (relative[0] != '.' && relative[1] != '.');
}
bool isPathWritable(const std::fs::path &path);
std::fs::path toShortPath(const std::fs::path &path);
enum class DialogMode {
Open,
Save,
Folder
};
void setFileBrowserErrorCallback(const std::function<void()> &callback);
void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback);
bool openFileBrowser(DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath = {}, bool multiple = false);
enum class ImHexPath : u32 {
@@ -103,12 +40,16 @@ namespace hex::fs {
Themes,
Libraries,
Nodes,
Layouts,
END
};
std::optional<std::fs::path> getExecutablePath();
bool isPathWritable(const std::fs::path &path);
std::vector<std::fs::path> getDefaultPaths(ImHexPath path, bool listNonExisting = false);
// temporarily expose these for the migration function
std::vector<std::fs::path> getDataPaths();
std::vector<std::fs::path> appendPath(std::vector<std::fs::path> paths, const std::fs::path &folder);
}

View File

@@ -1,12 +0,0 @@
#pragma once
#if defined(OS_MACOS)
#include <hex/helpers/fs.hpp>
extern "C" char * getMacExecutableDirectoryPath();
extern "C" char * getMacApplicationSupportDirectoryPath();
extern "C" void macFree(void *ptr);
#endif

View File

@@ -0,0 +1,325 @@
#pragma once
#include <hex.hpp>
#include <future>
#include <map>
#include <string>
#include <vector>
#include <curl/curl.h>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/core.hpp>
#include <wolv/utils/guards.hpp>
#include <wolv/utils/string.hpp>
#include <mbedtls/ssl.h>
namespace hex {
class HttpRequest {
public:
class ResultBase {
public:
ResultBase() = default;
explicit ResultBase(u32 statusCode) : m_statusCode(statusCode), m_valid(true) { }
[[nodiscard]] u32 getStatusCode() const {
return this->m_statusCode;
}
[[nodiscard]] bool isSuccess() const {
return this->getStatusCode() == 200;
}
[[nodiscard]] bool isValid() const {
return this->m_valid;
}
private:
u32 m_statusCode = 0;
bool m_valid = false;
};
template<typename T>
class Result : public ResultBase {
public:
Result() = default;
Result(u32 statusCode, T data) : ResultBase(statusCode), m_data(std::move(data)) { }
[[nodiscard]]
const T& getData() const {
return this->m_data;
}
private:
T m_data;
};
HttpRequest(std::string method, std::string url);
~HttpRequest();
HttpRequest(const HttpRequest&) = delete;
HttpRequest& operator=(const HttpRequest&) = delete;
HttpRequest(HttpRequest &&other) noexcept {
this->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);
this->m_caCert = std::move(other.m_caCert);
}
HttpRequest& operator=(HttpRequest &&other) noexcept {
this->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);
this->m_caCert = std::move(other.m_caCert);
return *this;
}
static void setCACert(std::string data) {
HttpRequest::s_caCertData = std::move(data);
}
static void setProxy(std::string proxy) {
HttpRequest::s_proxyUrl = std::move(proxy);
}
void setMethod(std::string method) {
this->m_method = std::move(method);
}
void setUrl(std::string url) {
this->m_url = std::move(url);
}
void addHeader(std::string key, std::string value) {
this->m_headers[std::move(key)] = std::move(value);
}
void setBody(std::string body) {
this->m_body = std::move(body);
}
void setTimeout(u32 timeout) {
this->m_timeout = timeout;
}
float getProgress() const {
return this->m_progress;
}
void cancel() {
this->m_canceled = true;
}
template<typename T = std::string>
std::future<Result<T>> downloadFile(const std::fs::path &path) {
return std::async(std::launch::async, [this, path] {
std::vector<u8> response;
wolv::io::File file(path, wolv::io::File::Mode::Create);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &file);
return this->executeImpl<T>(response);
});
}
std::future<Result<std::vector<u8>>> 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);
return this->executeImpl<std::vector<u8>>(response);
});
}
template<typename T = std::string>
std::future<Result<T>> uploadFile(const std::fs::path &path, const std::string &mimeName = "filename") {
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_mimepart *part = curl_mime_addpart(mime);
wolv::io::File file(path, wolv::io::File::Mode::Read);
curl_mime_data_cb(part, file.getSize(),
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
auto file = static_cast<FILE*>(arg);
return fread(buffer, size, nitems, file);
},
[](void *arg, curl_off_t offset, int origin) -> int {
auto file = static_cast<FILE*>(arg);
if (fseek(file, offset, origin) != 0)
return CURL_SEEKFUNC_CANTSEEK;
else
return CURL_SEEKFUNC_OK;
},
[](void *arg) {
auto file = static_cast<FILE*>(arg);
fclose(file);
},
file.getHandle());
curl_mime_filename(part, fileName.c_str());
curl_mime_name(part, mimeName.c_str());
curl_easy_setopt(this->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);
return this->executeImpl<T>(responseData);
});
}
template<typename T = std::string>
std::future<Result<T>> uploadFile(std::vector<u8> data, const std::string &mimeName = "filename") {
return std::async(std::launch::async, [this, data = std::move(data), mimeName]{
curl_mime *mime = curl_mime_init(this->m_curl);
curl_mimepart *part = curl_mime_addpart(mime);
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
curl_mime_filename(part, "data.bin");
curl_mime_name(part, mimeName.c_str());
curl_easy_setopt(this->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);
return this->executeImpl<T>(responseData);
});
}
template<typename T = std::string>
std::future<Result<T>> execute() {
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);
return this->executeImpl<T>(responseData);
});
}
std::string urlEncode(const std::string &input) {
auto escapedString = curl_easy_escape(this->m_curl, input.c_str(), std::strlen(input.c_str()));
if (escapedString != nullptr) {
std::string output = escapedString;
curl_free(escapedString);
return output;
}
return {};
}
std::string urlDecode(const std::string &input) {
auto unescapedString = curl_easy_unescape(this->m_curl, input.c_str(), std::strlen(input.c_str()), nullptr);
if (unescapedString != nullptr) {
std::string output = unescapedString;
curl_free(unescapedString);
return output;
}
return {};
}
protected:
void setDefaultConfig();
template<typename T>
Result<T> 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());
setDefaultConfig();
if (!this->m_body.empty()) {
curl_easy_setopt(this->m_curl, CURLOPT_POSTFIELDS, this->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) {
std::string header = hex::format("{}: {}", key, value);
headers = curl_slist_append(headers, header.c_str());
}
curl_easy_setopt(this->m_curl, CURLOPT_HTTPHEADER, headers);
{
std::scoped_lock lock(this->m_transmissionMutex);
auto result = curl_easy_perform(this->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));
if (!HttpRequest::s_proxyUrl.empty()){
log::info("A custom proxy '{0}' is in use. Is it working correctly?", HttpRequest::s_proxyUrl);
}
return { };
}
}
long statusCode = 0;
curl_easy_getinfo(this->m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
return Result<T>(statusCode, { data.begin(), data.end() });
}
[[maybe_unused]] static CURLcode sslCtxFunction(CURL *ctx, void *sslctx, void *userData);
static size_t writeToVector(void *contents, size_t size, size_t nmemb, void *userdata);
static size_t writeToFile(void *contents, size_t size, size_t nmemb, void *userdata);
static int progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow);
private:
CURL *m_curl;
std::mutex m_transmissionMutex;
std::string m_method;
std::string m_url;
std::string m_body;
std::map<std::string, std::string> m_headers;
u32 m_timeout = 1000;
std::atomic<float> m_progress = 0.0F;
std::atomic<bool> m_canceled = false;
[[maybe_unused]] std::unique_ptr<mbedtls_x509_crt> m_caCert;
static std::string s_caCertData, s_proxyUrl;
};
}

View File

@@ -17,7 +17,7 @@ namespace hex::log {
namespace {
[[maybe_unused]] void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level) {
const auto now = fmt::localtime(std::chrono::system_clock::now());
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);
@@ -31,9 +31,6 @@ namespace hex::log {
template<typename... T>
[[maybe_unused]] void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto... args) {
static std::mutex logMutex;
std::scoped_lock lock(logMutex);
auto dest = getDestination();
printPrefix(dest, ts, level);

View File

@@ -1,78 +0,0 @@
#pragma once
#include <hex.hpp>
#include <cstring>
#include <future>
#include <optional>
#include <string>
#include <filesystem>
#include <atomic>
#include <nlohmann/json_fwd.hpp>
#include <curl/curl.h>
#include <curl/system.h>
#include <mbedtls/ssl.h>
#include <hex/helpers/fs.hpp>
namespace hex {
template<typename T>
struct Response {
i32 code;
T body;
};
template<>
struct Response<void> {
i32 code;
};
class Net {
public:
Net();
~Net();
constexpr static u32 DefaultTimeout = 2'000;
std::future<Response<std::string>> getString(const std::string &url, u32 timeout = DefaultTimeout, const std::map<std::string, std::string> &extraHeaders = {}, const std::string &body = {});
std::future<Response<nlohmann::json>> getJson(const std::string &url, u32 timeout = DefaultTimeout, const std::map<std::string, std::string> &extraHeaders = {}, const std::string &body = {});
std::future<Response<nlohmann::json>> postJson(const std::string &url, u32 timeout = DefaultTimeout, const std::map<std::string, std::string> &extraHeaders = {}, const std::string &body = {});
std::future<Response<std::string>> uploadFile(const std::string &url, const std::fs::path &filePath, u32 timeout = DefaultTimeout);
std::future<Response<void>> downloadFile(const std::string &url, const std::fs::path &filePath, u32 timeout = DefaultTimeout);
[[nodiscard]] std::string encode(const std::string &input);
[[nodiscard]] std::string decode(const std::string &input);
[[nodiscard]] float getProgress() const { return this->m_progress; }
void cancel() { this->m_shouldCancel = true; }
static void setProxy(const std::string &url);
static void setCACert(const std::string &content);
private:
void setCommonSettings(std::string &response, const std::string &url, u32 timeout = 2000, const std::map<std::string, std::string> &extraHeaders = {}, const std::string &body = {});
std::optional<i32> execute();
friend int progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow);
friend CURLcode sslCtxFunction(CURL *ctx, void *sslctx, void *userData);
private:
CURL *m_ctx;
mbedtls_x509_crt m_caCert;
curl_slist *m_headers = nullptr;
std::mutex m_transmissionActive;
float m_progress = 0.0F;
bool m_shouldCancel = false;
static std::string s_proxyUrl;
static std::string s_caCert;
};
}

View File

@@ -75,6 +75,13 @@ namespace hex::gl {
return copy;
}
auto operator==(const Vector<T, Size>& other) {
for (size_t i = 0; i < Size; i++)
if (this->m_data[i] != other[i])
return false;
return true;
}
private:
std::array<T, Size> m_data;
};

View File

@@ -1,55 +0,0 @@
#pragma once
#include <hex.hpp>
#include <string>
#include <vector>
#if defined(OS_WINDOWS)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#define SOCKET_NONE INVALID_SOCKET
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#define SOCKET_NONE -1
#endif
namespace hex {
class Socket {
public:
Socket() = default;
Socket(const Socket &) = delete;
Socket(Socket &&other) noexcept;
Socket(const std::string &address, u16 port);
~Socket();
void connect(const std::string &address, u16 port);
void disconnect();
[[nodiscard]] bool isConnected() const;
[[nodiscard]] std::string readString(size_t size = 0x1000) const;
[[nodiscard]] std::vector<u8> readBytes(size_t size = 0x1000) const;
void writeString(const std::string &string) const;
void writeBytes(const std::vector<u8> &bytes) const;
private:
bool m_connected = false;
#if defined(OS_WINDOWS)
SOCKET m_socket = SOCKET_NONE;
#else
int m_socket = SOCKET_NONE;
#endif
};
}

View File

@@ -26,11 +26,11 @@ namespace hex {
void close();
[[nodiscard]] std::vector<u8> read(const std::fs::path &path);
[[nodiscard]] std::vector<u8> readVector(const std::fs::path &path);
[[nodiscard]] std::string readString(const std::fs::path &path);
void write(const std::fs::path &path, const std::vector<u8> &data);
void write(const std::fs::path &path, const std::string &data);
void writeVector(const std::fs::path &path, const std::vector<u8> &data);
void writeString(const std::fs::path &path, const std::string &data);
[[nodiscard]] std::vector<std::fs::path> listEntries(const std::fs::path &basePath = "/");
[[nodiscard]] bool contains(const std::fs::path &path);
@@ -42,6 +42,7 @@ namespace hex {
private:
mtar_t m_ctx = { };
std::fs::path m_path;
bool m_valid = false;
};

View File

@@ -19,14 +19,24 @@
#include <variant>
#include <vector>
#define TOKEN_CONCAT_IMPL(x, y) x##y
#define TOKEN_CONCAT(x, y) TOKEN_CONCAT_IMPL(x, y)
#define ANONYMOUS_VARIABLE(prefix) TOKEN_CONCAT(prefix, __COUNTER__)
struct ImVec2;
namespace hex {
template<typename T>
std::vector<T> sampleData(const std::vector<T> &data, size_t count) {
size_t stride = std::max(1.0, double(data.size()) / count);
std::vector<T> result;
result.reserve(count);
for (size_t i = 0; i < data.size(); i += stride) {
result.push_back(data[i]);
}
return result;
}
float operator""_scaled(long double value);
float operator""_scaled(unsigned long long value);
ImVec2 scaled(const ImVec2 &vector);
@@ -44,6 +54,7 @@ namespace hex {
std::string to_string(u128 value);
std::string to_string(i128 value);
std::optional<u8> parseBinaryString(const std::string &string);
std::string toByteString(u64 bytes);
std::string makePrintable(u8 c);
@@ -101,11 +112,6 @@ namespace hex {
return i;
}
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
template<size_t Size>
struct SizeTypeImpl { };
@@ -205,14 +211,6 @@ namespace hex {
std::string toEngineeringString(double value);
template<typename T>
std::vector<u8> toBytes(T value) {
std::vector<u8> bytes(sizeof(T));
std::memcpy(bytes.data(), &value, sizeof(T));
return bytes;
}
inline std::vector<u8> parseByteString(const std::string &string) {
auto byteString = std::string(string);
byteString.erase(std::remove(byteString.begin(), byteString.end(), ' '), byteString.end());
@@ -240,26 +238,6 @@ namespace hex {
return result;
}
template<typename T>
inline void trimLeft(std::basic_string<T> &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch) && ch >= 0x20;
}));
}
template<typename T>
inline void trimRight(std::basic_string<T> &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch) && ch >= 0x20;
}).base(), s.end());
}
template<typename T>
inline void trim(std::basic_string<T> &s) {
trimLeft(s);
trimRight(s);
}
float float16ToFloat32(u16 float16);
inline bool equalsIgnoreCase(const std::string &left, const std::string &right) {
@@ -276,13 +254,6 @@ namespace hex {
return iter != a.end();
}
template<typename T> requires requires(T t) { t.u8string(); }
std::string toUTF8String(const T &value) {
auto result = value.u8string();
return { result.begin(), result.end() };
}
template<typename T, typename... VariantTypes>
T get_or(const std::variant<VariantTypes...> &variant, T alt) {
const T *value = std::get_if<T>(&variant);
@@ -312,89 +283,6 @@ namespace hex {
return string.substr(0, maxLength - 3) + "...";
}
namespace scope_guard {
#define SCOPE_GUARD ::hex::scope_guard::ScopeGuardOnExit() + [&]()
#define ON_SCOPE_EXIT [[maybe_unused]] auto ANONYMOUS_VARIABLE(SCOPE_EXIT_) = SCOPE_GUARD
template<class F>
class ScopeGuard {
private:
F m_func;
bool m_active;
public:
explicit constexpr ScopeGuard(F func) : m_func(std::move(func)), m_active(true) { }
~ScopeGuard() {
if (this->m_active) { this->m_func(); }
}
void release() { this->m_active = false; }
ScopeGuard(ScopeGuard &&other) noexcept : m_func(std::move(other.m_func)), m_active(other.m_active) {
other.cancel();
}
ScopeGuard &operator=(ScopeGuard &&) = delete;
};
enum class ScopeGuardOnExit
{
};
template<typename F>
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F &&f) {
return ScopeGuard<F>(std::forward<F>(f));
}
}
namespace first_time_exec {
#define FIRST_TIME [[maybe_unused]] static auto ANONYMOUS_VARIABLE(FIRST_TIME_) = ::hex::first_time_exec::FirstTimeExecutor() + [&]()
template<class F>
class FirstTimeExecute {
public:
explicit constexpr FirstTimeExecute(F func) { func(); }
FirstTimeExecute &operator=(FirstTimeExecute &&) = delete;
};
enum class FirstTimeExecutor
{
};
template<typename F>
constexpr FirstTimeExecute<F> operator+(FirstTimeExecutor, F &&f) {
return FirstTimeExecute<F>(std::forward<F>(f));
}
}
namespace final_cleanup {
#define FINAL_CLEANUP [[maybe_unused]] static auto ANONYMOUS_VARIABLE(ON_EXIT_) = ::hex::final_cleanup::FinalCleanupExecutor() + [&]()
template<class F>
class FinalCleanupExecute {
F m_func;
public:
explicit constexpr FinalCleanupExecute(F func) : m_func(func) { }
constexpr ~FinalCleanupExecute() { this->m_func(); }
FinalCleanupExecute &operator=(FinalCleanupExecute &&) = delete;
};
enum class FinalCleanupExecutor
{
};
template<typename F>
constexpr FinalCleanupExecute<F> operator+(FinalCleanupExecutor, F &&f) {
return FinalCleanupExecute<F>(std::forward<F>(f));
}
}
std::optional<std::fs::path> getInitialFilePath();
}

View File

@@ -6,6 +6,10 @@
#include <hex.hpp>
/**
* This macro is used to define all the required entry points for a plugin.
* Name, Author and Description will be displayed in the in the plugin list on the Welcome screen.
*/
#define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(name, author, description)
#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \

View File

@@ -6,279 +6,24 @@
#include <hex/providers/provider.hpp>
#include <hex/helpers/literals.hpp>
#include <wolv/io/buffered_reader.hpp>
namespace hex::prv {
using namespace hex::literals;
class BufferedReader {
inline void providerReaderFunction(Provider *provider, void *buffer, u64 address, size_t size) {
provider->read(address, buffer, size);
}
class ProviderReader : public wolv::io::BufferedReader<prv::Provider, providerReaderFunction> {
public:
explicit BufferedReader(Provider *provider, size_t bufferSize = 16_MiB)
: m_provider(provider), m_bufferAddress(provider->getBaseAddress()), m_maxBufferSize(bufferSize),
m_startAddress(provider->getBaseAddress()), m_endAddress(provider->getBaseAddress() + provider->getActualSize() - 1LLU),
m_buffer(bufferSize) {
using BufferedReader::BufferedReader;
ProviderReader(Provider *provider, size_t bufferSize = 0x100000) : BufferedReader(provider, provider->getActualSize(), bufferSize) {
this->setEndAddress(provider->getBaseAddress() + provider->getActualSize() - 1);
this->seek(provider->getBaseAddress());
}
void seek(u64 address) {
this->m_startAddress = address;
}
void setEndAddress(u64 address) {
if (address >= this->m_provider->getActualSize())
address = this->m_provider->getActualSize() - 1;
this->m_endAddress = address;
}
[[nodiscard]] std::vector<u8> read(u64 address, size_t size) {
if (size > this->m_buffer.size()) {
std::vector<u8> result;
result.resize(size);
this->m_provider->read(address, result.data(), result.size());
return result;
}
this->updateBuffer(address, size);
auto result = &this->m_buffer[address - this->m_bufferAddress];
return { result, result + std::min(size, this->m_buffer.size()) };
}
[[nodiscard]] std::vector<u8> readReverse(u64 address, size_t size) {
if (size > this->m_buffer.size()) {
std::vector<u8> result;
result.resize(size);
this->m_provider->read(address, result.data(), result.size());
return result;
}
this->updateBuffer(address - std::min<u64>(address, this->m_buffer.size()), size);
auto result = &this->m_buffer[address - this->m_bufferAddress];
return { result, result + std::min(size, this->m_buffer.size()) };
}
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = u8;
using pointer = const value_type*;
using reference = const value_type&;
Iterator(BufferedReader *reader, u64 address) : m_reader(reader), m_address(address) {}
Iterator& operator++() {
this->m_address++;
return *this;
}
Iterator operator++(int) {
auto copy = *this;
this->m_address++;
return copy;
}
Iterator& operator--() {
this->m_address--;
return *this;
}
Iterator operator--(int) {
auto copy = *this;
this->m_address--;
return copy;
}
Iterator& operator+=(i64 offset) {
this->m_address += offset;
return *this;
}
Iterator& operator-=(i64 offset) {
this->m_address -= offset;
return *this;
}
value_type operator*() const {
return (*this)[0];
}
[[nodiscard]] u64 getAddress() const {
return this->m_address;
}
void setAddress(u64 address) {
this->m_address = address;
}
difference_type operator-(const Iterator &other) const {
return this->m_address - other.m_address;
}
Iterator operator+(i64 offset) const {
return { this->m_reader, this->m_address + offset };
}
value_type operator[](i64 offset) const {
auto result = this->m_reader->read(this->m_address + offset, 1);
if (result.empty())
return 0x00;
return result[0];
}
friend bool operator== (const Iterator& left, const Iterator& right) { return left.m_address == right.m_address; };
friend bool operator!= (const Iterator& left, const Iterator& right) { return left.m_address != right.m_address; };
friend bool operator> (const Iterator& left, const Iterator& right) { return left.m_address > right.m_address; };
friend bool operator< (const Iterator& left, const Iterator& right) { return left.m_address < right.m_address; };
friend bool operator>= (const Iterator& left, const Iterator& right) { return left.m_address >= right.m_address; };
friend bool operator<= (const Iterator& left, const Iterator& right) { return left.m_address <= right.m_address; };
private:
BufferedReader *m_reader;
u64 m_address;
};
class ReverseIterator {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = u8;
using pointer = const value_type*;
using reference = const value_type&;
ReverseIterator(BufferedReader *reader, u64 address) : m_reader(reader), m_address(address) {}
ReverseIterator& operator++() {
this->m_address--;
return *this;
}
ReverseIterator operator++(int) {
auto copy = *this;
this->m_address--;
return copy;
}
ReverseIterator& operator--() {
this->m_address++;
return *this;
}
ReverseIterator operator--(int) {
auto copy = *this;
this->m_address++;
return copy;
}
ReverseIterator& operator+=(i64 offset) {
this->m_address -= offset;
return *this;
}
ReverseIterator& operator-=(i64 offset) {
this->m_address += offset;
return *this;
}
value_type operator*() const {
return (*this)[0];
}
[[nodiscard]] u64 getAddress() const {
return this->m_address;
}
void setAddress(u64 address) {
this->m_address = address;
}
difference_type operator-(const ReverseIterator &other) const {
return other.m_address - this->m_address;
}
ReverseIterator operator+(i64 offset) const {
return { this->m_reader, this->m_address - offset };
}
value_type operator[](i64 offset) const {
auto result = this->m_reader->readReverse(this->m_address - offset, 1);
if (result.empty())
return 0x00;
return result[0];
}
friend bool operator== (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address == right.m_address; };
friend bool operator!= (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address != right.m_address; };
friend bool operator> (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address > right.m_address; };
friend bool operator< (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address < right.m_address; };
friend bool operator>= (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address >= right.m_address; };
friend bool operator<= (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address <= right.m_address; };
private:
BufferedReader *m_reader;
u64 m_address = 0x00;
};
Iterator begin() {
return { this, this->m_startAddress };
}
Iterator end() {
return { this, this->m_endAddress + 1 };
}
ReverseIterator rbegin() {
return { this, this->m_startAddress };
}
ReverseIterator rend() {
return { this, 0 };
}
private:
void updateBuffer(u64 address, size_t size) {
if (!this->m_bufferValid || address < this->m_bufferAddress || address + size > (this->m_bufferAddress + this->m_buffer.size())) {
const auto remainingBytes = (this->m_endAddress - address) + 1;
if (remainingBytes < this->m_maxBufferSize)
this->m_buffer.resize(remainingBytes);
else
this->m_buffer.resize(this->m_maxBufferSize);
this->m_provider->read(address, this->m_buffer.data(), this->m_buffer.size());
this->m_bufferAddress = address;
this->m_bufferValid = true;
}
}
private:
Provider *m_provider;
u64 m_bufferAddress = 0x00;
size_t m_maxBufferSize;
bool m_bufferValid = false;
u64 m_startAddress = 0x00, m_endAddress;
std::vector<u8> m_buffer;
};
}

View File

@@ -16,6 +16,9 @@
namespace hex::prv {
/**
* @brief Represent the data source for a tab in the UI
*/
class Provider {
public:
constexpr static size_t PageSize = 0x1000'0000;
@@ -29,7 +32,21 @@ namespace hex::prv {
[[nodiscard]] virtual bool isResizable() const = 0;
[[nodiscard]] virtual bool isSavable() const = 0;
/**
* @brief Read data from this provider, applying overlays and patches
* @param offset offset to start reading the data
* @param buffer buffer to write read data
* @param size number of bytes to read
* @param overlays apply overlays and patches is true. Same as readRaw() if false
*/
virtual void read(u64 offset, void *buffer, size_t size, bool overlays = true);
/**
* @brief Write data to the patches of this provider. Will not directly modify provider.
* @param offset offset to start writing the data
* @param buffer buffer to take data to write from
* @param size number of bytes to write
*/
virtual void write(u64 offset, const void *buffer, size_t size);
virtual void resize(size_t newSize);
@@ -39,7 +56,19 @@ namespace hex::prv {
virtual void save();
virtual void saveAs(const std::fs::path &path);
/**
* @brief Read data from this provider, without applying overlays and patches
* @param offset offset to start reading the data
* @param buffer buffer to write read data
* @param size number of bytes to read
*/
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
/**
* @brief Write data directly to this provider
* @param offset offset to start writing the data
* @param buffer buffer to take data to write from
* @param size number of bytes to write
*/
virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0;
[[nodiscard]] virtual size_t getActualSize() const = 0;
@@ -67,6 +96,13 @@ namespace hex::prv {
[[nodiscard]] virtual std::vector<std::pair<std::string, std::string>> getDataDescription() const = 0;
[[nodiscard]] virtual std::variant<std::string, i128> queryInformation(const std::string &category, const std::string &argument);
/**
* @brief Opens this provider
* the return value of this function allows to ensure the provider is available,
* so calling Provider::isAvailable() just after a call to open() that returned true is dedundant.
* @note This is not related to the EventProviderOpened event
* @return true if the provider was opened sucessfully, else false
*/
[[nodiscard]] virtual bool open() = 0;
virtual void close() = 0;
@@ -84,7 +120,7 @@ namespace hex::prv {
[[nodiscard]] virtual bool hasLoadInterface() const;
[[nodiscard]] virtual bool hasInterface() const;
virtual void drawLoadInterface();
virtual bool drawLoadInterface();
virtual void drawInterface();
[[nodiscard]] u32 getID() const;
@@ -103,19 +139,36 @@ namespace hex::prv {
void skipLoadInterface() { this->m_skipLoadInterface = true; }
[[nodiscard]] bool shouldSkipLoadInterface() const { return this->m_skipLoadInterface; }
void setErrorMessage(const std::string &errorMessage) { this->m_errorMessage = errorMessage; }
[[nodiscard]] const std::string& getErrorMessage() const { return this->m_errorMessage; }
protected:
u32 m_currPage = 0;
u64 m_baseAddress = 0;
u32 m_patchTreeOffset = 0;
std::list<std::map<u64, u8>> m_patches;
decltype(m_patches)::iterator m_currPatches;
std::list<Overlay *> m_overlays;
u32 m_id;
/**
* @brief true if there is any data that needs to be saved
*/
bool m_dirty = false;
/**
* @brief Control whetever to skip provider initialization
* initialization may be asking the user for information related to the provider,
* e.g. a process ID for the process memory provider
* this is used mainly when restoring a provider with already known initialization information
* for example when loading a project or loading a provider from the "recent" lsit
*
*/
bool m_skipLoadInterface = false;
std::string m_errorMessage;
private:
static u32 s_idCounter;
};

View File

@@ -0,0 +1,78 @@
#pragma once
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/providers/provider.hpp>
#include <concepts>
#include <map>
#include <utility>
namespace hex {
template<typename T>
class PerProvider {
public:
PerProvider() { this->onCreate(); }
PerProvider(const PerProvider&) = delete;
PerProvider(PerProvider&&) = delete;
PerProvider& operator=(const PerProvider&) = delete;
PerProvider& operator=(PerProvider&&) = delete;
PerProvider(T data) : m_data({ { ImHexApi::Provider::get(), std::move(data) } }) { this->onCreate(); }
~PerProvider() = default;
T* operator->() {
return &this->get();
}
const T* operator->() const {
return &this->get();
}
T& get(prv::Provider *provider = ImHexApi::Provider::get()) {
return this->m_data[provider];
}
const T& get(prv::Provider *provider = ImHexApi::Provider::get()) const {
return this->m_data[provider];
}
T& operator*() {
return this->get();
}
const T& operator*() const {
return this->get();
}
PerProvider& operator=(T data) {
this->m_data = std::move(data);
return *this;
}
operator T&() {
return this->get();
}
private:
void onCreate() {
(void)EventManager::subscribe<EventProviderOpened>([this](prv::Provider *provider) {
this->m_data.emplace(provider, T());
});
(void)EventManager::subscribe<EventProviderDeleted>([this](prv::Provider *provider){
this->m_data.erase(provider);
});
EventManager::subscribe<EventImHexClosing>([this] {
this->m_data.clear();
});
}
private:
std::map<prv::Provider *, T> m_data;
};
}

View File

@@ -26,6 +26,10 @@ enum ImGuiCustomCol {
ImGuiCustomCol_Highlight,
ImGuiCustomCol_IEEEToolSign,
ImGuiCustomCol_IEEEToolExp,
ImGuiCustomCol_IEEEToolMantissa,
ImGuiCustomCol_COUNT
};
@@ -75,6 +79,8 @@ namespace ImGui {
bool BulletHyperlink(const char *label, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
bool DescriptionButton(const char *label, const char *description, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
void HelpHover(const char *text);
void UnderlinedText(const char *label, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0));
void TextSpinner(const char *label);
@@ -151,4 +157,7 @@ namespace ImGui {
bool BitCheckbox(const char* label, bool* v);
bool DimmedButton(const char* label);
bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size_arg = ImVec2(0, 0));
}

View File

@@ -0,0 +1,92 @@
#pragma once
#include <hex.hpp>
#include <memory>
#include <string>
#include <vector>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/helpers/utils.hpp>
#include <hex/api/task.hpp>
namespace hex {
namespace impl {
class PopupBase {
public:
explicit PopupBase(std::string unlocalizedName, bool closeButton, bool modal)
: m_unlocalizedName(std::move(unlocalizedName)), m_closeButton(closeButton), m_modal(modal) { }
virtual ~PopupBase() = default;
virtual void drawContent() = 0;
[[nodiscard]] virtual ImGuiWindowFlags getFlags() const { return ImGuiWindowFlags_None; }
[[nodiscard]] virtual ImVec2 getMinSize() const {
return { 0, 0 };
}
[[nodiscard]] virtual ImVec2 getMaxSize() const {
return { 0, 0 };
}
[[nodiscard]] static std::vector<std::unique_ptr<PopupBase>> &getOpenPopups() {
return s_openPopups;
}
[[nodiscard]] const std::string &getUnlocalizedName() const {
return this->m_unlocalizedName;
}
[[nodiscard]] bool hasCloseButton() const {
return this->m_closeButton;
}
[[nodiscard]] bool isModal() const {
return this->m_modal;
}
void close() {
this->m_close = true;
}
[[nodiscard]] bool shouldClose() const {
return this->m_close;
}
protected:
static std::vector<std::unique_ptr<PopupBase>> s_openPopups;
private:
std::string m_unlocalizedName;
bool m_closeButton, m_modal;
std::atomic<bool> m_close = false;
};
}
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) { }
public:
template<typename ...Args>
static void open(Args && ... args) {
static std::mutex mutex;
std::lock_guard lock(mutex);
auto popup = std::make_unique<T>(std::forward<Args>(args)...);
s_openPopups.emplace_back(std::move(popup));
}
};
}

View File

@@ -13,6 +13,7 @@
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/providers/provider.hpp>
#include <hex/providers/provider_data.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/api/localization.hpp>
@@ -34,13 +35,6 @@ namespace hex {
[[nodiscard]] virtual bool isAvailable() const;
[[nodiscard]] virtual bool shouldProcess() const { return this->isAvailable() && this->getWindowOpenState(); }
static void showInfoPopup(const std::string &message);
static void showErrorPopup(const std::string &message);
static void showFatalPopup(const std::string &message);
static void showYesNoQuestionPopup(const std::string &message, const std::function<void()> &yesCallback, const std::function<void()> &noCallback);
static void showFileChooserPopup(const std::vector<std::fs::path> &paths, const std::vector<nfdfilteritem_t> &validExtensions, bool multiple, const std::function<void(std::fs::path)> &callback);
[[nodiscard]] virtual bool hasViewMenuItemEntry() const;
[[nodiscard]] virtual ImVec2 getMinSize() const;
[[nodiscard]] virtual ImVec2 getMaxSize() const;

View File

@@ -1,17 +1,17 @@
#include <hex/api/content_registry.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/ui/view.hpp>
#include <hex/data_processor/node.hpp>
#include <filesystem>
#include <fstream>
#include <thread>
#include <nlohmann/json.hpp>
#include <hex/data_processor/node.hpp>
#include <wolv/io/file.hpp>
namespace hex {
@@ -19,58 +19,89 @@ namespace hex {
constexpr auto SettingsFile = "settings.json";
void load() {
bool loaded = false;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
fs::File file(dir / SettingsFile, fs::File::Mode::Read);
namespace impl {
if (file.isValid()) {
getSettingsData() = nlohmann::json::parse(file.readString());
loaded = true;
break;
std::map<Category, std::vector<Entry>> &getEntries() {
static std::map<Category, std::vector<Entry>> entries;
return entries;
}
std::map<std::string, std::string> &getCategoryDescriptions() {
static std::map<std::string, std::string> descriptions;
return descriptions;
}
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName) {
auto &settings = getSettingsData();
if (!settings.contains(unlocalizedCategory)) return {};
if (!settings[unlocalizedCategory].contains(unlocalizedName)) return {};
return settings[unlocalizedCategory][unlocalizedName];
}
nlohmann::json &getSettingsData() {
static nlohmann::json settings;
return settings;
}
void load() {
bool loaded = false;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Read);
if (file.isValid()) {
getSettingsData() = nlohmann::json::parse(file.readString());
loaded = true;
break;
}
}
if (!loaded)
store();
}
void store() {
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Create);
if (file.isValid()) {
file.writeString(getSettingsData().dump(4));
break;
}
}
}
if (!loaded)
store();
}
void store() {
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
fs::File file(dir / SettingsFile, fs::File::Mode::Create);
if (file.isValid()) {
file.write(getSettingsData().dump(4));
break;
void clear() {
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
wolv::io::fs::remove(dir / SettingsFile);
}
}
}
void clear() {
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
hex::fs::remove(dir / SettingsFile);
}
}
static auto getCategoryEntry(const std::string &unlocalizedCategory) {
auto &entries = getEntries();
const size_t curSlot = entries.size();
auto found = entries.find(Category { unlocalizedCategory });
static auto getCategoryEntry(const std::string &unlocalizedCategory) {
auto &entries = getEntries();
const size_t curSlot = entries.size();
auto found = entries.find(Category { unlocalizedCategory });
if (found == entries.end()) {
auto [iter, _] = entries.emplace(Category { unlocalizedCategory, curSlot }, std::vector<Entry> {});
return iter;
}
if (found == entries.end()) {
auto [iter, _] = entries.emplace(Category { unlocalizedCategory, curSlot }, std::vector<Entry> {});
return iter;
return found;
}
return found;
}
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback, bool requiresRestart) {
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const impl::Callback &callback, bool requiresRestart) {
log::debug("Registered new integer setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName, requiresRestart, callback });
impl::getCategoryEntry(unlocalizedCategory)->second.emplace_back(impl::Entry { unlocalizedName, requiresRestart, callback });
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
@@ -78,12 +109,12 @@ namespace hex {
json[unlocalizedCategory][unlocalizedName] = int(defaultValue);
}
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback, bool requiresRestart) {
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const impl::Callback &callback, bool requiresRestart) {
log::debug("Registered new string setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName, requiresRestart, callback });
impl::getCategoryEntry(unlocalizedCategory)->second.emplace_back(impl::Entry { unlocalizedName, requiresRestart, callback });
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
@@ -91,12 +122,12 @@ namespace hex {
json[unlocalizedCategory][unlocalizedName] = std::string(defaultValue);
}
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const Callback &callback, bool requiresRestart) {
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const impl::Callback &callback, bool requiresRestart) {
log::debug("Registered new string array setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName, requiresRestart, callback });
impl::getCategoryEntry(unlocalizedCategory)->second.emplace_back(impl::Entry { unlocalizedName, requiresRestart, callback });
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
@@ -105,11 +136,11 @@ namespace hex {
}
void addCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription) {
getCategoryDescriptions()[unlocalizedCategory] = unlocalizedCategoryDescription;
impl::getCategoryDescriptions()[unlocalizedCategory] = unlocalizedCategoryDescription;
}
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value) {
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
@@ -118,7 +149,7 @@ namespace hex {
}
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value) {
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
@@ -127,7 +158,7 @@ namespace hex {
}
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &value) {
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
@@ -137,7 +168,7 @@ namespace hex {
i64 read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue) {
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
return defaultValue;
@@ -151,7 +182,7 @@ namespace hex {
}
std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue) {
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
return defaultValue;
@@ -165,7 +196,7 @@ namespace hex {
}
std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue) {
auto &json = getSettingsData();
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
return defaultValue;
@@ -181,49 +212,37 @@ namespace hex {
return json[unlocalizedCategory][unlocalizedName].get<std::vector<std::string>>();
}
std::map<Category, std::vector<Entry>> &getEntries() {
static std::map<Category, std::vector<Entry>> entries;
return entries;
}
std::map<std::string, std::string> &getCategoryDescriptions() {
static std::map<std::string, std::string> descriptions;
return descriptions;
}
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName) {
auto &settings = getSettingsData();
if (!settings.contains(unlocalizedCategory)) return {};
if (!settings[unlocalizedCategory].contains(unlocalizedName)) return {};
return settings[unlocalizedCategory][unlocalizedName];
}
nlohmann::json &getSettingsData() {
static nlohmann::json settings;
return settings;
}
}
namespace ContentRegistry::CommandPaletteCommands {
void add(Type type, const std::string &command, const std::string &unlocalizedDescription, const DisplayCallback &displayCallback, const ExecuteCallback &executeCallback) {
void add(Type type, const std::string &command, const std::string &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) {
log::debug("Registered new command palette command: {}", command);
getEntries().push_back(ContentRegistry::CommandPaletteCommands::Entry { type, command, unlocalizedDescription, displayCallback, executeCallback });
impl::getEntries().push_back(ContentRegistry::CommandPaletteCommands::impl::Entry { type, command, unlocalizedDescription, displayCallback, executeCallback });
}
std::vector<Entry> &getEntries() {
static std::vector<Entry> commands;
void addHandler(Type type, const std::string &command, const impl::QueryCallback &queryCallback, const impl::DisplayCallback &displayCallback) {
log::debug("Registered new command palette command handler: {}", command);
impl::getHandlers().push_back(ContentRegistry::CommandPaletteCommands::impl::Handler { type, command, queryCallback, displayCallback });
}
namespace impl {
std::vector<Entry> &getEntries() {
static std::vector<Entry> commands;
return commands;
}
std::vector<Handler> &getHandlers() {
static std::vector<Handler> commands;
return commands;
}
return commands;
}
}
@@ -241,6 +260,18 @@ namespace hex {
return functionName;
}
pl::PatternLanguage& getRuntime() {
static PerProvider<pl::PatternLanguage> runtime;
return *runtime;
}
std::mutex& getRuntimeLock() {
static std::mutex runtimeLock;
return runtimeLock;
}
void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider) {
runtime.reset();
@@ -258,14 +289,14 @@ namespace hex {
runtime.setIncludePaths(fs::getDefaultPaths(fs::ImHexPath::PatternsInclude) | fs::getDefaultPaths(fs::ImHexPath::Patterns));
for (const auto &func : getFunctions()) {
for (const auto &func : impl::getFunctions()) {
if (func.dangerous)
runtime.addDangerousFunction(func.ns, func.name, func.parameterCount, func.callback);
else
runtime.addFunction(func.ns, func.name, func.parameterCount, func.callback);
}
for (const auto &[name, callback] : getPragmas()) {
for (const auto &[name, callback] : impl::getPragmas()) {
runtime.addPragma(name, callback);
}
@@ -276,13 +307,13 @@ namespace hex {
void addPragma(const std::string &name, const pl::api::PragmaHandler &handler) {
log::debug("Registered new pattern language pragma: {}", name);
getPragmas()[name] = handler;
impl::getPragmas()[name] = handler;
}
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) {
log::debug("Registered new pattern language function: {}", getFunctionName(ns, name));
getFunctions().push_back({
impl::getFunctions().push_back({
ns, name,
parameterCount, func,
false
@@ -292,7 +323,7 @@ namespace hex {
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) {
log::debug("Registered new dangerous pattern language function: {}", getFunctionName(ns, name));
getFunctions().push_back({
impl::getFunctions().push_back({
ns, name,
parameterCount, func,
true
@@ -300,51 +331,61 @@ namespace hex {
}
std::map<std::string, impl::Visualizer> &impl::getVisualizers() {
static std::map<std::string, impl::Visualizer> visualizers;
return visualizers;
}
void addVisualizer(const std::string &name, const VisualizerFunctionCallback &function, u32 parameterCount) {
void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, u32 parameterCount) {
log::debug("Registered new pattern visualizer function: {}", name);
impl::getVisualizers()[name] = impl::Visualizer { parameterCount, function };
}
std::map<std::string, pl::api::PragmaHandler> &getPragmas() {
static std::map<std::string, pl::api::PragmaHandler> pragmas;
return pragmas;
namespace impl {
std::map<std::string, impl::Visualizer> &getVisualizers() {
static std::map<std::string, impl::Visualizer> visualizers;
return visualizers;
}
std::map<std::string, pl::api::PragmaHandler> &getPragmas() {
static std::map<std::string, pl::api::PragmaHandler> pragmas;
return pragmas;
}
std::vector<impl::FunctionDefinition> &getFunctions() {
static std::vector<impl::FunctionDefinition> functions;
return functions;
}
}
std::vector<impl::FunctionDefinition> &getFunctions() {
static std::vector<impl::FunctionDefinition> functions;
return functions;
}
}
namespace ContentRegistry::Views {
void impl::add(View *view) {
namespace impl {
std::map<std::string, std::unique_ptr<View>> &getEntries() {
static std::map<std::string, std::unique_ptr<View>> views;
return views;
}
}
void impl::add(std::unique_ptr<View> &&view) {
log::debug("Registered new view: {}", view->getUnlocalizedName());
getEntries().insert({ view->getUnlocalizedName(), view });
impl::getEntries().insert({ view->getUnlocalizedName(), std::move(view) });
}
std::map<std::string, View *> &getEntries() {
static std::map<std::string, View *> views;
return views;
}
View *getViewByName(const std::string &unlocalizedName) {
auto &views = getEntries();
View* getViewByName(const std::string &unlocalizedName) {
auto &views = impl::getEntries();
if (views.contains(unlocalizedName))
return views[unlocalizedName];
return views[unlocalizedName].get();
else
return nullptr;
}
@@ -356,13 +397,17 @@ namespace hex {
void add(const std::string &unlocalizedName, const impl::Callback &function) {
log::debug("Registered new tool: {}", unlocalizedName);
getEntries().emplace_back(impl::Entry { unlocalizedName, function, false });
impl::getEntries().emplace_back(impl::Entry { unlocalizedName, function, false });
}
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> entries;
namespace impl {
std::vector<Entry> &getEntries() {
static std::vector<Entry> tools;
return tools;
}
return entries;
}
}
@@ -372,21 +417,26 @@ namespace hex {
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);
getEntries().push_back({ unlocalizedName, requiredSize, requiredSize, std::move(displayGeneratorFunction), std::move(editingFunction) });
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);
getEntries().push_back({ unlocalizedName, requiredSize, maxSize, std::move(displayGeneratorFunction), std::move(editingFunction) });
impl::getEntries().push_back({ unlocalizedName, requiredSize, maxSize, std::move(displayGeneratorFunction), std::move(editingFunction) });
}
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> entries;
namespace impl {
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> entries;
return entries;
}
return entries;
}
}
namespace ContentRegistry::DataProcessorNode {
@@ -398,13 +448,17 @@ namespace hex {
}
void addSeparator() {
getEntries().push_back({ "", "", [] { return nullptr; } });
impl::getEntries().push_back({ "", "", [] { return nullptr; } });
}
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> nodes;
namespace impl {
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> nodes;
return nodes;
}
return nodes;
}
}
@@ -437,7 +491,7 @@ namespace hex {
LangEntry::setFallbackLanguage(code.get<std::string>());
}
getLanguages().insert({ code.get<std::string>(), hex::format("{} ({})", language.get<std::string>(), country.get<std::string>()) });
impl::getLanguages().insert({ code.get<std::string>(), hex::format("{} ({})", language.get<std::string>(), country.get<std::string>()) });
std::map<std::string, std::string> translationDefinitions;
for (auto &[key, value] : translations.items()) {
@@ -449,20 +503,25 @@ namespace hex {
translationDefinitions[key] = value.get<std::string>();
}
getLanguageDefinitions()[code.get<std::string>()].emplace_back(std::move(translationDefinitions));
impl::getLanguageDefinitions()[code.get<std::string>()].emplace_back(std::move(translationDefinitions));
}
std::map<std::string, std::string> &getLanguages() {
static std::map<std::string, std::string> languages;
namespace impl {
std::map<std::string, std::string> &getLanguages() {
static std::map<std::string, std::string> languages;
return languages;
}
std::map<std::string, std::vector<LanguageDefinition>> &getLanguageDefinitions() {
static std::map<std::string, std::vector<LanguageDefinition>> definitions;
return definitions;
}
return languages;
}
std::map<std::string, std::vector<LanguageDefinition>> &getLanguageDefinitions() {
static std::map<std::string, std::vector<LanguageDefinition>> definitions;
return definitions;
}
}
@@ -471,85 +530,97 @@ namespace hex {
void registerMainMenuItem(const std::string &unlocalizedName, u32 priority) {
log::debug("Registered new main menu item: {}", unlocalizedName);
getMainMenuItems().insert({ priority, { unlocalizedName } });
impl::getMainMenuItems().insert({ priority, { unlocalizedName } });
}
void addMenuItem(const std::string &unlocalizedMainMenuName, u32 priority, const impl::DrawCallback &function) {
log::debug("Added new menu item to menu {} with priority {}", unlocalizedMainMenuName, priority);
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);
getMenuItems().insert({
priority, {unlocalizedMainMenuName, function}
impl::getMenuItems().insert({
priority, { unlocalizedMainMenuNames, shortcut, function, enabledCallback }
});
if (shortcut.isLocal() && view != nullptr)
ShortcutManager::addShortcut(view, shortcut, function);
else
ShortcutManager::addGlobalShortcut(shortcut, function);
}
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);
unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue);
impl::getMenuItems().insert({
priority, { unlocalizedMainMenuNames, {}, function, enabledCallback }
});
}
void addMenuItemSeparator(std::vector<std::string> unlocalizedMainMenuNames, u32 priority) {
unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue);
impl::getMenuItems().insert({
priority, { unlocalizedMainMenuNames, {}, []{}, []{ return true; } }
});
}
void addWelcomeScreenEntry(const impl::DrawCallback &function) {
getWelcomeScreenEntries().push_back(function);
impl::getWelcomeScreenEntries().push_back(function);
}
void addFooterItem(const impl::DrawCallback &function) {
getFooterItems().push_back(function);
impl::getFooterItems().push_back(function);
}
void addToolbarItem(const impl::DrawCallback &function) {
getToolbarItems().push_back(function);
impl::getToolbarItems().push_back(function);
}
void addSidebarItem(const std::string &icon, const impl::DrawCallback &function) {
getSidebarItems().push_back({ icon, function });
impl::getSidebarItems().push_back({ icon, function });
}
void addTitleBarButton(const std::string &icon, const std::string &unlocalizedTooltip, const impl::ClickCallback &function) {
getTitleBarButtons().push_back({ icon, unlocalizedTooltip, function });
impl::getTitleBarButtons().push_back({ icon, unlocalizedTooltip, function });
}
void addLayout(const std::string &unlocalizedName, const impl::LayoutFunction &function) {
log::debug("Added new layout: {}", unlocalizedName);
namespace impl {
getLayouts().push_back({ unlocalizedName, function });
}
std::multimap<u32, impl::MainMenuItem> &getMainMenuItems() {
static std::multimap<u32, impl::MainMenuItem> items;
return items;
}
std::multimap<u32, impl::MenuItem> &getMenuItems() {
static std::multimap<u32, impl::MenuItem> items;
std::multimap<u32, impl::MainMenuItem> &getMainMenuItems() {
static std::multimap<u32, impl::MainMenuItem> items;
return items;
}
return items;
}
std::multimap<u32, impl::MenuItem> &getMenuItems() {
static std::multimap<u32, impl::MenuItem> items;
std::vector<impl::DrawCallback> &getWelcomeScreenEntries() {
static std::vector<impl::DrawCallback> entries;
return items;
}
return entries;
}
std::vector<impl::DrawCallback> &getFooterItems() {
static std::vector<impl::DrawCallback> items;
std::vector<impl::DrawCallback> &getWelcomeScreenEntries() {
static std::vector<impl::DrawCallback> entries;
return items;
}
std::vector<impl::DrawCallback> &getToolbarItems() {
static std::vector<impl::DrawCallback> items;
return entries;
}
std::vector<impl::DrawCallback> &getFooterItems() {
static std::vector<impl::DrawCallback> items;
return items;
}
std::vector<impl::SidebarItem> &getSidebarItems() {
static std::vector<impl::SidebarItem> items;
return items;
}
std::vector<impl::DrawCallback> &getToolbarItems() {
static std::vector<impl::DrawCallback> items;
return items;
}
std::vector<impl::TitleBarButton> &getTitleBarButtons() {
static std::vector<impl::TitleBarButton> buttons;
return items;
}
std::vector<impl::SidebarItem> &getSidebarItems() {
static std::vector<impl::SidebarItem> items;
return buttons;
}
return items;
}
std::vector<impl::TitleBarButton> &getTitleBarButtons() {
static std::vector<impl::TitleBarButton> buttons;
return buttons;
}
std::vector<impl::Layout> &getLayouts() {
static std::vector<impl::Layout> layouts;
return layouts;
}
}
@@ -562,12 +633,18 @@ namespace hex {
getEntries().push_back(unlocalizedName);
}
std::vector<std::string> &getEntries() {
static std::vector<std::string> providerNames;
return providerNames;
namespace impl {
std::vector<std::string> &getEntries() {
static std::vector<std::string> providerNames;
return providerNames;
}
}
}
namespace ContentRegistry::DataFormatter {
@@ -575,13 +652,17 @@ namespace hex {
void add(const std::string &unlocalizedName, const impl::Callback &callback) {
log::debug("Registered new data formatter: {}", unlocalizedName);
getEntries().push_back({ unlocalizedName, callback });
impl::getEntries().push_back({ unlocalizedName, callback });
}
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> entries;
namespace impl {
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> entries;
return entries;
}
return entries;
}
}
@@ -592,13 +673,17 @@ namespace hex {
for (const auto &extension : extensions)
log::debug("Registered new data handler for extensions: {}", extension);
getEntries().push_back({ extensions, callback });
impl::getEntries().push_back({ extensions, callback });
}
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> entries;
namespace impl {
std::vector<impl::Entry> &getEntries() {
static std::vector<impl::Entry> entries;
return entries;
}
return entries;
}
}
@@ -607,7 +692,7 @@ namespace hex {
const int DataVisualizer::TextInputFlags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll;
bool DataVisualizer::drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const {
bool DataVisualizer::drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const {
struct UserData {
u8 *data;
i32 maxChars;
@@ -636,29 +721,127 @@ namespace hex {
return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape);
}
void impl::addDataVisualizer(const std::string &unlocalizedName, DataVisualizer *visualizer) {
getVisualizers().insert({ unlocalizedName, visualizer });
bool DataVisualizer::drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const {
struct UserData {
std::string *data;
i32 maxChars;
bool editingDone;
};
UserData userData = {
.data = &data,
.maxChars = this->getMaxCharsPerCell(),
.editingDone = false
};
ImGui::PushID(reinterpret_cast<void*>(address));
ImGui::InputText("##editing_input", data.data(), data.size() + 1, flags | TextInputFlags | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int {
auto &userData = *reinterpret_cast<UserData*>(data->UserData);
userData.data->resize(data->BufSize);
if (data->BufTextLen >= userData.maxChars)
userData.editingDone = true;
return 0;
}, &userData);
ImGui::PopID();
return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape);
}
namespace impl {
void addDataVisualizer(const std::string &unlocalizedName, DataVisualizer *visualizer) {
getVisualizers().insert({ unlocalizedName, visualizer });
}
std::map<std::string, DataVisualizer*> &getVisualizers() {
static std::map<std::string, DataVisualizer*> visualizers;
return visualizers;
}
}
std::map<std::string, DataVisualizer*> &impl::getVisualizers() {
static std::map<std::string, DataVisualizer*> visualizers;
return visualizers;
}
}
namespace ContentRegistry::Hashes {
std::vector<Hash *> &impl::getHashes() {
static std::vector<Hash *> hashes;
namespace impl {
std::vector<Hash *> &getHashes() {
static std::vector<Hash *> hashes;
return hashes;
}
void add(Hash *hash) {
getHashes().push_back(hash);
}
return hashes;
}
void impl::add(Hash *hash) {
getHashes().push_back(hash);
}
namespace ContentRegistry::BackgroundServices {
namespace impl {
std::vector<Service> &getServices() {
static std::vector<Service> services;
return services;
}
void stopServices() {
for (auto &service : getServices()) {
service.thread.request_stop();
}
for (auto &service : getServices()) {
service.thread.detach();
}
}
}
void registerService(const std::string &unlocalizedName, const impl::Callback &callback) {
log::debug("Registered new background service: {}", unlocalizedName);
impl::getServices().push_back(impl::Service {
unlocalizedName,
std::jthread([callback](const std::stop_token &stopToken){
while (!stopToken.stop_requested()) {
callback();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
})
});
}
}
namespace ContentRegistry::CommunicationInterface {
namespace impl {
std::map<std::string, NetworkCallback> &getNetworkEndpoints() {
static std::map<std::string, NetworkCallback> endpoints;
return endpoints;
}
}
void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback) {
log::debug("Registered new network endpoint: {}", endpoint);
impl::getNetworkEndpoints().insert({ endpoint, callback });
}
}

View File

@@ -2,6 +2,7 @@
#include <hex/api/content_registry.hpp>
#include <hex/api/event.hpp>
#include <hex/api/task.hpp>
#include <hex/providers/provider.hpp>
#include <utility>
@@ -19,19 +20,6 @@
namespace hex {
namespace ImHexApi::Common {
void closeImHex(bool noQuestions) {
EventManager::post<RequestCloseImHex>(noQuestions);
}
void restartImHex() {
EventManager::post<RequestRestartImHex>();
EventManager::post<RequestCloseImHex>(false);
}
}
namespace ImHexApi::HexEditor {
@@ -190,6 +178,10 @@ namespace hex {
return impl::s_currentSelection;
}
void clearSelection() {
impl::s_currentSelection.reset();
}
void setSelection(const Region &region, prv::Provider *provider) {
setSelection(ProviderRegion { region, provider == nullptr ? Provider::get() : provider });
}
@@ -310,8 +302,6 @@ namespace hex {
if (it == s_providers.end())
return;
EventManager::post<EventProviderDeleted>(provider);
if (!s_providers.empty() && it - s_providers.begin() == s_currentProvider)
setCurrentProvider(0);
@@ -323,6 +313,7 @@ namespace hex {
EventManager::post<EventProviderClosed>(provider);
TaskManager::runWhenTasksFinished([provider] {
EventManager::post<EventProviderDeleted>(provider);
delete provider;
});
}
@@ -408,6 +399,14 @@ namespace hex {
}
void closeImHex(bool noQuestions) {
EventManager::post<RequestCloseImHex>(noQuestions);
}
void restartImHex() {
EventManager::post<RequestRestartImHex>();
EventManager::post<RequestCloseImHex>(false);
}
void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress) {
EventManager::post<EventSetTaskBarIconState>(u32(state), u32(type), progress);
@@ -433,7 +432,7 @@ namespace hex {
}
static float s_targetFPS = 60.0F;
static float s_targetFPS = 14.0F;
float getTargetFPS() {
return s_targetFPS;

View File

@@ -15,7 +15,7 @@ namespace hex {
view->m_shortcuts.insert({ shortcut, callback });
}
static Shortcut getShortcut(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {
static Shortcut getShortcut(bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {
Shortcut pressedShortcut;
if (ctrl)
@@ -26,21 +26,23 @@ namespace hex {
pressedShortcut += SHIFT;
if (super)
pressedShortcut += SUPER;
if (focused)
pressedShortcut += CurrentView;
pressedShortcut += static_cast<Keys>(keyCode);
return pressedShortcut;
}
void ShortcutManager::process(View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, keyCode);
void ShortcutManager::process(const std::unique_ptr<View> &currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);
if (focused && currentView->m_shortcuts.contains(pressedShortcut))
if (currentView->m_shortcuts.contains(pressedShortcut))
currentView->m_shortcuts[pressedShortcut]();
}
void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, keyCode);
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode);
if (ShortcutManager::s_globalShortcuts.contains(pressedShortcut))
ShortcutManager::s_globalShortcuts[pressedShortcut]();

View File

@@ -0,0 +1,83 @@
#include <hex/api/layout_manager.hpp>
#include <hex/helpers/fs.hpp>
#include <wolv/utils/string.hpp>
#include <imgui.h>
#include <fmt/format.h>
namespace hex {
std::optional<std::fs::path> LayoutManager::s_layoutPathToLoad;
std::optional<std::string> LayoutManager::s_layoutStringToLoad;
std::vector<LayoutManager::Layout> LayoutManager::s_layouts;
void LayoutManager::load(const std::fs::path &path) {
s_layoutPathToLoad = path;
}
void LayoutManager::loadString(const std::string &content) {
s_layoutStringToLoad = content;
}
void LayoutManager::save(const std::string &name) {
auto fileName = name;
fileName = wolv::util::replaceStrings(fileName, " ", "_");
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());
LayoutManager::reload();
}
std::vector<LayoutManager::Layout> LayoutManager::getLayouts() {
return s_layouts;
}
void LayoutManager::process() {
if (s_layoutPathToLoad.has_value()) {
ImGui::LoadIniSettingsFromDisk(wolv::util::toUTF8String(*s_layoutPathToLoad).c_str());
s_layoutPathToLoad = std::nullopt;
}
if (s_layoutStringToLoad.has_value()) {
ImGui::LoadIniSettingsFromMemory(s_layoutStringToLoad->c_str());
s_layoutStringToLoad = std::nullopt;
}
}
void LayoutManager::reload() {
s_layouts.clear();
for (const auto &directory : hex::fs::getDefaultPaths(fs::ImHexPath::Layouts)) {
for (const auto &entry : std::fs::directory_iterator(directory)) {
const auto &path = entry.path();
if (path.extension() != ".hexlyt")
continue;
auto name = path.stem().string();
name = wolv::util::replaceStrings(name, "_", " ");
for (size_t i = 0; i < name.size(); i++) {
if (i == 0 || name[i - 1] == '_')
name[i] = char(std::toupper(name[i]));
}
s_layouts.push_back({
name,
path
});
}
}
}
void LayoutManager::reset() {
s_layoutPathToLoad.reset();
s_layoutStringToLoad.reset();
s_layouts.clear();
}
}

View File

@@ -76,7 +76,7 @@ namespace hex {
void LangEntry::loadLanguage(const std::string &language) {
LangEntry::s_currStrings.clear();
auto &definitions = ContentRegistry::Language::getLanguageDefinitions();
auto &definitions = ContentRegistry::Language::impl::getLanguageDefinitions();
if (!definitions.contains(language))
return;
@@ -92,7 +92,7 @@ namespace hex {
}
const std::map<std::string, std::string> &LangEntry::getSupportedLanguages() {
return ContentRegistry::Language::getLanguages();
return ContentRegistry::Language::impl::getLanguages();
}
void LangEntry::setFallbackLanguage(const std::string &language) {

View File

@@ -3,6 +3,8 @@
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
#include <wolv/utils/string.hpp>
#include <filesystem>
#include <system_error>
@@ -17,7 +19,7 @@ namespace hex {
return;
}
#else
this->m_handle = dlopen(hex::toUTF8String(path).c_str(), RTLD_LAZY);
this->m_handle = dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY);
if (this->m_handle == nullptr) {
log::error("dlopen failed: {}!", dlerror());
@@ -69,7 +71,7 @@ namespace hex {
bool Plugin::initializePlugin() const {
const auto requestedVersion = getCompatibleVersion();
if (requestedVersion != IMHEX_VERSION) {
log::error("Refused to load plugin '{}' which was built for a different version of ImHex: '{}'", hex::toUTF8String(this->m_path.filename()), requestedVersion);
log::error("Refused to load plugin '{}' which was built for a different version of ImHex: '{}'", wolv::util::toUTF8String(this->m_path.filename()), requestedVersion);
return false;
}
@@ -145,7 +147,7 @@ namespace hex {
std::vector<Plugin> PluginManager::s_plugins;
bool PluginManager::load(const std::fs::path &pluginFolder) {
if (!fs::exists(pluginFolder))
if (!wolv::io::fs::exists(pluginFolder))
return false;
PluginManager::s_pluginFolder = pluginFolder;

View File

@@ -6,6 +6,9 @@
#include <hex/providers/provider.hpp>
#include <wolv/utils/guards.hpp>
#include <wolv/io/fs.hpp>
namespace hex {
constexpr static auto MetadataHeaderMagic = "HEX";
@@ -24,7 +27,7 @@ namespace hex {
ProjectFile::s_currProjectPath = originalPath;
};
if (!fs::exists(filePath) || !fs::isRegularFile(filePath))
if (!wolv::io::fs::exists(filePath) || !wolv::io::fs::isRegularFile(filePath))
return false;
if (filePath.extension() != ".hexproj")
return false;
@@ -37,7 +40,7 @@ namespace hex {
return false;
{
const auto metadataContent = tar.read(MetadataPath);
const auto metadataContent = tar.readVector(MetadataPath);
if (!std::string(metadataContent.begin(), metadataContent.end()).starts_with(MetadataHeaderMagic))
return false;
@@ -106,29 +109,33 @@ namespace hex {
bool result = true;
for (const auto &handler : ProjectFile::getHandlers()) {
try {
if (!handler.store(handler.basePath, tar))
if (!handler.store(handler.basePath, tar) && handler.required)
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
if (handler.required)
result = false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
const auto basePath = std::fs::path(std::to_string(provider->getID()));
for (const auto &handler: ProjectFile::getProviderHandlers()) {
try {
if (!handler.store(provider, basePath / handler.basePath, tar))
if (!handler.store(provider, basePath / handler.basePath, tar) && handler.required)
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
if (handler.required)
result = false;
}
}
}
{
const auto metadataContent = hex::format("{}\n{}", MetadataHeaderMagic, IMHEX_VERSION);
tar.write(MetadataPath, metadataContent);
tar.writeString(MetadataPath, metadataContent);
}
ImHexApi::Provider::resetDirty();

View File

@@ -47,7 +47,7 @@ namespace hex {
void Task::update(u64 value) {
this->m_currValue = value;
if (this->m_shouldInterrupt)
if (this->m_shouldInterrupt) [[unlikely]]
throw TaskInterruptor();
}
@@ -165,6 +165,14 @@ namespace hex {
task->interrupt();
}
u32 TaskHolder::getProgress() const {
if (this->m_task.expired())
return 0;
auto task = this->m_task.lock();
return (u32)((task->getValue() * 100) / task->getMaxValue());
}
void TaskManager::init() {
for (u32 i = 0; i < std::thread::hardware_concurrency(); i++)
@@ -184,7 +192,6 @@ namespace hex {
}
void TaskManager::runner(const std::stop_token &stopToken) {
std::mutex mutex;
while (true) {
std::shared_ptr<Task> task;
{
@@ -239,12 +246,13 @@ namespace hex {
}
void TaskManager::collectGarbage() {
std::unique_lock lock1(s_queueMutex);
std::unique_lock lock2(s_deferredCallsMutex);
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
{
std::unique_lock lock1(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);
for (auto &call : s_tasksFinishedCallbacks)
call();
s_tasksFinishedCallbacks.clear();
@@ -293,4 +301,4 @@ namespace hex {
s_tasksFinishedCallbacks.push_back(function);
}
}
}

View File

@@ -1,22 +1,25 @@
#include <hex/api/theme_manager.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
#include <nlohmann/json.hpp>
namespace hex::api {
namespace hex {
std::map<std::string, nlohmann::json> ThemeManager::s_themes;
std::map<std::string, std::function<void(std::string, std::string)>> ThemeManager::s_themeHandlers, ThemeManager::s_styleHandlers;
std::map<std::string, ThemeManager::ThemeHandler> ThemeManager::s_themeHandlers;
std::map<std::string, ThemeManager::StyleHandler> ThemeManager::s_styleHandlers;
std::string ThemeManager::s_imagePostfix;
std::string ThemeManager::s_currTheme;
void ThemeManager::addThemeHandler(const std::string &name, const std::function<void(std::string, std::string)> &handler) {
s_themeHandlers[name] = handler;
void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) {
s_themeHandlers[name] = { colorMap, getFunction, setFunction };
}
void ThemeManager::addStyleHandler(const std::string &name, const std::function<void(std::string, std::string)> &handler) {
s_styleHandlers[name] = handler;
void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) {
s_styleHandlers[name] = { styleMap };
}
void ThemeManager::addTheme(const std::string &content) {
@@ -29,6 +32,9 @@ namespace hex::api {
}
std::optional<ImColor> ThemeManager::parseColorString(const std::string &colorString) {
if (colorString == "auto")
return ImVec4(0, 0, 0, -1);
if (colorString.length() != 9 || colorString[0] != '#')
return std::nullopt;
@@ -46,9 +52,48 @@ namespace hex::api {
return std::nullopt;
}
if (color == 0x00000000)
return ImVec4(0, 0, 0, -1);
return ImColor(hex::changeEndianess(color, std::endian::big));
}
nlohmann::json ThemeManager::exportCurrentTheme(const std::string &name) {
nlohmann::json theme = {
{ "name", name },
{ "image_postfix", s_imagePostfix },
{ "colors", {} },
{ "styles", {} },
{ "base", s_currTheme }
};
for (const auto &[type, handler] : s_themeHandlers) {
theme["colors"][type] = {};
for (const auto &[key, value] : handler.colorMap) {
auto color = handler.getFunction(value);
theme["colors"][type][key] = fmt::format("#{:08X}", hex::changeEndianess(u32(color), std::endian::big));
}
}
for (const auto &[type, handler] : s_styleHandlers) {
theme["styles"][type] = {};
for (const auto &[key, style] : handler.styleMap) {
if (std::holds_alternative<float*>(style.value))
theme["styles"][type][key] = *std::get<float*>(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
};
}
}
}
return theme;
}
void ThemeManager::changeTheme(std::string name) {
if (!s_themes.contains(name)) {
if (s_themes.empty()) {
@@ -64,7 +109,8 @@ namespace hex::api {
if (theme.contains("base")) {
if (theme["base"].is_string()) {
changeTheme(theme["base"].get<std::string>());
if (theme["base"] != name)
changeTheme(theme["base"].get<std::string>());
} else {
hex::log::error("Theme '{}' has invalid base theme!", name);
}
@@ -77,19 +123,53 @@ namespace hex::api {
continue;
}
for (const auto &[key, value] : content.items())
s_themeHandlers[type](key, value.get<std::string>());
const auto &handler = s_themeHandlers[type];
for (const auto &[key, value] : content.items()) {
if (!handler.colorMap.contains(key)) {
log::warn("No color found for '{}.{}'", type, key);
continue;
}
auto color = parseColorString(value.get<std::string>());
if (!color.has_value()) {
log::warn("Invalid color '{}' for '{}.{}'", value.get<std::string>(), type, key);
continue;
}
s_themeHandlers[type].setFunction(s_themeHandlers[type].colorMap.at(key), color.value());
}
}
}
if (theme.contains("styles")) {
for (const auto&[key, value] : theme["styles"].items()) {
if (!s_styleHandlers.contains(key)) {
log::warn("No style handler found for '{}'", key);
for (const auto&[type, content] : theme["styles"].items()) {
if (!s_styleHandlers.contains(type)) {
log::warn("No style handler found for '{}'", type);
continue;
}
s_styleHandlers[key](name, value.get<std::string>());
auto &handler = s_styleHandlers[type];
for (const auto &[key, value] : content.items()) {
if (!handler.styleMap.contains(key))
continue;
auto &style = handler.styleMap.at(key);
const float scale = style.needsScaling ? 1_scaled : 1.0F;
if (value.is_number_float()) {
if (auto newValue = std::get_if<float*>(&style.value); newValue != nullptr)
**newValue = value.get<float>() * scale;
else
log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name);
} else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) {
if (auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr)
**newValue = ImVec2(value[0].get<float>() * scale, value[1].get<float>() * scale);
else
log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name);
} else {
hex::log::error("Theme '{}' has invalid style value for '{}.{}'!", name, type, key);
}
}
}
}
@@ -100,6 +180,8 @@ namespace hex::api {
hex::log::error("Theme '{}' has invalid image postfix!", name);
}
}
s_currTheme = name;
}
const std::string &ThemeManager::getThemeImagePostfix() {
@@ -119,6 +201,7 @@ namespace hex::api {
ThemeManager::s_styleHandlers.clear();
ThemeManager::s_themeHandlers.clear();
ThemeManager::s_imagePostfix.clear();
ThemeManager::s_currTheme.clear();
}
}

View File

@@ -86,7 +86,7 @@ namespace hex::dp {
return *reinterpret_cast<long double *>(outputData.data());
}
void Node::setBufferOnOutput(u32 index, const std::vector<u8> &data) {
void Node::setBufferOnOutput(u32 index, std::span<const u8> data) {
if (index >= this->getAttributes().size())
throwNodeError("Attribute index out of bounds!");
@@ -95,7 +95,7 @@ namespace hex::dp {
if (attribute.getIOType() != Attribute::IOType::Out)
throwNodeError("Tried to set output data of an input attribute!");
attribute.getOutputData() = data;
attribute.getOutputData() = { data.begin(), data.end() };
}
void Node::setIntegerOnOutput(u32 index, i128 integer) {

View File

@@ -1,8 +1,8 @@
#include <hex/helpers/crypto.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/concepts.hpp>
#include <wolv/utils/guards.hpp>
#include <mbedtls/version.h>
#include <mbedtls/base64.h>
@@ -11,13 +11,10 @@
#include <mbedtls/sha1.h>
#include <mbedtls/sha256.h>
#include <mbedtls/sha512.h>
#include <mbedtls/aes.h>
#include <mbedtls/cipher.h>
#include <array>
#include <span>
#include <functional>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <bit>

View File

@@ -2,13 +2,28 @@
#include <hex/helpers/utils.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/string.hpp>
namespace hex {
EncodingFile::EncodingFile(Type type, const std::fs::path &path) {
auto file = fs::File(path, fs::File::Mode::Read);
auto file = wolv::io::File(path, wolv::io::File::Mode::Read);
switch (type) {
case Type::Thingy:
parseThingyFile(file);
parse(file.readString());
break;
default:
return;
}
this->m_valid = true;
}
EncodingFile::EncodingFile(Type type, const std::string &content) {
switch (type) {
case Type::Thingy:
parse(content);
break;
default:
return;
@@ -23,7 +38,7 @@ namespace hex {
if (size > buffer.size()) continue;
auto key = std::vector<u8>(buffer.begin(), buffer.begin() + size);
std::vector<u8> key(buffer.begin(), buffer.begin() + size);
if (mapping.contains(key))
return { mapping.at(key), size };
}
@@ -31,8 +46,23 @@ namespace hex {
return { ".", 1 };
}
void EncodingFile::parseThingyFile(fs::File &file) {
for (const auto &line : splitString(file.readString(), "\n")) {
size_t EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
for (auto riter = this->m_mapping.crbegin(); riter != this->m_mapping.crend(); ++riter) {
const auto &[size, mapping] = *riter;
if (size > buffer.size()) continue;
std::vector<u8> key(buffer.begin(), buffer.begin() + size);
if (mapping.contains(key))
return size;
}
return 1;
}
void EncodingFile::parse(const std::string &content) {
this->m_tableContent = content;
for (const auto &line : splitString(this->m_tableContent, "\n")) {
std::string from, to;
{
@@ -51,15 +81,17 @@ namespace hex {
if (fromBytes.empty()) continue;
if (to.length() > 1)
hex::trim(to);
to = wolv::util::trim(to);
if (to.empty())
to = " ";
if (!this->m_mapping.contains(fromBytes.size()))
this->m_mapping.insert({ fromBytes.size(), {} });
this->m_mapping[fromBytes.size()].insert({ fromBytes, to });
this->m_longestSequence = std::max(this->m_longestSequence, fromBytes.size());
auto keySize = fromBytes.size();
this->m_mapping[keySize].insert({ std::move(fromBytes), to });
this->m_longestSequence = std::max(this->m_longestSequence, keySize);
}
}

View File

@@ -1,171 +0,0 @@
#include <hex/helpers/file.hpp>
#include <hex/helpers/utils.hpp>
#include <unistd.h>
namespace hex::fs {
File::File(const std::fs::path &path, Mode mode) noexcept : m_path(path) {
#if defined(OS_WINDOWS)
if (mode == File::Mode::Read)
this->m_file = _wfopen(path.c_str(), L"rb");
else if (mode == File::Mode::Write)
this->m_file = _wfopen(path.c_str(), L"r+b");
if (mode == File::Mode::Create || (mode == File::Mode::Write && this->m_file == nullptr))
this->m_file = _wfopen(path.c_str(), L"w+b");
#else
if (mode == File::Mode::Read)
this->m_file = fopen64(hex::toUTF8String(path).c_str(), "rb");
else if (mode == File::Mode::Write)
this->m_file = fopen64(hex::toUTF8String(path).c_str(), "r+b");
if (mode == File::Mode::Create || (mode == File::Mode::Write && this->m_file == nullptr))
this->m_file = fopen64(hex::toUTF8String(path).c_str(), "w+b");
#endif
}
File::File() noexcept {
this->m_file = nullptr;
}
File::File(File &&other) noexcept {
this->m_file = other.m_file;
other.m_file = nullptr;
}
File::~File() {
this->close();
}
File &File::operator=(File &&other) noexcept {
this->m_file = other.m_file;
other.m_file = nullptr;
this->m_path = std::move(other.m_path);
return *this;
}
void File::seek(u64 offset) {
fseeko64(this->m_file, offset, SEEK_SET);
}
void File::close() {
if (isValid()) {
std::fclose(this->m_file);
this->m_file = nullptr;
}
}
size_t File::readBuffer(u8 *buffer, size_t size) {
if (!isValid()) return 0;
return fread(buffer, size, 1, this->m_file);
}
std::vector<u8> File::readBytes(size_t numBytes) {
if (!isValid()) return {};
auto size = numBytes == 0 ? getSize() : numBytes;
if (size == 0) return {};
std::vector<u8> bytes(size);
auto bytesRead = fread(bytes.data(), 1, bytes.size(), this->m_file);
bytes.resize(bytesRead);
return bytes;
}
std::string File::readString(size_t numBytes) {
if (!isValid()) return {};
if (getSize() == 0) return {};
auto bytes = readBytes(numBytes);
if (bytes.empty())
return "";
auto cString = reinterpret_cast<const char *>(bytes.data());
return { cString, hex::strnlen(cString, bytes.size()) };
}
std::u8string File::readU8String(size_t numBytes) {
if (!isValid()) return {};
if (getSize() == 0) return {};
auto bytes = readBytes(numBytes);
if (bytes.empty())
return u8"";
auto cString = reinterpret_cast<const char8_t *>(bytes.data());
return { cString, hex::strnlen(reinterpret_cast<const char*>(bytes.data()), bytes.size()) };
}
void File::write(const u8 *buffer, size_t size) {
if (!isValid()) return;
std::fwrite(buffer, size, 1, this->m_file);
}
void File::write(const std::vector<u8> &bytes) {
if (!isValid()) return;
std::fwrite(bytes.data(), 1, bytes.size(), this->m_file);
}
void File::write(const std::string &string) {
if (!isValid()) return;
std::fwrite(string.data(), string.size(), 1, this->m_file);
}
void File::write(const std::u8string &string) {
if (!isValid()) return;
std::fwrite(string.data(), string.size(), 1, this->m_file);
}
size_t File::getSize() const {
if (!isValid()) return 0;
auto startPos = ftello64(this->m_file);
fseeko64(this->m_file, 0, SEEK_END);
auto size = ftello64(this->m_file);
fseeko64(this->m_file, startPos, SEEK_SET);
if (size < 0)
return 0;
return size;
}
void File::setSize(u64 size) {
if (!isValid()) return;
auto result = ftruncate64(fileno(this->m_file), size);
hex::unused(result);
}
void File::flush() {
std::fflush(this->m_file);
}
bool File::remove() {
this->close();
return std::remove(hex::toUTF8String(this->m_path).c_str()) == 0;
}
void File::disableBuffering() {
if (!isValid()) return;
std::setvbuf(this->m_file, nullptr, _IONBF, 0);
}
}

View File

@@ -2,11 +2,7 @@
#include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/fs_macos.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/intrinsics.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/logger.hpp>
#include <xdg.hpp>
@@ -21,68 +17,25 @@
#include <algorithm>
#include <filesystem>
#include <wolv/io/file.hpp>
#include <wolv/io/fs.hpp>
namespace hex::fs {
std::optional<std::fs::path> getExecutablePath() {
#if defined(OS_WINDOWS)
std::wstring exePath(MAX_PATH, '\0');
if (GetModuleFileNameW(nullptr, exePath.data(), exePath.length()) == 0)
return std::nullopt;
hex::trim(exePath);
return exePath;
#elif defined(OS_LINUX)
std::string exePath(PATH_MAX, '\0');
if (readlink("/proc/self/exe", exePath.data(), PATH_MAX) < 0)
return std::nullopt;
hex::trim(exePath);
return exePath;
#elif defined(OS_MACOS)
std::string exePath;
{
auto string = getMacExecutableDirectoryPath();
exePath = string;
macFree(string);
}
hex::trim(exePath);
return exePath;
#else
return std::nullopt;
#endif
}
bool isPathWritable(const std::fs::path &path) {
constexpr static auto TestFileName = "__imhex__tmp__";
{
File file(path / TestFileName, File::Mode::Read);
if (file.isValid()) {
if (!file.remove())
return false;
}
}
File file(path / TestFileName, File::Mode::Create);
bool result = file.isValid();
if (!file.remove())
return false;
return result;
}
static std::function<void()> s_fileBrowserErrorCallback;
void setFileBrowserErrorCallback(const std::function<void()> &callback) {
static std::function<void(const std::string&)> s_fileBrowserErrorCallback;
void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) {
s_fileBrowserErrorCallback = callback;
}
bool openFileBrowser(DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) {
NFD::Init();
NFD::ClearError();
if (NFD::Init() != NFD_OKAY) {
log::error("NFD init returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr)
s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
return false;
}
NFD::UniquePathU8 outPath;
NFD::UniquePathSet outPaths;
@@ -119,8 +72,9 @@ namespace hex::fs {
}
}
} else if (result == NFD_ERROR) {
log::error("Requested file dialog returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr)
s_fileBrowserErrorCallback();
s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
}
NFD::Quit();
@@ -128,7 +82,7 @@ namespace hex::fs {
return result == NFD_OKAY;
}
static std::vector<std::fs::path> getDataPaths() {
std::vector<std::fs::path> getDataPaths() {
std::vector<std::fs::path> paths;
#if defined(OS_WINDOWS)
@@ -143,14 +97,7 @@ namespace hex::fs {
#elif defined(OS_MACOS)
std::fs::path applicationSupportDirPath;
{
auto string = getMacApplicationSupportDirectoryPath();
applicationSupportDirPath = std::string(string);
macFree(string);
}
paths.push_back(applicationSupportDirPath);
paths.push_back(wolv::io::fs::getApplicationSupportDirectoryPath());
#elif defined(OS_LINUX)
@@ -166,12 +113,12 @@ namespace hex::fs {
#if defined(OS_MACOS)
if (auto executablePath = fs::getExecutablePath(); executablePath.has_value())
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())
paths.push_back(*executablePath);
#else
if (auto executablePath = fs::getExecutablePath(); executablePath.has_value())
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())
paths.push_back(executablePath->parent_path());
#endif
@@ -193,17 +140,7 @@ namespace hex::fs {
#elif defined(OS_MACOS)
return getDataPaths();
#elif defined(OS_LINUX)
std::vector<std::fs::path> paths;
paths.push_back(xdg::DataHomeDir());
auto dataDirs = xdg::DataDirs();
std::copy(dataDirs.begin(), dataDirs.end(), std::back_inserter(paths));
for (auto &path : paths)
path = path / "imhex";
return paths;
return {xdg::ConfigHomeDir() / "imhex"};
#endif
}
@@ -239,7 +176,7 @@ namespace hex::fs {
result = appendPath(getDataPaths(), "encodings");
break;
case ImHexPath::Logs:
result = appendPath(getConfigPaths(), "logs");
result = appendPath(getDataPaths(), "logs");
break;
case ImHexPath::Plugins:
result = appendPath(getPluginPaths(), "plugins");
@@ -277,17 +214,38 @@ namespace hex::fs {
case ImHexPath::Themes:
result = appendPath(getDataPaths(), "themes");
break;
case ImHexPath::Layouts:
result = appendPath(getDataPaths(), "layouts");
break;
}
if (!listNonExisting) {
result.erase(std::remove_if(result.begin(), result.end(), [](const auto &path) {
return !fs::isDirectory(path);
return !wolv::io::fs::isDirectory(path);
}), result.end());
}
return result;
}
bool isPathWritable(const std::fs::path &path) {
constexpr static auto TestFileName = "__imhex__tmp__";
{
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Read);
if (file.isValid()) {
if (!file.remove())
return false;
}
}
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create);
bool result = file.isValid();
if (!file.remove())
return false;
return result;
}
std::fs::path toShortPath(const std::fs::path &path) {
#if defined(OS_WINDOWS)
size_t size = GetShortPathNameW(path.c_str(), nullptr, 0);

View File

@@ -1,44 +0,0 @@
#if defined(OS_MACOS)
#include <string.h>
#include <stdlib.h>
#include <Foundation/Foundation.h>
char* getMacExecutableDirectoryPath(void) {
@autoreleasepool {
const char *pathString = [[[[[NSBundle mainBundle] executableURL] URLByDeletingLastPathComponent] path] UTF8String];
char *result = malloc(strlen(pathString) + 1);
strcpy(result, pathString);
return result;
}
}
char* getMacApplicationSupportDirectoryPath(void) {
@autoreleasepool {
NSError* error = nil;
NSURL* dirUrl = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&error];
if (error != nil) {
return NULL;
}
const char *pathString = [[[dirUrl URLByAppendingPathComponent:(@"imhex")] path] UTF8String];
char *result = malloc(strlen(pathString) + 1);
strcpy(result, pathString);
return result;
}
}
void macFree(void *ptr) {
free(ptr);
}
#endif

View File

@@ -0,0 +1,97 @@
#include <hex/helpers/http_requests.hpp>
namespace hex {
std::string HttpRequest::s_caCertData;
std::string HttpRequest::s_proxyUrl;
HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) {
AT_FIRST_TIME {
curl_global_init(CURL_GLOBAL_ALL);
};
AT_FINAL_CLEANUP {
curl_global_cleanup();
};
this->m_curl = curl_easy_init();
}
HttpRequest::~HttpRequest() {
curl_easy_cleanup(this->m_curl);
}
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(this->m_curl, CURLOPT_PROXY, s_proxyUrl.c_str());
#if defined(IMHEX_USE_BUNDLED_CA)
curl_easy_setopt(this->m_curl, CURLOPT_CAINFO, nullptr);
curl_easy_setopt(this->m_curl, CURLOPT_CAPATH, nullptr);
curl_easy_setopt(this->m_curl, CURLOPT_SSLCERTTYPE, "PEM");
curl_easy_setopt(this->m_curl, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunction);
this->m_caCert = std::make_unique<mbedtls_x509_crt>();
curl_easy_setopt(this->m_curl, CURLOPT_SSL_CTX_DATA, this->m_caCert.get());
#endif
}
CURLcode HttpRequest::sslCtxFunction(CURL *ctx, void *sslctx, void *userData) {
hex::unused(ctx, userData);
auto *cfg = static_cast<mbedtls_ssl_config *>(sslctx);
auto crt = static_cast<mbedtls_x509_crt*>(userData);
mbedtls_x509_crt_init(crt);
mbedtls_x509_crt_parse(crt, reinterpret_cast<const u8 *>(HttpRequest::s_caCertData.data()), HttpRequest::s_caCertData.size());
mbedtls_ssl_conf_ca_chain(cfg, crt, nullptr);
return CURLE_OK;
}
size_t HttpRequest::writeToVector(void *contents, size_t size, size_t nmemb, void *userdata) {
auto &response = *reinterpret_cast<std::vector<u8>*>(userdata);
auto startSize = response.size();
response.resize(startSize + size * nmemb);
std::memcpy(response.data() + startSize, contents, size * nmemb);
return size * nmemb;
}
size_t HttpRequest::writeToFile(void *contents, size_t size, size_t nmemb, void *userdata) {
auto &file = *reinterpret_cast<wolv::io::File*>(userdata);
file.writeBuffer(reinterpret_cast<const u8*>(contents), size * nmemb);
return size * nmemb;
}
int HttpRequest::progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) {
auto &request = *static_cast<HttpRequest *>(contents);
if (dlTotal > 0)
request.m_progress = float(dlNow) / dlTotal;
else if (ulTotal > 0)
request.m_progress = float(ulNow) / ulTotal;
else
request.m_progress = 0.0F;
return request.m_canceled ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK;
}
}

View File

@@ -1,13 +1,12 @@
#include <hex/helpers/logger.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/fmt.hpp>
#include <iostream>
#include <wolv/io/file.hpp>
namespace hex::log {
static fs::File g_loggerFile;
static wolv::io::File g_loggerFile;
FILE *getDestination() {
if (g_loggerFile.isValid())
@@ -24,8 +23,8 @@ namespace hex::log {
if (g_loggerFile.isValid()) return;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Logs, true)) {
fs::createDirectories(path);
g_loggerFile = fs::File(path / hex::format("{0:%Y%m%d_%H%M%S}.log", fmt::localtime(std::chrono::system_clock::now())), fs::File::Mode::Create);
wolv::io::fs::createDirectories(path);
g_loggerFile = wolv::io::File(path / hex::format("{0:%Y%m%d_%H%M%S}.log", fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()))), wolv::io::File::Mode::Create);
g_loggerFile.disableBuffering();
if (g_loggerFile.isValid()) break;

View File

@@ -3,6 +3,9 @@
#include <hex/helpers/utils.hpp>
#include <hex/helpers/fs.hpp>
#include <wolv/utils/guards.hpp>
#include <wolv/utils/string.hpp>
#include <hex/providers/provider.hpp>
#include <filesystem>
@@ -25,9 +28,11 @@ namespace hex::magic {
std::error_code error;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Magic)) {
for (const auto &entry : std::fs::directory_iterator(dir, error)) {
if (entry.is_regular_file() && ((sourceFiles && entry.path().extension().empty()) || (!sourceFiles && entry.path().extension() == ".mgc"))) {
magicFiles += hex::toUTF8String(fs::toShortPath(entry.path())) + MAGIC_PATH_SEPARATOR;
for (const auto &entry : std::fs::recursive_directory_iterator(dir, error)) {
auto path = std::fs::absolute(entry.path());
if (entry.is_regular_file() && ((sourceFiles && path.extension().empty()) || (!sourceFiles && path.extension() == ".mgc"))) {
magicFiles += wolv::util::toUTF8String(wolv::io::fs::toShortPath(path)) + MAGIC_PATH_SEPARATOR;
}
}
}

View File

@@ -1,304 +0,0 @@
#include <hex/helpers/net.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/api/content_registry.hpp>
#include <filesystem>
#include <cstdio>
#include <nlohmann/json.hpp>
namespace hex {
Net::Net() {
FIRST_TIME {
curl_global_sslset(CURLSSLBACKEND_MBEDTLS, nullptr, nullptr);
curl_global_init(CURL_GLOBAL_ALL);
};
FINAL_CLEANUP {
curl_global_cleanup();
};
this->m_ctx = curl_easy_init();
}
Net::~Net() {
curl_easy_cleanup(this->m_ctx);
}
static size_t writeToString(void *contents, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append((char *)contents, size * nmemb);
return size * nmemb;
}
static size_t writeToFile(void *contents, size_t size, size_t nmemb, void *userdata) {
FILE *file = static_cast<FILE *>(userdata);
return fwrite(contents, size, nmemb, file);
}
[[maybe_unused]]
CURLcode sslCtxFunction(CURL *ctx, void *sslctx, void *userData) {
hex::unused(ctx, userData);
auto *cfg = static_cast<mbedtls_ssl_config *>(sslctx);
auto crt = static_cast<mbedtls_x509_crt*>(userData);
mbedtls_x509_crt_init(crt);
mbedtls_x509_crt_parse(crt, reinterpret_cast<const u8 *>(Net::s_caCert.data()), Net::s_caCert.size());
mbedtls_ssl_conf_ca_chain(cfg, crt, nullptr);
return CURLE_OK;
}
int progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) {
auto &net = *static_cast<Net *>(contents);
if (dlTotal > 0)
net.m_progress = float(dlNow) / dlTotal;
else if (ulTotal > 0)
net.m_progress = float(ulNow) / ulTotal;
else
net.m_progress = 0.0F;
return net.m_shouldCancel ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK;
}
void Net::setCommonSettings(std::string &response, const std::string &url, u32 timeout, const std::map<std::string, std::string> &extraHeaders, const std::string &body) {
this->m_headers = curl_slist_append(this->m_headers, "Cache-Control: no-cache");
if (!extraHeaders.empty())
for (const auto &[key, value] : extraHeaders) {
std::string entry = key;
entry += ": ";
entry += value;
this->m_headers = curl_slist_append(this->m_headers, entry.c_str());
}
if (!body.empty())
curl_easy_setopt(this->m_ctx, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(this->m_ctx, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(this->m_ctx, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
curl_easy_setopt(this->m_ctx, CURLOPT_URL, url.c_str());
curl_easy_setopt(this->m_ctx, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(this->m_ctx, CURLOPT_HTTPHEADER, this->m_headers);
curl_easy_setopt(this->m_ctx, CURLOPT_USERAGENT, "ImHex/1.0");
curl_easy_setopt(this->m_ctx, CURLOPT_DEFAULT_PROTOCOL, "https");
curl_easy_setopt(this->m_ctx, CURLOPT_WRITEFUNCTION, writeToString);
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(this->m_ctx, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(this->m_ctx, CURLOPT_TIMEOUT_MS, 0L);
curl_easy_setopt(this->m_ctx, CURLOPT_CONNECTTIMEOUT_MS, timeout);
curl_easy_setopt(this->m_ctx, CURLOPT_XFERINFODATA, this);
curl_easy_setopt(this->m_ctx, CURLOPT_XFERINFOFUNCTION, progressCallback);
curl_easy_setopt(this->m_ctx, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(this->m_ctx, CURLOPT_NOPROGRESS, 0L);
#if defined(IMHEX_USE_BUNDLED_CA)
curl_easy_setopt(this->m_ctx, CURLOPT_CAINFO, nullptr);
curl_easy_setopt(this->m_ctx, CURLOPT_CAPATH, nullptr);
curl_easy_setopt(this->m_ctx, CURLOPT_SSLCERTTYPE, "PEM");
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunction);
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_CTX_DATA, &this->m_caCert);
#endif
curl_easy_setopt(this->m_ctx, CURLOPT_PROXY, Net::s_proxyUrl.c_str());
}
std::optional<i32> Net::execute() {
CURLcode result = curl_easy_perform(this->m_ctx);
if (result != CURLE_OK){
char *url = nullptr;
curl_easy_getinfo(this->m_ctx, CURLINFO_EFFECTIVE_URL, &url);
log::error("Net request '{0}' failed with error {1}: '{2}'", url, u32(result), curl_easy_strerror(result));
if(!Net::s_proxyUrl.empty()){
log::info("A custom proxy '{}' is in use. Is it working correctly?", Net::s_proxyUrl);
}
}
long responseCode = 0;
curl_easy_getinfo(this->m_ctx, CURLINFO_RESPONSE_CODE, &responseCode);
curl_slist_free_all(this->m_headers);
this->m_headers = nullptr;
this->m_progress = 0.0F;
this->m_shouldCancel = false;
if (result != CURLE_OK)
return std::nullopt;
else
return i32(responseCode);
}
std::future<Response<std::string>> Net::getString(const std::string &url, u32 timeout, const std::map<std::string, std::string> &extraHeaders, const std::string &body) {
this->m_transmissionActive.lock();
return std::async(std::launch::async, [=, this] {
std::string response;
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET");
setCommonSettings(response, url, timeout, extraHeaders, body);
auto responseCode = execute();
return Response<std::string> { responseCode.value_or(0), response };
});
}
std::future<Response<nlohmann::json>> Net::getJson(const std::string &url, u32 timeout, const std::map<std::string, std::string> &extraHeaders, const std::string &body) {
this->m_transmissionActive.lock();
return std::async(std::launch::async, [=, this] {
std::string response;
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET");
setCommonSettings(response, url, timeout, extraHeaders, body);
auto responseCode = execute();
if (!responseCode.has_value())
return Response<nlohmann::json> { 0, { } };
else
return Response<nlohmann::json> { responseCode.value_or(0), nlohmann::json::parse(response, nullptr, false, true) };
});
}
std::future<Response<nlohmann::json>> Net::postJson(const std::string &url, u32 timeout, const std::map<std::string, std::string> &extraHeaders, const std::string &body) {
this->m_transmissionActive.lock();
return std::async(std::launch::async, [=, this] {
std::string response;
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "POST");
setCommonSettings(response, url, timeout, extraHeaders, body);
auto responseCode = execute();
if (!responseCode.has_value())
return Response<nlohmann::json> { 0, { } };
else
return Response<nlohmann::json> { responseCode.value_or(0), nlohmann::json::parse(response, nullptr, false, true) };
});
}
std::future<Response<std::string>> Net::uploadFile(const std::string &url, const std::fs::path &filePath, u32 timeout) {
this->m_transmissionActive.lock();
return std::async(std::launch::async, [=, this] {
std::string response;
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
fs::File file(filePath, fs::File::Mode::Read);
if (!file.isValid())
return Response<std::string> { 400, {} };
curl_mime *mime = curl_mime_init(this->m_ctx);
curl_mimepart *part = curl_mime_addpart(mime);
auto fileName = hex::toUTF8String(filePath.filename());
curl_mime_data_cb(part, file.getSize(),
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
auto file = static_cast<FILE*>(arg);
return fread(buffer, size, nitems, file);
},
[](void *arg, curl_off_t offset, int origin) -> int {
auto file = static_cast<FILE*>(arg);
if (fseek(file, offset, origin) != 0)
return CURL_SEEKFUNC_CANTSEEK;
else
return CURL_SEEKFUNC_OK;
},
[](void *arg) {
auto file = static_cast<FILE*>(arg);
fclose(file);
},
file.getHandle());
curl_mime_filename(part, fileName.c_str());
curl_mime_name(part, "file");
setCommonSettings(response, url, timeout);
curl_easy_setopt(this->m_ctx, CURLOPT_MIMEPOST, mime);
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "POST");
auto responseCode = execute();
return Response<std::string> { responseCode.value_or(0), response };
});
}
std::future<Response<void>> Net::downloadFile(const std::string &url, const std::fs::path &filePath, u32 timeout) {
this->m_transmissionActive.lock();
return std::async(std::launch::async, [=, this] {
std::string response;
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
fs::File file(filePath, fs::File::Mode::Create);
if (!file.isValid())
return Response<void> { 400 };
setCommonSettings(response, url, timeout);
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(this->m_ctx, CURLOPT_WRITEFUNCTION, writeToFile);
curl_easy_setopt(this->m_ctx, CURLOPT_WRITEDATA, file.getHandle());
auto responseCode = execute();
return Response<void> { responseCode.value_or(0) };
});
}
std::string Net::encode(const std::string &input) {
auto escapedString = curl_easy_escape(this->m_ctx, input.c_str(), std::strlen(input.c_str()));
if (escapedString != nullptr) {
std::string output = escapedString;
curl_free(escapedString);
return output;
}
return {};
}
std::string Net::decode(const std::string &input) {
auto unescapedString = curl_easy_unescape(this->m_ctx, input.c_str(), std::strlen(input.c_str()), nullptr);
if (unescapedString != nullptr) {
std::string output = unescapedString;
curl_free(unescapedString);
return output;
}
return {};
}
std::string Net::s_proxyUrl;
void Net::setProxy(const std::string &url) {
Net::s_proxyUrl = url;
}
std::string Net::s_caCert;
void Net::setCACert(const std::string &content) {
Net::s_caCert = content;
}
}

View File

@@ -3,6 +3,8 @@
#include <hex/helpers/utils.hpp>
#include <hex/helpers/logger.hpp>
#include <wolv/utils/guards.hpp>
namespace hex::gl {
Shader::Shader(std::string_view vertexSource, std::string_view fragmentSource) {

View File

@@ -1,104 +0,0 @@
#include <hex/helpers/socket.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/logger.hpp>
namespace hex {
Socket::Socket(const std::string &address, u16 port) {
#if defined(OS_WINDOWS)
FIRST_TIME {
WSAData wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
};
FINAL_CLEANUP {
WSACleanup();
};
#endif
this->connect(address, port);
}
Socket::Socket(Socket &&other) noexcept {
this->m_socket = other.m_socket;
this->m_connected = other.m_connected;
other.m_socket = SOCKET_NONE;
}
Socket::~Socket() {
this->disconnect();
}
void Socket::writeBytes(const std::vector<u8> &bytes) const {
if (!this->isConnected()) return;
::send(this->m_socket, reinterpret_cast<const char *>(bytes.data()), bytes.size(), 0);
}
void Socket::writeString(const std::string &string) const {
if (!this->isConnected()) return;
::send(this->m_socket, string.c_str(), string.length(), 0);
}
std::vector<u8> Socket::readBytes(size_t size) const {
std::vector<u8> data;
data.resize(size);
auto receivedSize = ::recv(this->m_socket, reinterpret_cast<char *>(data.data()), size, 0);
if (receivedSize < 0)
return {};
data.resize(receivedSize);
return data;
}
std::string Socket::readString(size_t size) const {
auto bytes = readBytes(size);
std::string result;
std::copy(bytes.begin(), bytes.end(), std::back_inserter(result));
return result;
}
bool Socket::isConnected() const {
return this->m_connected;
}
void Socket::connect(const std::string &address, u16 port) {
this->m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (this->m_socket == SOCKET_NONE)
return;
sockaddr_in client = { };
client.sin_family = AF_INET;
client.sin_port = htons(port);
#if defined(OS_WINDOWS)
client.sin_addr.S_un.S_addr = ::inet_addr(address.c_str());
#else
client.sin_addr.s_addr = ::inet_addr(address.c_str());
#endif
this->m_connected = ::connect(this->m_socket, reinterpret_cast<sockaddr *>(&client), sizeof(client)) == 0;
}
void Socket::disconnect() {
if (this->m_socket != SOCKET_NONE) {
#if defined(OS_WINDOWS)
closesocket(this->m_socket);
#else
close(this->m_socket);
#endif
}
this->m_connected = false;
}
}

View File

@@ -94,75 +94,82 @@
}
#elif defined(HEX_HAS_EXECINFO) && __has_include(BACKTRACE_HEADER)
#elif defined(HEX_HAS_EXECINFO)
#include BACKTRACE_HEADER
#include <llvm/Demangle/Demangle.h>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
#if __has_include(BACKTRACE_HEADER)
namespace hex::stacktrace {
#include BACKTRACE_HEADER
#include <llvm/Demangle/Demangle.h>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
void initialize() {
namespace hex::stacktrace {
}
std::vector<StackFrame> getStackTrace() {
static std::vector<StackFrame> result;
std::array<void*, 128> addresses;
auto count = backtrace(addresses.data(), addresses.size());
auto functions = backtrace_symbols(addresses.data(), count);
for (i32 i = 0; i < count; i++)
result.push_back(StackFrame { "", functions[i], 0 });
return result;
}
}
#elif defined(HEX_HAS_BACKTRACE) && __has_include(BACKTRACE_HEADER)
#include BACKTRACE_HEADER
#include <llvm/Demangle/Demangle.h>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
namespace hex::stacktrace {
static struct backtrace_state *s_backtraceState;
void initialize() {
if (auto executablePath = fs::getExecutablePath(); executablePath.has_value()) {
static std::string path = executablePath->string();
s_backtraceState = backtrace_create_state(path.c_str(), 1, [](void *, const char *msg, int) { log::error("{}", msg); }, nullptr);
}
}
std::vector<StackFrame> getStackTrace() {
static std::vector<StackFrame> result;
result.clear();
if (s_backtraceState != nullptr) {
backtrace_full(s_backtraceState, 0, [](void *, uintptr_t, const char *fileName, int lineNumber, const char *function) -> int {
if (fileName == nullptr)
fileName = "??";
if (function == nullptr)
function = "??";
result.push_back(StackFrame { std::fs::path(fileName).filename().string(), llvm::demangle(function), u32(lineNumber) });
return 0;
}, nullptr, nullptr);
void initialize() {
}
return result;
std::vector<StackFrame> getStackTrace() {
static std::vector<StackFrame> result;
std::array<void*, 128> addresses;
auto count = backtrace(addresses.data(), addresses.size());
auto functions = backtrace_symbols(addresses.data(), count);
for (i32 i = 0; i < count; i++)
result.push_back(StackFrame { "", functions[i], 0 });
return result;
}
}
#endif
#elif defined(HEX_HAS_BACKTRACE)
#if __has_include(BACKTRACE_HEADER)
#include BACKTRACE_HEADER
#include <llvm/Demangle/Demangle.h>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
namespace hex::stacktrace {
static struct backtrace_state *s_backtraceState;
void initialize() {
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
static std::string path = executablePath->string();
s_backtraceState = backtrace_create_state(path.c_str(), 1, [](void *, const char *msg, int) { log::error("{}", msg); }, nullptr);
}
}
std::vector<StackFrame> getStackTrace() {
static std::vector<StackFrame> result;
result.clear();
if (s_backtraceState != nullptr) {
backtrace_full(s_backtraceState, 0, [](void *, uintptr_t, const char *fileName, int lineNumber, const char *function) -> int {
if (fileName == nullptr)
fileName = "??";
if (function == nullptr)
function = "??";
result.push_back(StackFrame { std::fs::path(fileName).filename().string(), llvm::demangle(function), u32(lineNumber) });
return 0;
}, nullptr, nullptr);
}
return result;
}
}
}
#endif
#else

View File

@@ -1,7 +1,9 @@
#include <hex/helpers/tar.hpp>
#include <hex/helpers/literals.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <wolv/io/file.hpp>
namespace hex {
@@ -12,11 +14,11 @@ namespace hex {
// Explicitly create file so a short path gets generated
if (mode == Mode::Create) {
fs::File file(path, fs::File::Mode::Create);
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.flush();
}
auto shortPath = hex::fs::toShortPath(path);
auto shortPath = wolv::io::fs::toShortPath(path);
if (mode == Tar::Mode::Read)
error = mtar_open(&this->m_ctx, shortPath.string().c_str(), "r");
else if (mode == Tar::Mode::Write)
@@ -26,6 +28,7 @@ namespace hex {
else
error = MTAR_EFAILURE;
this->m_path = path;
this->m_valid = (error == MTAR_ESUCCESS);
}
@@ -35,6 +38,7 @@ 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;
other.m_ctx = { };
@@ -45,6 +49,8 @@ namespace hex {
this->m_ctx = other.m_ctx;
other.m_ctx = { };
this->m_path = other.m_path;
this->m_valid = other.m_valid;
other.m_valid = false;
@@ -58,7 +64,7 @@ namespace hex {
mtar_header_t header;
while (mtar_read_header(&this->m_ctx, &header) != MTAR_ENULLRECORD) {
std::fs::path path = header.name;
if (header.name != PaxHeaderName && fs::isSubPath(basePath, path)) {
if (header.name != PaxHeaderName && wolv::io::fs::isSubPath(basePath, path)) {
result.emplace_back(header.name);
}
@@ -83,15 +89,20 @@ namespace hex {
this->m_valid = false;
}
std::vector<u8> Tar::read(const std::fs::path &path) {
std::vector<u8> Tar::readVector(const std::fs::path &path) {
mtar_header_t header;
auto fixedPath = path.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_find(&this->m_ctx, fixedPath.c_str(), &header);
int ret = mtar_find(&this->m_ctx, 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));
return {};
}
std::vector<u8> result(header.size, 0x00);
mtar_read_data(&this->m_ctx, result.data(), result.size());
@@ -99,11 +110,11 @@ namespace hex {
}
std::string Tar::readString(const std::fs::path &path) {
auto result = this->read(path);
auto result = this->readVector(path);
return { result.begin(), result.end() };
}
void Tar::write(const std::fs::path &path, const std::vector<u8> &data) {
void Tar::writeVector(const std::fs::path &path, const std::vector<u8> &data) {
if (path.has_parent_path()) {
std::fs::path pathPart;
for (const auto &part : path.parent_path()) {
@@ -125,21 +136,21 @@ namespace hex {
mtar_write_data(&this->m_ctx, data.data(), data.size());
}
void Tar::write(const std::fs::path &path, const std::string &data) {
this->write(path, std::vector<u8>(data.begin(), data.end()));
void Tar::writeString(const std::fs::path &path, const std::string &data) {
this->writeVector(path, { data.begin(), data.end() });
}
static void writeFile(mtar_t *ctx, mtar_header_t *header, const std::fs::path &path) {
constexpr static u64 BufferSize = 1_MiB;
fs::File outputFile(path, fs::File::Mode::Create);
wolv::io::File outputFile(path, wolv::io::File::Mode::Create);
std::vector<u8> buffer;
for (u64 offset = 0; offset < header->size; offset += BufferSize) {
buffer.resize(std::min<u64>(BufferSize, header->size - offset));
mtar_read_data(ctx, buffer.data(), buffer.size());
outputFile.write(buffer);
outputFile.writeVector(buffer);
}
}

View File

@@ -4,6 +4,7 @@
#include <codecvt>
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/helpers/fmt.hpp>
@@ -66,6 +67,25 @@ namespace hex {
return { data + index + 1 };
}
std::optional<u8> parseBinaryString(const std::string &string) {
if (string.empty())
return std::nullopt;
u8 byte = 0x00;
for (char c : string) {
byte <<= 1;
if (c == '1')
byte |= 0b01;
else if (c == '0')
byte |= 0b00;
else
return std::nullopt;
}
return byte;
}
std::string toByteString(u64 bytes) {
double value = bytes;
u8 unitIndex = 0;
@@ -482,4 +502,13 @@ namespace hex {
return value;
}
static std::optional<std::fs::path> fileToOpen;
extern "C" void openFile(const char *path) {
fileToOpen = path;
}
std::optional<std::fs::path> getInitialFilePath() {
return fileToOpen;
}
}

View File

@@ -9,6 +9,11 @@
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
void openFile(const char *path);
void openWebpageMacos(const char *url) {
CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL);
@@ -30,4 +35,21 @@
return [[NSScreen mainScreen] backingScaleFactor];
}
@interface HexDocument : NSDocument
@end
@implementation HexDocument
- (BOOL) readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError {
NSString* urlString = [url absoluteString];
const char* utf8String = [urlString UTF8String];
openFile(utf8String);
return YES;
}
@end
#endif

View File

@@ -9,6 +9,7 @@
#include <optional>
#include <hex/helpers/magic.hpp>
#include <wolv/io/file.hpp>
namespace hex::prv {
@@ -16,6 +17,7 @@ namespace hex::prv {
Provider::Provider() : m_id(s_idCounter++) {
this->m_patches.emplace_back();
this->m_currPatches = this->m_patches.begin();
}
Provider::~Provider() {
@@ -34,9 +36,33 @@ namespace hex::prv {
this->markDirty();
}
void Provider::save() { }
void Provider::save() {
EventManager::post<EventProviderSaved>(this);
}
void Provider::saveAs(const std::fs::path &path) {
hex::unused(path);
wolv::io::File file(path, wolv::io::File::Mode::Create);
if (file.isValid()) {
std::vector<u8> buffer(std::min<size_t>(0xFF'FFFF, this->getActualSize()), 0x00);
size_t bufferSize = 0;
for (u64 offset = 0; offset < this->getActualSize(); offset += bufferSize) {
bufferSize = buffer.size();
auto [region, valid] = this->getRegionValidity(offset + this->getBaseAddress());
if (!valid)
offset = region.getEndAddress() + 1;
auto [newRegion, newValid] = this->getRegionValidity(offset + this->getBaseAddress());
bufferSize = std::min<size_t>(bufferSize, (newRegion.getEndAddress() - offset) + 1);
bufferSize = std::min<size_t>(bufferSize, this->getActualSize() - offset);
this->read(offset + this->getBaseAddress(), buffer.data(), bufferSize, true);
file.writeBuffer(buffer.data(), bufferSize);
}
EventManager::post<EventProviderSaved>(this);
}
}
void Provider::resize(size_t newSize) {
@@ -95,25 +121,21 @@ namespace hex::prv {
std::map<u64, u8> &Provider::getPatches() {
auto iter = this->m_patches.end();
for (u32 i = 0; i < this->m_patchTreeOffset + 1; i++)
iter--;
return *(iter);
return *this->m_currPatches;
}
const std::map<u64, u8> &Provider::getPatches() const {
auto iter = this->m_patches.end();
for (u32 i = 0; i < this->m_patchTreeOffset + 1; i++)
iter--;
return *(iter);
return *this->m_currPatches;
}
void Provider::applyPatches() {
for (auto &[patchAddress, patch] : getPatches()) {
this->writeRaw(patchAddress - this->getBaseAddress(), &patch, 1);
}
if (!this->isWritable())
return;
this->markDirty();
this->m_patches.emplace_back();
@@ -175,17 +197,14 @@ namespace hex::prv {
}
void Provider::addPatch(u64 offset, const void *buffer, size_t size, bool createUndo) {
if (this->m_patchTreeOffset > 0) {
auto iter = this->m_patches.end();
for (u32 i = 0; i < this->m_patchTreeOffset; i++)
iter--;
if (createUndo) {
// Delete all patches after the current one if a modification is made while
// the current patch list is not at the end of the undo stack
if (std::next(this->m_currPatches) != this->m_patches.end())
this->m_patches.erase(std::next(this->m_currPatches), this->m_patches.end());
this->m_patches.erase(iter, this->m_patches.end());
this->m_patchTreeOffset = 0;
}
if (createUndo)
createUndoPoint();
}
for (u64 i = 0; i < size; i++) {
u8 patch = reinterpret_cast<const u8 *>(buffer)[i];
@@ -196,31 +215,35 @@ namespace hex::prv {
getPatches().erase(offset + i);
else
getPatches()[offset + i] = patch;
EventManager::post<EventPatchCreated>(offset, originalValue, patch);
}
this->markDirty();
}
void Provider::createUndoPoint() {
this->m_patches.push_back(getPatches());
this->m_currPatches = std::prev(this->m_patches.end());
}
void Provider::undo() {
if (canUndo())
this->m_patchTreeOffset++;
this->m_currPatches--;
}
void Provider::redo() {
if (canRedo())
this->m_patchTreeOffset--;
this->m_currPatches++;
}
bool Provider::canUndo() const {
return this->m_patchTreeOffset < this->m_patches.size() - 1;
return this->m_currPatches != this->m_patches.begin();
}
bool Provider::canRedo() const {
return this->m_patchTreeOffset > 0;
return std::next(this->m_currPatches) != this->m_patches.end();
}
bool Provider::hasFilePicker() const {
@@ -239,7 +262,8 @@ namespace hex::prv {
return false;
}
void Provider::drawLoadInterface() {
bool Provider::drawLoadInterface() {
return true;
}
void Provider::drawInterface() {

View File

@@ -14,6 +14,8 @@
#include <hex/api/imhex_api.hpp>
#include <fonts/codicons_font.h>
namespace ImGui {
Texture::Texture(const ImU8 *buffer, int size, int width, int height) {
@@ -263,6 +265,29 @@ namespace ImGui {
return pressed;
}
void HelpHover(const char *text) {
const auto iconColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, ImGui::GetStyle().FramePadding.y));
ImGui::PushStyleColor(ImGuiCol_Text, iconColor);
ImGui::Button(ICON_VS_INFO);
ImGui::PopStyleColor();
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetTextLineHeight() * 25, 0), ImVec2(ImGui::GetTextLineHeight() * 25, FLT_MAX));
ImGui::BeginTooltip();
ImGui::TextFormattedWrapped("{}", text);
ImGui::EndTooltip();
}
ImGui::PopStyleVar();
ImGui::PopStyleColor(3);
}
void UnderlinedText(const char *label, ImColor color, const ImVec2 &size_arg) {
ImGuiWindow *window = GetCurrentWindow();
@@ -344,6 +369,10 @@ namespace ImGui {
colors[ImGuiCustomCol_ToolbarBrown] = ImColor(219, 179, 119);
colors[ImGuiCustomCol_Highlight] = ImColor(77, 198, 155);
colors[ImGuiCustomCol_IEEEToolSign] = ImColor(93, 93, 127);
colors[ImGuiCustomCol_IEEEToolExp] = ImColor(93, 127, 93);
colors[ImGuiCustomCol_IEEEToolMantissa] = ImColor(127, 93, 93);
}
void StyleCustomColorsLight() {
@@ -362,6 +391,10 @@ namespace ImGui {
colors[ImGuiCustomCol_ToolbarBrown] = ImColor(219, 179, 119);
colors[ImGuiCustomCol_Highlight] = ImColor(41, 151, 112);
colors[ImGuiCustomCol_IEEEToolSign] = ImColor(187, 187, 255);
colors[ImGuiCustomCol_IEEEToolExp] = ImColor(187, 255, 187);
colors[ImGuiCustomCol_IEEEToolMantissa] = ImColor(255, 187,187);
}
void StyleCustomColorsClassic() {
@@ -380,6 +413,9 @@ namespace ImGui {
colors[ImGuiCustomCol_ToolbarBrown] = ImColor(219, 179, 119);
colors[ImGuiCustomCol_Highlight] = ImColor(77, 198, 155);
colors[ImGuiCustomCol_IEEEToolSign] = ImColor(93, 93, 127);
colors[ImGuiCustomCol_IEEEToolExp] = ImColor(93, 127, 93);
colors[ImGuiCustomCol_IEEEToolMantissa] = ImColor(127, 93, 93);
}
void OpenPopupInWindow(const char *window_name, const char *popup_name) {
@@ -500,7 +536,7 @@ namespace ImGui {
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered
: ImGuiCol_Button);
RenderNavHighlight(bb, id);
RenderFrame(bb.Min, bb.Max, col, false, style.FrameRounding);
RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
RenderTextClipped(bb.Min + style.FramePadding * ImVec2(1, 2), bb.Max - style.FramePadding, symbol, nullptr, &label_size, style.ButtonTextAlign, &bb);
PopStyleColor();
@@ -698,4 +734,34 @@ namespace ImGui {
return pressed;
}
bool DimmedButton(const char* label){
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetCustomColorU32(ImGuiCustomCol_DescButtonHovered));
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetCustomColorU32(ImGuiCustomCol_DescButton));
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_ButtonActive));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetCustomColorU32(ImGuiCustomCol_DescButtonActive));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
bool res = ImGui::Button(label);
ImGui::PopStyleColor(4);
ImGui::PopStyleVar(1);
return res;
}
bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size_arg){
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetCustomColorU32(ImGuiCustomCol_DescButtonHovered));
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetCustomColorU32(ImGuiCustomCol_DescButton));
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_ButtonActive));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetCustomColorU32(ImGuiCustomCol_DescButtonActive));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
bool res = IconButton(symbol, color, size_arg);
ImGui::PopStyleColor(4);
ImGui::PopStyleVar(1);
return res;
}
}

View File

@@ -0,0 +1,7 @@
#include <hex/ui/popup.hpp>
namespace hex::impl {
std::vector<std::unique_ptr<PopupBase>> PopupBase::s_openPopups;
}

View File

@@ -17,33 +17,6 @@ namespace hex {
return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable();
}
void View::showInfoPopup(const std::string &message) {
EventManager::post<RequestShowInfoPopup>(message);
}
void View::showErrorPopup(const std::string &message) {
EventManager::post<RequestShowErrorPopup>(message);
}
void View::showFatalPopup(const std::string &message) {
EventManager::post<RequestShowFatalErrorPopup>(message);
}
void View::showYesNoQuestionPopup(const std::string &message, const std::function<void()> &yesCallback, const std::function<void()> &noCallback) {
EventManager::post<RequestShowYesNoQuestionPopup>(message, yesCallback, noCallback);
}
void View::showFileChooserPopup(const std::vector<std::fs::path> &paths, const std::vector<nfdfilteritem_t> &validExtensions, bool multiple, const std::function<void(std::fs::path)> &callback) {
if (paths.empty()) {
fs::openFileBrowser(fs::DialogMode::Open, validExtensions, [callback](const auto &path) {
callback(path);
}, {}, multiple);
} else {
EventManager::post<RequestShowFileChooserPopup>(paths, validExtensions, callback, multiple);
}
}
bool View::hasViewMenuItemEntry() const {
return true;
}

View File

@@ -39,4 +39,4 @@ endif ()
if (APPLE)
add_compile_definitions(GL_SILENCE_DEPRECATION)
endif ()
endif ()

View File

@@ -40,9 +40,7 @@ namespace hex {
void exitGLFW();
void exitImGui();
friend void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *);
friend void ImHexSettingsHandler_ReadLine(ImGuiContext *, ImGuiSettingsHandler *handler, void *, const char *line);
friend void ImHexSettingsHandler_WriteAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf);
void registerEventHandlers();
GLFWwindow *m_window = nullptr;

View File

@@ -1,6 +1,8 @@
#include "init/splash_window.hpp"
#include <hex/api/imhex_api.hpp>
#include <hex/api/task.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/utils_macos.hpp>
#include <hex/helpers/fmt.hpp>
@@ -14,9 +16,12 @@
#include <hex/ui/imgui_imhex_extensions.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_opengl3_loader.h>
#include <fonts/fontawesome_font.h>
#include <GLFW/glfw3.h>
#include <wolv/utils/guards.hpp>
#include <unistd.h>
#include <chrono>
@@ -90,44 +95,54 @@ namespace hex::init {
}
bool WindowSplash::loop() {
// Load splash screen image from romfs
auto splash = romfs::get("splash.png");
ImGui::Texture splashTexture = ImGui::Texture(reinterpret_cast<const ImU8 *>(splash.data()), splash.size());
// If the image couldn't be loaded correctly, something went wrong during the build process
// Close the application since this would lead to errors later on anyway.
if (!splashTexture.isValid()) {
log::fatal("Could not load splash screen image!");
exit(EXIT_FAILURE);
}
// Launch init tasks in background
auto tasksSucceeded = processTasksAsync();
auto scale = ImHexApi::System::getGlobalScale();
// Splash window rendering loop
while (!glfwWindowShouldClose(this->m_window)) {
glfwPollEvents();
// Start a new ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// Draw the splash screen background
auto drawList = ImGui::GetForegroundDrawList();
{
std::lock_guard guard(this->m_progressMutex);
auto drawList = ImGui::GetForegroundDrawList();
drawList->AddImage(splashTexture, ImVec2(0, 0), splashTexture.getSize() * scale);
drawList->AddText(ImVec2(15, 120) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv 2020 - {0}", &__DATE__[7]).c_str());
#if defined(DEBUG) && defined(GIT_BRANCH) && defined(GIT_COMMIT_HASH)
drawList->AddText(ImVec2(15, 140) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{0} : {1} {2}@{3}", IMHEX_VERSION, ICON_FA_CODE_BRANCH, GIT_BRANCH, GIT_COMMIT_HASH).c_str());
#if defined(DEBUG) && defined(GIT_BRANCH) && defined(GIT_COMMIT_HASH_SHORT)
drawList->AddText(ImVec2(15, 140) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{0} : {1} {2}@{3}", IMHEX_VERSION, ICON_FA_CODE_BRANCH, GIT_BRANCH, GIT_COMMIT_HASH_SHORT).c_str());
#else
drawList->AddText(ImVec2(15, 140) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{0}", IMHEX_VERSION).c_str());
#endif
}
// Draw the task progress bar
{
std::lock_guard guard(this->m_progressMutex);
drawList->AddRectFilled(ImVec2(0, splashTexture.getSize().y - 5) * scale, ImVec2(splashTexture.getSize().x * this->m_progress, splashTexture.getSize().y) * scale, 0xFFFFFFFF);
drawList->AddText(ImVec2(15, splashTexture.getSize().y - 25) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("[{}] {}...", "|/-\\"[ImU32(ImGui::GetTime() * 15) % 4], this->m_currTaskName).c_str());
}
// Render the frame
ImGui::Render();
int displayWidth, displayHeight;
glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight);
@@ -138,6 +153,7 @@ namespace hex::init {
glfwSwapBuffers(this->m_window);
// Check if all background tasks have finished so the splash screen can be closed
if (tasksSucceeded.wait_for(0s) == std::future_status::ready) {
return tasksSucceeded.get();
}
@@ -147,20 +163,25 @@ namespace hex::init {
}
static void centerWindow(GLFWwindow *window) {
// Get the primary monitor
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
if (!monitor)
return;
// Get information about the monitor
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
if (!mode)
return;
// Get the position of the monitor's viewport on the virtual screen
int monitorX, monitorY;
glfwGetMonitorPos(monitor, &monitorX, &monitorY);
// Get the window size
int windowWidth, windowHeight;
glfwGetWindowSize(window, &windowWidth, &windowHeight);
// Center the splash screen on the monitor
glfwSetWindowPos(window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2);
}
@@ -174,22 +195,25 @@ namespace hex::init {
exit(EXIT_FAILURE);
}
// Configure used OpenGL version
#if defined(OS_MACOS)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
#else
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif
// Make splash screen non-resizable, undecorated and transparent
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
// Create the splash screen window
this->m_window = glfwCreateWindow(1, 400, "Starting ImHex...", nullptr, nullptr);
if (this->m_window == nullptr) {
log::fatal("Failed to create GLFW window!");
@@ -223,6 +247,7 @@ namespace hex::init {
}
void WindowSplash::initImGui() {
// Initialize ImGui
IMGUI_CHECKVERSION();
GImGui = ImGui::CreateContext();
ImGui::StyleColorsDark();
@@ -239,31 +264,36 @@ namespace hex::init {
ImGui::GetStyle().ScaleAllSizes(ImHexApi::System::getGlobalScale());
io.Fonts->Clear();
// Load fonts necessary for the splash screen
{
io.Fonts->Clear();
ImFontConfig cfg;
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
cfg.SizePixels = 13.0_scaled;
io.Fonts->AddFontDefault(&cfg);
ImFontConfig cfg;
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
cfg.SizePixels = 13.0_scaled;
io.Fonts->AddFontDefault(&cfg);
cfg.MergeMode = true;
cfg.MergeMode = true;
ImWchar fontAwesomeRange[] = {
ICON_MIN_FA, ICON_MAX_FA, 0
};
std::uint8_t *px;
int w, h;
io.Fonts->AddFontFromMemoryCompressedTTF(font_awesome_compressed_data, font_awesome_compressed_size, 11.0_scaled, &cfg, fontAwesomeRange);
io.Fonts->GetTexDataAsRGBA32(&px, &w, &h);
ImWchar fontAwesomeRange[] = {
ICON_MIN_FA, ICON_MAX_FA, 0
};
std::uint8_t *px;
int w, h;
io.Fonts->AddFontFromMemoryCompressedTTF(font_awesome_compressed_data, font_awesome_compressed_size, 11.0_scaled, &cfg, fontAwesomeRange);
io.Fonts->GetTexDataAsAlpha8(&px, &w, &h);
// Create new font atlas
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA8, GL_UNSIGNED_INT, px);
io.Fonts->SetTexID(reinterpret_cast<ImTextureID>(tex));
// Create new font atlas
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, px);
io.Fonts->SetTexID(reinterpret_cast<ImTextureID>(tex));
}
// Don't save window settings for the splash screen
io.IniFilename = nullptr;
}

View File

@@ -1,60 +1,104 @@
#include "init/tasks.hpp"
#include <imgui.h>
#include <imgui_freetype.h>
#include <romfs/romfs.hpp>
#include <hex/helpers/http_requests.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/api_urls.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/theme_manager.hpp>
#include <hex/api/plugin_manager.hpp>
#include <hex/api/layout_manager.hpp>
#include <hex/ui/view.hpp>
#include <hex/helpers/net.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/ui/popup.hpp>
#include <fonts/fontawesome_font.h>
#include <fonts/codicons_font.h>
#include <fonts/unifont_font.h>
#include <hex/api/plugin_manager.hpp>
#include <filesystem>
#include <nlohmann/json.hpp>
#include <wolv/io/fs.hpp>
#include <wolv/io/file.hpp>
namespace hex::init {
using namespace std::literals::string_literals;
#if defined(HEX_UPDATE_CHECK)
static bool checkForUpdates() {
// documentation of the value above the setting definition
int showCheckForUpdates = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.check_for_updates", 2);
// Check if we should check for updates
if (showCheckForUpdates == 1){
hex::Net net;
HttpRequest request("GET", GitHubApiURL + "/releases/latest"s);
request.setTimeout(2000);
auto releases = net.getJson(GitHubApiURL + "/releases/latest"s, 2000).get();
if (releases.code != 200)
// Query the GitHub API for the latest release version
auto response = request.execute().get();
if (response.getStatusCode() != 200)
return false;
if (!releases.body.contains("tag_name") || !releases.body["tag_name"].is_string())
nlohmann::json releases;
try {
releases = nlohmann::json::parse(response.getData());
} catch (std::exception &e) {
return false;
}
// Check if the response is valid
if (!releases.contains("tag_name") || !releases["tag_name"].is_string())
return false;
// Convert the current version string to a format that can be compared to the latest release
auto versionString = std::string(IMHEX_VERSION);
size_t versionLength = std::min(versionString.find_first_of('-'), versionString.length());
auto currVersion = "v" + versionString.substr(0, versionLength);
auto latestVersion = releases.body["tag_name"].get<std::string_view>();
// Get the latest release version string
auto latestVersion = releases["tag_name"].get<std::string_view>();
// Check if the latest release is different from the current version
if (latestVersion != currVersion)
ImHexApi::System::impl::addInitArgument("update-available", latestVersion.data());
}
return true;
}
#endif
bool setupEnvironment() {
hex::log::debug("Using romfs: '{}'", romfs::name());
Net::setCACert(std::string(romfs::get("cacert.pem").string()));
// Load the SSL certificate
constexpr static auto CaCertFileName = "cacert.pem";
// Look for a custom certificate in the config folder
std::fs::path caCertPath;
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Config)) {
for (const auto &file : std::fs::directory_iterator(folder)) {
if (file.path().filename() == CaCertFileName) {
caCertPath = file.path();
break;
}
}
}
// If a custom certificate was found, use it, otherwise use the one from the romfs
std::string caCertData;
if (!caCertPath.empty())
caCertData = wolv::io::File(caCertPath, wolv::io::File::Mode::Read).readString();
else
caCertData = std::string(romfs::get(CaCertFileName).string());
HttpRequest::setCACert(caCertData);
return true;
}
@@ -64,13 +108,13 @@ namespace hex::init {
using enum fs::ImHexPath;
// Create all folders
// Try to create all default directories
for (u32 path = 0; path < u32(fs::ImHexPath::END); path++) {
for (auto &folder : fs::getDefaultPaths(static_cast<fs::ImHexPath>(path), true)) {
try {
fs::createDirectories(folder);
wolv::io::fs::createDirectories(folder);
} catch (...) {
log::error("Failed to create folder {}!", hex::toUTF8String(folder));
log::error("Failed to create folder {}!", wolv::util::toUTF8String(folder));
result = false;
}
}
@@ -82,68 +126,129 @@ namespace hex::init {
return result;
}
bool migrateConfig(){
// Check if there is a new config in folder
auto configPaths = hex::fs::getDefaultPaths(hex::fs::ImHexPath::Config, false);
// There should always be exactly one config path on Linux
std::fs::path newConfigPath = configPaths[0];
wolv::io::File newConfigFile(newConfigPath / "settings.json", wolv::io::File::Mode::Read);
if (!newConfigFile.isValid()) {
// find an old config
std::fs::path oldConfigPath;
for (const auto &dir : hex::fs::appendPath(hex::fs::getDataPaths(), "config")) {
wolv::io::File oldConfigFile(dir / "settings.json", wolv::io::File::Mode::Read);
if (oldConfigFile.isValid()) {
oldConfigPath = dir;
break;
}
}
if (!oldConfigPath.empty()) {
log::info("Found config file in {}! Migrating to {}", oldConfigPath.string(), newConfigPath.string());
std::fs::rename(oldConfigPath / "settings.json", newConfigPath / "settings.json");
wolv::io::File oldIniFile(oldConfigPath / "interface.ini", wolv::io::File::Mode::Read);
if (oldIniFile.isValid()) {
std::fs::rename(oldConfigPath / "interface.ini", newConfigPath / "interface.ini");
}
std::fs::remove(oldConfigPath);
}
}
return true;
}
static bool loadFontsImpl(bool loadUnicode) {
float fontSize = ImHexApi::System::getFontSize();
const auto &fontFile = ImHexApi::System::getCustomFontPath();
// Setup basic font configuration
auto fonts = IM_NEW(ImFontAtlas)();
ImFontConfig cfg = {};
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
cfg.OversampleH = cfg.OversampleV = 2, cfg.PixelSnapH = true;
cfg.SizePixels = fontSize;
fonts->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;
// Configure font glyph ranges that should be loaded from the default font and unifont
ImVector<ImWchar> ranges;
{
ImFontGlyphRangesBuilder glyphRangesBuilder;
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesDefault());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesJapanese());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesChineseFull());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesCyrillic());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesKorean());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesThai());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesVietnamese());
{
constexpr static ImWchar controlCodeRange[] = { 0x0000, 0x001F, 0 };
constexpr static ImWchar controlCodeRange[] = { 0x0001, 0x001F, 0 };
constexpr static ImWchar extendedAsciiRange[] = { 0x007F, 0x00FF, 0 };
glyphRangesBuilder.AddRanges(controlCodeRange);
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesDefault());
glyphRangesBuilder.AddRanges(extendedAsciiRange);
}
if (loadUnicode) {
constexpr static ImWchar fullRange[] = { 0x0100, 0xFFEF, 0 };
glyphRangesBuilder.AddRanges(fullRange);
} else {
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesJapanese());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesChineseFull());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesCyrillic());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesKorean());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesThai());
glyphRangesBuilder.AddRanges(fonts->GetGlyphRangesVietnamese());
}
glyphRangesBuilder.BuildRanges(&ranges);
}
// Glyph range for font awesome icons
ImWchar fontAwesomeRange[] = {
ICON_MIN_FA, ICON_MAX_FA, 0
};
// Glyph range for codicons icons
ImWchar codiconsRange[] = {
ICON_MIN_VS, ICON_MAX_VS, 0
};
// Load main font
// If a custom font has been specified, load it, otherwise load the default ImGui font
if (fontFile.empty()) {
// Load default font if no custom one has been specified
fonts->Clear();
fonts->AddFontDefault(&cfg);
} else {
// Load custom font
fonts->AddFontFromFileTTF(hex::toUTF8String(fontFile).c_str(), 0, &cfg, ranges.Data);
fonts->AddFontFromFileTTF(wolv::util::toUTF8String(fontFile).c_str(), 0, &cfg, ranges.Data);
}
// Merge all fonts into one big font atlas
cfg.MergeMode = true;
// Add font awesome and codicons icons to font atlas
fonts->AddFontFromMemoryCompressedTTF(font_awesome_compressed_data, font_awesome_compressed_size, 0, &cfg, fontAwesomeRange);
fonts->AddFontFromMemoryCompressedTTF(codicons_compressed_data, codicons_compressed_size, 0, &cfg, codiconsRange);
// Add unifont if unicode support is enabled
if (loadUnicode)
fonts->AddFontFromMemoryCompressedTTF(unifont_compressed_data, unifont_compressed_size, 0, &cfg, ranges.Data);
// Try to build the font atlas
if (!fonts->Build()) {
// The main reason the font atlas failed to build is that the font is too big for the GPU to handle
// If unicode support is enabled, therefor try to load the font atlas without unicode support
// If that still didn't work, there's probably something else going on with the graphics drivers
// Especially Intel GPU drivers are known to have various bugs
if (loadUnicode) {
log::error("Failed to build font atlas! Disabling Unicode support.");
ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.enable_unicode", false);
IM_DELETE(fonts);
// Disable unicode support in settings
ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.enable_unicode", false);
// Try to load the font atlas again
return loadFontsImpl(false);
} else {
log::error("Failed to build font atlas! Check your Graphics driver!");
@@ -151,6 +256,7 @@ namespace hex::init {
}
}
// Configure ImGui to use the font atlas
View::setFontAtlas(fonts);
View::setFontConfig(cfg);
@@ -162,11 +268,15 @@ namespace hex::init {
}
bool deleteSharedData() {
EventManager::clear();
// This function is called when ImHex is closed. It deletes all shared data that was created by plugins
// This is a bit of a hack but necessary because when ImHex gets closed, all plugins are unloaded in order for
// destructors to be called correctly. To prevent crashes when ImHex exits, we need to delete all shared data
EventManager::post<EventImHexClosing>();
while (ImHexApi::Provider::isValid())
ImHexApi::Provider::remove(ImHexApi::Provider::get());
ContentRegistry::Provider::getEntries().clear();
ContentRegistry::Provider::impl::getEntries().clear();
ImHexApi::System::getInitArguments().clear();
ImHexApi::HexEditor::impl::getBackgroundHighlights().clear();
@@ -176,50 +286,53 @@ namespace hex::init {
ImHexApi::HexEditor::impl::getTooltips().clear();
ImHexApi::HexEditor::impl::getTooltipFunctions().clear();
ContentRegistry::Settings::getEntries().clear();
ContentRegistry::Settings::getSettingsData().clear();
ContentRegistry::Settings::impl::getEntries().clear();
ContentRegistry::Settings::impl::getSettingsData().clear();
ContentRegistry::CommandPaletteCommands::getEntries().clear();
ContentRegistry::CommandPaletteCommands::impl::getEntries().clear();
ContentRegistry::CommandPaletteCommands::impl::getHandlers().clear();
ContentRegistry::PatternLanguage::getFunctions().clear();
ContentRegistry::PatternLanguage::getPragmas().clear();
ContentRegistry::PatternLanguage::impl::getFunctions().clear();
ContentRegistry::PatternLanguage::impl::getPragmas().clear();
ContentRegistry::PatternLanguage::impl::getVisualizers().clear();
{
auto &views = ContentRegistry::Views::getEntries();
for (auto &[name, view] : views)
delete view;
views.clear();
}
ContentRegistry::Views::impl::getEntries().clear();
impl::PopupBase::getOpenPopups().clear();
ContentRegistry::Tools::getEntries().clear();
ContentRegistry::DataInspector::getEntries().clear();
ContentRegistry::Tools::impl::getEntries().clear();
ContentRegistry::DataInspector::impl::getEntries().clear();
ContentRegistry::Language::getLanguages().clear();
ContentRegistry::Language::getLanguageDefinitions().clear();
ContentRegistry::Language::impl::getLanguages().clear();
ContentRegistry::Language::impl::getLanguageDefinitions().clear();
LangEntry::resetLanguageStrings();
ContentRegistry::Interface::getWelcomeScreenEntries().clear();
ContentRegistry::Interface::getFooterItems().clear();
ContentRegistry::Interface::getToolbarItems().clear();
ContentRegistry::Interface::getMainMenuItems().clear();
ContentRegistry::Interface::getMenuItems().clear();
ContentRegistry::Interface::getSidebarItems().clear();
ContentRegistry::Interface::getTitleBarButtons().clear();
ContentRegistry::Interface::getLayouts().clear();
ContentRegistry::Interface::impl::getWelcomeScreenEntries().clear();
ContentRegistry::Interface::impl::getFooterItems().clear();
ContentRegistry::Interface::impl::getToolbarItems().clear();
ContentRegistry::Interface::impl::getMainMenuItems().clear();
ContentRegistry::Interface::impl::getMenuItems().clear();
ContentRegistry::Interface::impl::getSidebarItems().clear();
ContentRegistry::Interface::impl::getTitleBarButtons().clear();
ShortcutManager::clearShortcuts();
TaskManager::getRunningTasks().clear();
ContentRegistry::DataProcessorNode::getEntries().clear();
ContentRegistry::DataProcessorNode::impl::getEntries().clear();
ContentRegistry::DataFormatter::getEntries().clear();
ContentRegistry::FileHandler::getEntries().clear();
ContentRegistry::DataFormatter::impl::getEntries().clear();
ContentRegistry::FileHandler::impl::getEntries().clear();
ContentRegistry::Hashes::impl::getHashes().clear();
api::ThemeManager::reset();
ContentRegistry::BackgroundServices::impl::stopServices();
ContentRegistry::BackgroundServices::impl::getServices().clear();
ContentRegistry::CommunicationInterface::impl::getNetworkEndpoints().clear();
LayoutManager::reset();
ThemeManager::reset();
{
auto &visualizers = ContentRegistry::HexEditor::impl::getVisualizers();
@@ -233,16 +346,21 @@ namespace hex::init {
fs::setFileBrowserErrorCallback(nullptr);
EventManager::clear();
return true;
}
bool loadPlugins() {
// Load plugins
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
PluginManager::load(dir);
}
// Get loaded plugins
auto &plugins = PluginManager::getPlugins();
// If no plugins were loaded, ImHex wasn't installed properly. This will trigger an error popup later on
if (plugins.empty()) {
log::error("No plugins found!");
@@ -250,7 +368,8 @@ namespace hex::init {
return false;
}
const auto shouldLoadPlugin = [executablePath = hex::fs::getExecutablePath()](const Plugin &plugin) {
const auto shouldLoadPlugin = [executablePath = wolv::io::fs::getExecutablePath()](const Plugin &plugin) {
// In debug builds, ignore all plugins that are not part of the executable directory
#if !defined(DEBUG)
return true;
#endif
@@ -258,12 +377,14 @@ namespace hex::init {
if (!executablePath.has_value())
return true;
// In debug builds, ignore all plugins that are not part of the executable directory
// Check if the plugin is somewhere in the same directory tree as the executable
return !std::fs::relative(plugin.getPath(), executablePath->parent_path()).string().starts_with("..");
};
u32 builtinPlugins = 0;
u32 loadErrors = 0;
// Load the builtin plugin first, so it can initialize everything that's necessary for ImHex to work
for (const auto &plugin : plugins) {
if (!plugin.isBuiltinPlugin()) continue;
@@ -272,15 +393,18 @@ namespace hex::init {
continue;
}
// Make sure there's only one built-in plugin
builtinPlugins++;
if (builtinPlugins > 1) continue;
// Initialize the plugin
if (!plugin.initializePlugin()) {
log::error("Failed to initialize plugin {}", hex::toUTF8String(plugin.getPath().filename()));
log::error("Failed to initialize plugin {}", wolv::util::toUTF8String(plugin.getPath().filename()));
loadErrors++;
}
}
// Load all other plugins
for (const auto &plugin : plugins) {
if (plugin.isBuiltinPlugin()) continue;
@@ -289,18 +413,22 @@ namespace hex::init {
continue;
}
// Initialize the plugin
if (!plugin.initializePlugin()) {
log::error("Failed to initialize plugin {}", hex::toUTF8String(plugin.getPath().filename()));
log::error("Failed to initialize plugin {}", wolv::util::toUTF8String(plugin.getPath().filename()));
loadErrors++;
}
}
// If no plugins were loaded successfully, ImHex wasn't installed properly. This will trigger an error popup later on
if (loadErrors == plugins.size()) {
log::error("No plugins loaded successfully!");
ImHexApi::System::impl::addInitArgument("no-plugins");
return false;
}
// ImHex requires exactly one built-in plugin
// If no built-in plugin or more than one was found, something's wrong and we can't continue
if (builtinPlugins == 0) {
log::error("Built-in plugin not found!");
ImHexApi::System::impl::addInitArgument("no-builtin-plugin");
@@ -314,6 +442,27 @@ namespace hex::init {
return true;
}
bool clearOldLogs() {
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Logs, true)) {
std::vector<std::filesystem::directory_entry> files;
for (const auto& file : std::filesystem::directory_iterator(path))
files.push_back(file);
if (files.size() <= 10)
return true;
std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) {
return std::filesystem::last_write_time(a) > std::filesystem::last_write_time(b);
});
for (auto it = files.begin() + 10; it != files.end(); it++)
std::filesystem::remove(it->path());
}
return true;
}
bool unloadPlugins() {
PluginManager::unload();
@@ -322,12 +471,15 @@ namespace hex::init {
bool loadSettings() {
try {
ContentRegistry::Settings::load();
// Try to load settings from file
ContentRegistry::Settings::impl::load();
} catch (std::exception &e) {
// If that fails, create a new settings file
log::error("Failed to load configuration! {}", e.what());
ContentRegistry::Settings::clear();
ContentRegistry::Settings::store();
ContentRegistry::Settings::impl::clear();
ContentRegistry::Settings::impl::store();
return false;
}
@@ -337,7 +489,7 @@ namespace hex::init {
bool storeSettings() {
try {
ContentRegistry::Settings::store();
ContentRegistry::Settings::impl::store();
} catch (std::exception &e) {
log::error("Failed to store configuration! {}", e.what());
return false;
@@ -349,9 +501,14 @@ namespace hex::init {
return {
{ "Setting up environment", setupEnvironment, false },
{ "Creating directories", createDirectories, false },
#if defined(OS_LINUX)
{ "Migrate config to .config", migrateConfig, false },
#endif
{ "Loading settings", loadSettings, false },
{ "Loading plugins", loadPlugins, false },
#if defined(HEX_UPDATE_CHECK)
{ "Checking for updates", checkForUpdates, true },
#endif
{ "Loading fonts", loadFonts, true },
};
}
@@ -361,7 +518,8 @@ namespace hex::init {
{ "Saving settings...", storeSettings, false },
{ "Cleaning up shared data...", deleteSharedData, false },
{ "Unloading plugins...", unloadPlugins, false },
{ "Clearing old logs...", clearOldLogs, false },
};
}
}
}

View File

@@ -1,6 +1,5 @@
#include <hex.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/logger.hpp>
#include "window.hpp"
@@ -8,25 +7,29 @@
#include "init/splash_window.hpp"
#include "init/tasks.hpp"
#include <hex/api/task.hpp>
#include <hex/api/project_file_manager.hpp>
#include <wolv/io/fs.hpp>
#include <wolv/utils/guards.hpp>
int main(int argc, char **argv, char **envp) {
using namespace hex;
ImHexApi::System::impl::setProgramArguments(argc, argv, envp);
bool shouldRestart = false;
// Check if ImHex is installed in portable mode
{
if (const auto executablePath = fs::getExecutablePath(); executablePath.has_value()) {
if (const auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
const auto flagFile = executablePath->parent_path() / "PORTABLE";
if (fs::exists(flagFile) && fs::isRegularFile(flagFile))
if (wolv::io::fs::exists(flagFile) && wolv::io::fs::isRegularFile(flagFile))
ImHexApi::System::impl::setPortableVersion(true);
}
}
bool shouldRestart = false;
do {
// Register an event to handle restarting of ImHex
EventManager::subscribe<RequestRestartImHex>([&]{ shouldRestart = true; });
shouldRestart = false;
@@ -38,15 +41,17 @@ int main(int argc, char **argv, char **envp) {
init::WindowSplash splashWindow;
// Add initialization tasks to run
TaskManager::init();
for (const auto &[name, task, async] : init::getInitTasks())
splashWindow.addStartupTask(name, task, async);
// Draw the splash window while tasks are running
if (!splashWindow.loop())
ImHexApi::System::getInitArguments().insert({ "tasks-failed", {} });
}
// Clean up
// Clean up everything after the main window is closed
ON_SCOPE_EXIT {
for (const auto &[name, task, async] : init::getExitTasks())
task();
@@ -65,7 +70,12 @@ int main(int argc, char **argv, char **envp) {
}
}
// Open file that has been requested to be opened through other, OS-specific means
if (auto path = hex::getInitialFilePath(); path.has_value()) {
EventManager::post<RequestOpenFile>(path.value());
}
// Render the main window
window.loop();
}

View File

@@ -9,7 +9,10 @@
#include <hex/helpers/utils.hpp>
#include <hex/helpers/logger.hpp>
#include <wolv/utils/core.hpp>
#include <nlohmann/json.hpp>
#include <cstdio>
#include <sys/wait.h>
#include <unistd.h>
@@ -24,6 +27,12 @@ namespace hex {
setenv("LD_LIBRARY_PATH", hex::format("{};{}", hex::getEnvironmentVariable("LD_LIBRARY_PATH").value_or(""), path.string().c_str()).c_str(), true);
}
// Various libraries sadly directly print to stderr with no way to disable it
// We redirect stderr to /dev/null to prevent this
wolv::util::unused(freopen("/dev/null", "w", stderr));
setvbuf(stderr, nullptr, _IONBF, 0);
// Redirect stdout to log file if we're not running in a terminal
if (!isatty(STDOUT_FILENO)) {
log::redirectToFile();
}
@@ -39,6 +48,7 @@ namespace hex {
std::array<char, 128> buffer = { 0 };
std::string result;
// Ask GNOME for the current theme
// TODO: In the future maybe support more DEs instead of just GNOME
FILE *pipe = popen("gsettings get org.gnome.desktop.interface gtk-theme 2>&1", "r");
if (pipe == nullptr) return;

View File

@@ -10,6 +10,7 @@
#include <hex/helpers/logger.hpp>
#include <nlohmann/json.hpp>
#include <cstdio>
#include <unistd.h>
#include <imgui_impl_glfw.h>
@@ -23,6 +24,12 @@ namespace hex {
setenv("LD_LIBRARY_PATH", hex::format("{};{}", hex::getEnvironmentVariable("LD_LIBRARY_PATH").value_or(""), path.string().c_str()).c_str(), true);
}
// Various libraries sadly directly print to stderr with no way to disable it
// We redirect stderr to /dev/null to prevent this
freopen("/dev/null", "w", stderr);
setvbuf(stderr, nullptr, _IONBF, 0);
// Redirect stdout to log file if we're not running in a terminal
if (!isatty(STDOUT_FILENO)) {
log::redirectToFile();
}

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