mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-29 00:10:02 -05:00
Compare commits
306 Commits
v1.38.1
...
feature/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b2e95b9d0 | ||
|
|
c677aae7b1 | ||
|
|
f70268ea40 | ||
|
|
d454714428 | ||
|
|
8b938faf01 | ||
|
|
324170e0d8 | ||
|
|
e463df9fc4 | ||
|
|
fe348d33e7 | ||
|
|
f9343c8e94 | ||
|
|
1670607e38 | ||
|
|
c92b55e1b6 | ||
|
|
77eff651ed | ||
|
|
219f588cbe | ||
|
|
0e2d7ee3bc | ||
|
|
b57d9118c1 | ||
|
|
d4879572fa | ||
|
|
854535fec6 | ||
|
|
a109e14ee3 | ||
|
|
eca7597aca | ||
|
|
936b5b6312 | ||
|
|
cea63f6561 | ||
|
|
7083b6ab3e | ||
|
|
432e16e0c4 | ||
|
|
8d691b2e6a | ||
|
|
f3ccbb9b84 | ||
|
|
a4af283a37 | ||
|
|
27935b1234 | ||
|
|
ea0b97a066 | ||
|
|
e668feb807 | ||
|
|
21a94d67c2 | ||
|
|
fbd6d6b9fc | ||
|
|
257d122a9f | ||
|
|
f7f70a16da | ||
|
|
aa5a83444d | ||
|
|
d25c80c0a5 | ||
|
|
6f83b050cd | ||
|
|
72e177aafc | ||
|
|
5648378837 | ||
|
|
c1cdef7ca1 | ||
|
|
4b07f7745b | ||
|
|
a2fc9325c9 | ||
|
|
380b1dbce3 | ||
|
|
04a8bd6798 | ||
|
|
0cf27b9e9e | ||
|
|
4a091f452e | ||
|
|
ffa8b0d0db | ||
|
|
ff65217b7e | ||
|
|
1b90bb2c34 | ||
|
|
1018aea395 | ||
|
|
d20d6736b3 | ||
|
|
5333a33775 | ||
|
|
0d9ea8a1d1 | ||
|
|
b6a90aa89d | ||
|
|
060f0e6e56 | ||
|
|
800a24b42e | ||
|
|
a5008722aa | ||
|
|
a0b0082d98 | ||
|
|
f476842008 | ||
|
|
d39d107de4 | ||
|
|
7f889a75bb | ||
|
|
70fdbd4a48 | ||
|
|
71c71c5bbf | ||
|
|
ade8c8f68d | ||
|
|
16b02caf7a | ||
|
|
dc1c205fb1 | ||
|
|
b974533f96 | ||
|
|
81e561d47b | ||
|
|
b9e0a9f0e6 | ||
|
|
954bcadd75 | ||
|
|
d6781e7f93 | ||
|
|
fc38c27769 | ||
|
|
96a5a5d34c | ||
|
|
928e0f227a | ||
|
|
93fc6f2de1 | ||
|
|
cdc260b45c | ||
|
|
0e41813cfc | ||
|
|
a7e94e31c9 | ||
|
|
2903a97941 | ||
|
|
753e1ceff6 | ||
|
|
06f4d12f10 | ||
|
|
684c2e66fb | ||
|
|
4a311ed69f | ||
|
|
220b5f9772 | ||
|
|
42fd5b0fef | ||
|
|
c28492e51d | ||
|
|
d8ff84672c | ||
|
|
2af967d788 | ||
|
|
8cb23e8200 | ||
|
|
9a058efc79 | ||
|
|
06c8cb51e3 | ||
|
|
01e8ae1b5c | ||
|
|
b052cb001c | ||
|
|
d004eb3048 | ||
|
|
dccd74667e | ||
|
|
6b6470850f | ||
|
|
f09b1aae23 | ||
|
|
68487bc903 | ||
|
|
ebd78e1526 | ||
|
|
6d0dfcfe2e | ||
|
|
5b01d23ed7 | ||
|
|
d9eb01b526 | ||
|
|
9e165ac4a1 | ||
|
|
25ab371a81 | ||
|
|
d62abaed8d | ||
|
|
1fb7a318ed | ||
|
|
e29ae631fc | ||
|
|
3cc0b9294e | ||
|
|
e80f7fa14f | ||
|
|
9261fd7190 | ||
|
|
58c714b350 | ||
|
|
5cd3f5c255 | ||
|
|
0d443d7d00 | ||
|
|
38fecca489 | ||
|
|
f655ea59a8 | ||
|
|
aa7daa5c54 | ||
|
|
f4b2be9334 | ||
|
|
edbd8a811e | ||
|
|
ea9b197d36 | ||
|
|
a0a049a920 | ||
|
|
550fe8e4aa | ||
|
|
2064aea3b6 | ||
|
|
3411bc4577 | ||
|
|
6eedb469e9 | ||
|
|
684373b88b | ||
|
|
08aa03bab6 | ||
|
|
62bc953d53 | ||
|
|
5263f81487 | ||
|
|
8272ebf68f | ||
|
|
90d8e03f2c | ||
|
|
0d34edc7f6 | ||
|
|
63fd61e245 | ||
|
|
58fe8a22a9 | ||
|
|
03dd20c263 | ||
|
|
712a125be7 | ||
|
|
49e14e6e7c | ||
|
|
e8a6e102c3 | ||
|
|
b2cc09852d | ||
|
|
60627b8325 | ||
|
|
8fc2d6b225 | ||
|
|
051cdfa305 | ||
|
|
bfa807ca8b | ||
|
|
598914a67a | ||
|
|
3274649b77 | ||
|
|
5756105347 | ||
|
|
ed583d8bd1 | ||
|
|
731cf10207 | ||
|
|
cb898ce8cf | ||
|
|
892334e31a | ||
|
|
a16e16853f | ||
|
|
5cec83b3ef | ||
|
|
a628784c6d | ||
|
|
fd8b70fb12 | ||
|
|
3dad5a43a1 | ||
|
|
89dea86b3b | ||
|
|
42da24e31d | ||
|
|
5b9b5d4f1f | ||
|
|
5332a26294 | ||
|
|
54d9f8ec5c | ||
|
|
73a17308cc | ||
|
|
b835c48a0c | ||
|
|
40b604c6e4 | ||
|
|
1fd3580f97 | ||
|
|
e28f3b75a4 | ||
|
|
53153ca3e0 | ||
|
|
a496b14a0f | ||
|
|
da6e7240d6 | ||
|
|
89981c6994 | ||
|
|
fe22a43e09 | ||
|
|
3e1a797ea7 | ||
|
|
b23ce7ba18 | ||
|
|
6165f891ca | ||
|
|
7df4b1157c | ||
|
|
64cbd5fc8d | ||
|
|
ba7e789a80 | ||
|
|
d6d70ca076 | ||
|
|
88c37bb7d9 | ||
|
|
59b4f4efce | ||
|
|
ed1f120b0b | ||
|
|
bf461abfec | ||
|
|
d42665db88 | ||
|
|
33c4dc3347 | ||
|
|
6d976fb785 | ||
|
|
39b43cec2d | ||
|
|
691b56b4ac | ||
|
|
bf1f613052 | ||
|
|
721ed9f2a2 | ||
|
|
f760b1ba83 | ||
|
|
1dba144fca | ||
|
|
ebc1b531ff | ||
|
|
5ecf122686 | ||
|
|
56ed18882c | ||
|
|
86c555d053 | ||
|
|
49ec30899e | ||
|
|
d4a2b617bd | ||
|
|
8e7bd4b98a | ||
|
|
ea359285e0 | ||
|
|
d4bfa5d284 | ||
|
|
4433006842 | ||
|
|
646ebcdd00 | ||
|
|
8b53b36b20 | ||
|
|
64db392699 | ||
|
|
bff78704cc | ||
|
|
40651e8dfd | ||
|
|
e5d9d9ec9e | ||
|
|
4e628826c3 | ||
|
|
018bedb2bd | ||
|
|
33e315709a | ||
|
|
8b14a4775b | ||
|
|
fa8fdb0170 | ||
|
|
f856e16917 | ||
|
|
3b5271ab77 | ||
|
|
428fbddbbb | ||
|
|
5774837a6e | ||
|
|
17c2dfcbd0 | ||
|
|
a1711ccfa6 | ||
|
|
261610dcf1 | ||
|
|
92cfdf1145 | ||
|
|
155465b8c6 | ||
|
|
6f49bbdd41 | ||
|
|
3badaa5cba | ||
|
|
a66747a0d0 | ||
|
|
c376759be0 | ||
|
|
1c17f3ee43 | ||
|
|
5f549cc8aa | ||
|
|
f97be02087 | ||
|
|
2d82776e62 | ||
|
|
fdee0ac3e3 | ||
|
|
d775b80a44 | ||
|
|
2047a41498 | ||
|
|
f88890a052 | ||
|
|
5500faa57e | ||
|
|
89004574d3 | ||
|
|
c11c05a399 | ||
|
|
baa3329e7f | ||
|
|
e696d384c2 | ||
|
|
932c281223 | ||
|
|
858fe0384e | ||
|
|
e904cd749f | ||
|
|
6b16f39be4 | ||
|
|
021c7e5fdb | ||
|
|
c161a5c71b | ||
|
|
76ccdbccea | ||
|
|
553ee89787 | ||
|
|
cb6247b16e | ||
|
|
cfac7ff0ba | ||
|
|
49bbe7dc77 | ||
|
|
07b6fa0e2e | ||
|
|
67396f2009 | ||
|
|
a20ff87cc9 | ||
|
|
e02e57a729 | ||
|
|
225dc53795 | ||
|
|
e7404376db | ||
|
|
d6aec341fe | ||
|
|
3a3c2fb204 | ||
|
|
e9b5cdbccf | ||
|
|
3f30e63d95 | ||
|
|
f9c6866c7b | ||
|
|
388dccfd9f | ||
|
|
1676342e28 | ||
|
|
62732de227 | ||
|
|
63e777c84c | ||
|
|
ab95cdf3e5 | ||
|
|
827b5b01dd | ||
|
|
bfa9788099 | ||
|
|
de25ce7fbb | ||
|
|
21e61bfce6 | ||
|
|
82e168c438 | ||
|
|
48583a2b6e | ||
|
|
0db0982fa7 | ||
|
|
6a28ce9e4b | ||
|
|
1db79f6117 | ||
|
|
f234103320 | ||
|
|
45c382a19a | ||
|
|
fb7ef61d06 | ||
|
|
2586645d02 | ||
|
|
e23cb5509d | ||
|
|
5cbd53ae7a | ||
|
|
a4ee590875 | ||
|
|
495608ed7c | ||
|
|
4d10d9a195 | ||
|
|
4914a34dd9 | ||
|
|
ab0fb3131d | ||
|
|
7922d3b3cb | ||
|
|
6427f53b5a | ||
|
|
994df0a3a4 | ||
|
|
e6eee55810 | ||
|
|
855e4c4913 | ||
|
|
c2e07bf7b2 | ||
|
|
77b9e3eac8 | ||
|
|
790487eea6 | ||
|
|
eb83354179 | ||
|
|
9ba8754f97 | ||
|
|
84346119b3 | ||
|
|
2b3abd06db | ||
|
|
8267aad79e | ||
|
|
3f9ce561b9 | ||
|
|
347fc3ed9f | ||
|
|
0488c996e9 | ||
|
|
37bfd97d93 | ||
|
|
c8652b0576 | ||
|
|
1208d2eb5e | ||
|
|
ab34fed0c5 | ||
|
|
0906e5f9cf | ||
|
|
47b1c603b3 | ||
|
|
4bda321e7a | ||
|
|
691ff11fbc |
166
.clang-tidy
166
.clang-tidy
@@ -1,69 +1,97 @@
|
|||||||
# Generated from CLion Inspection settings
|
# All rules should have a comment associated
|
||||||
---
|
# Directives that do not have any effect (e.g. disabling a rule that is not enabled) can be done to add an explanation comment.
|
||||||
Checks: '-*,
|
# Or at least an empty comment # to show they were put here explicitely,
|
||||||
mpi-*,
|
# and not as part of the historical CLion-generated rules
|
||||||
bugprone-*,
|
# Note: `- -X` means disable X
|
||||||
-bugprone-signal-handler,
|
# CLI usage: go to the build directory and run: `run-clang-tidy -allow-no-checks -source-filter ".*/lib/.*" -fix -j`
|
||||||
-bugprone-narrowing-conversions,
|
|
||||||
-bugprone-redundant-branch-condition,
|
Checks:
|
||||||
-bugprone-exception-escape,
|
- -*
|
||||||
-bugprone-shared-ptr-array-mismatch,
|
- mpi-*
|
||||||
-bugprone-implicit-widening-of-multiplication-result,
|
- bugprone-*
|
||||||
-bugprone-signed-char-misuse,
|
- -bugprone-signal-handler
|
||||||
-bugprone-unhandled-exception-at-new,
|
- -bugprone-narrowing-conversions
|
||||||
-bugprone-infinite-loop,
|
- -bugprone-redundant-branch-condition
|
||||||
-bugprone-easily-swappable-parameters,
|
- -bugprone-exception-escape
|
||||||
cert-err52-cpp,
|
- -bugprone-shared-ptr-array-mismatch
|
||||||
cert-err60-cpp,
|
- -bugprone-implicit-widening-of-multiplication-result
|
||||||
cert-err34-c,
|
- -bugprone-signed-char-misuse
|
||||||
cert-str34-c,
|
- -bugprone-unhandled-exception-at-new
|
||||||
cert-dcl21-cpp,
|
- -bugprone-infinite-loop
|
||||||
cert-msc50-cpp,
|
- -bugprone-easily-swappable-parameters
|
||||||
cert-msc51-cpp,
|
- -bugprone-float-loop-counter #
|
||||||
cert-dcl58-cpp,
|
- -bugprone-unchecked-string-to-number-conversion # Unfortunately no alternative
|
||||||
cert-flp30-c,
|
- -bugprone-branch-clone # Mostly warns about one-line duplicates
|
||||||
cppcoreguidelines-avoid-const-or-ref-data-members,
|
- cert-err52-cpp
|
||||||
cppcoreguidelines-pro-type-member-init,
|
- cert-err60-cpp
|
||||||
cppcoreguidelines-slicing,
|
- cert-str34-c
|
||||||
cppcoreguidelines-interfaces-global-init,
|
- cert-dcl21-cpp
|
||||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
- cert-msc50-cpp
|
||||||
cppcoreguidelines-narrowing-conversions,
|
- cert-msc51-cpp
|
||||||
google-default-arguments,
|
- cert-dcl58-cpp
|
||||||
google-runtime-operator,
|
- cppcoreguidelines-avoid-const-or-ref-data-members
|
||||||
google-explicit-constructor,
|
- cppcoreguidelines-pro-type-member-init # We want to use default member initializers
|
||||||
hicpp-multiway-paths-covered,
|
- cppcoreguidelines-slicing
|
||||||
hicpp-exception-baseclass,
|
- cppcoreguidelines-interfaces-global-init
|
||||||
misc-*,
|
- -cppcoreguidelines-pro-type-static-cast-downcast # dynamic_cast has a runtime overhead
|
||||||
-misc-definitions-in-headers,
|
- -cppcoreguidelines-narrowing-conversions #
|
||||||
-misc-unused-parameters,
|
- google-runtime-operator
|
||||||
-misc-unused-alias-decls,
|
- google-explicit-constructor
|
||||||
-misc-use-anonymous-namespace,
|
- -google-default-arguments # Provider and ViewProvider read() is a good example of why this is useful
|
||||||
-misc-misleading-identifier,
|
- hicpp-multiway-paths-covered
|
||||||
-misc-confusable-identifiers,
|
- hicpp-exception-baseclass
|
||||||
-misc-misleading-bidirectional,
|
- misc-*
|
||||||
-misc-static-assert,
|
- -misc-definitions-in-headers
|
||||||
-misc-no-recursion,
|
- -misc-unused-parameters
|
||||||
-misc-const-correctness,
|
- -misc-unused-alias-decls
|
||||||
modernize-*,
|
- -misc-use-anonymous-namespace
|
||||||
-modernize-use-trailing-return-type,
|
- -misc-misleading-identifier
|
||||||
openmp-use-default-none,
|
- -misc-confusable-identifiers
|
||||||
performance-*,
|
- -misc-misleading-bidirectional
|
||||||
-performance-no-int-to-ptr,
|
- -misc-static-assert
|
||||||
portability-*,
|
- -misc-no-recursion
|
||||||
-portability-restrict-system-includes,
|
- -misc-const-correctness
|
||||||
readability-*,
|
- -misc-use-internal-linkage # False positives if header where function is defined is not included
|
||||||
-readability-redundant-preprocessor,
|
- -misc-include-cleaner # Allow indirect includes
|
||||||
-readability-named-parameter,
|
- -misc-non-private-member-variables-in-classes #
|
||||||
-readability-function-size,
|
- modernize-*
|
||||||
-readability-use-anyofallof,
|
- -modernize-use-trailing-return-type
|
||||||
-readability-identifier-length,
|
- -modernize-use-std-print # We want to use fmt::print instead
|
||||||
-readability-magic-numbers,
|
- -modernize-use-integer-sign-comparison # Too much occurrences to change
|
||||||
-readability-braces-around-statements,
|
- openmp-use-default-none
|
||||||
-readability-suspicious-call-argument,
|
- performance-*
|
||||||
-readability-isolate-declaration,
|
- -performance-no-int-to-ptr
|
||||||
-readability-else-after-return,
|
- portability-*
|
||||||
-readability-redundant-access-specifiers,
|
- -portability-restrict-system-includes
|
||||||
-readability-function-cognitive-complexity,
|
- readability-*
|
||||||
-readability-identifier-naming,
|
- -readability-redundant-preprocessor
|
||||||
*-include-cleaner,
|
- -readability-named-parameter
|
||||||
-readability-qualified-auto'
|
- -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
|
||||||
|
- -readability-use-std-min-max # Less readable imo
|
||||||
|
- -readability-math-missing-parentheses # Basic math
|
||||||
|
- -readability-implicit-bool-conversion # Not much of a problem ?
|
||||||
|
- -readability-convert-member-functions-to-static #
|
||||||
|
- -readability-use-concise-preprocessor-directives # We do not use #ifdef
|
||||||
|
- -readability-uppercase-literal-suffix # Not important enough
|
||||||
|
- -readability-redundant-string-cstr # Sometimes used to stop at first null byte
|
||||||
|
- -readability-static-accessed-through-instance #
|
||||||
|
- -readability-ambiguous-smartptr-reset-call # Fix is hard to read
|
||||||
|
|
||||||
|
# Will fix later
|
||||||
|
- -modernize-avoid-c-arrays
|
||||||
|
- -readability-make-member-function-const # idk + lots of occurences
|
||||||
|
- -readability-misleading-indentation # We need to handle cases with #if defined()
|
||||||
|
- -bugprone-unchecked-optional-access
|
||||||
|
- -performance-unnecessary-value-param # idk
|
||||||
|
- -readability-avoid-nested-conditional-operator
|
||||||
|
|||||||
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
# Sponsor links
|
# Sponsor links
|
||||||
|
|
||||||
patreon: werwolv
|
|
||||||
custom: https://werwolv.net/donate
|
|
||||||
github: WerWolv
|
github: WerWolv
|
||||||
|
ko_fi: WerWolv
|
||||||
|
custom: "https://werwolv.net/donate"
|
||||||
|
|||||||
7
.github/scripts/delete-artifact.sh
vendored
Executable file
7
.github/scripts/delete-artifact.sh
vendored
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -xe
|
||||||
|
ARTIFACT_NAME="$1"
|
||||||
|
|
||||||
|
ARTIFACT_ID=$(gh api repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/artifacts --jq ".artifacts[] | select(.name==\"$ARTIFACT_NAME\") | .id")
|
||||||
|
gh api -X DELETE repos/$GITHUB_REPOSITORY/actions/artifacts/$ARTIFACT_ID
|
||||||
|
echo "Deleted artifact $ARTIFACT_NAME with ID $ARTIFACT_ID"
|
||||||
236
.github/workflows/build.yml
vendored
236
.github/workflows/build.yml
vendored
@@ -1,5 +1,9 @@
|
|||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -49,7 +53,7 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: 📜 Setup ccache
|
- name: 📜 Setup ccache
|
||||||
uses: hendrikmuhs/ccache-action@main
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
id: cache-ccache
|
id: cache-ccache
|
||||||
with:
|
with:
|
||||||
key: ${{ runner.os }}-mingw-ccache-${{ github.run_id }}
|
key: ${{ runner.os }}-mingw-ccache-${{ github.run_id }}
|
||||||
@@ -95,7 +99,6 @@ jobs:
|
|||||||
-DIMHEX_GENERATE_PDBS=ON \
|
-DIMHEX_GENERATE_PDBS=ON \
|
||||||
-DIMHEX_REPLACE_DWARF_WITH_PDB=ON \
|
-DIMHEX_REPLACE_DWARF_WITH_PDB=ON \
|
||||||
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \
|
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \
|
||||||
-DCPACK_WIX_VERSION="4" \
|
|
||||||
-DCPACK_WIX_ROOT="$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools" \
|
-DCPACK_WIX_ROOT="$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools" \
|
||||||
..
|
..
|
||||||
|
|
||||||
@@ -106,8 +109,8 @@ jobs:
|
|||||||
|
|
||||||
- name: 🕯️ Install WiX Toolkit
|
- name: 🕯️ Install WiX Toolkit
|
||||||
run: |
|
run: |
|
||||||
"C:/Program Files/dotnet/dotnet.exe" tool install --global wix
|
"C:/Program Files/dotnet/dotnet.exe" tool install --global wix@6.0.2
|
||||||
"$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools/wix" extension add -g WixToolset.UI.wixext
|
"$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools/wix" extension add --global WixToolset.UI.wixext/6.0.2
|
||||||
|
|
||||||
- name: 🪲 Create PDBs for MSI
|
- name: 🪲 Create PDBs for MSI
|
||||||
run: |
|
run: |
|
||||||
@@ -166,6 +169,7 @@ jobs:
|
|||||||
|
|
||||||
- name: ⬆️ Upload Windows Installer
|
- name: ⬆️ Upload Windows Installer
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
id: upload-installer
|
||||||
with:
|
with:
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
name: Windows Installer ${{ matrix.architecture_name }}
|
name: Windows Installer ${{ matrix.architecture_name }}
|
||||||
@@ -233,7 +237,7 @@ jobs:
|
|||||||
arch: ${{ matrix.vs_arch }}
|
arch: ${{ matrix.vs_arch }}
|
||||||
|
|
||||||
- name: 📜 Setup ccache
|
- name: 📜 Setup ccache
|
||||||
uses: hendrikmuhs/ccache-action@main
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
id: cache-ccache
|
id: cache-ccache
|
||||||
with:
|
with:
|
||||||
key: ${{ runner.os }}-msvc-${{ matrix.vs_arch }}-ccache-${{ github.run_id }}
|
key: ${{ runner.os }}-msvc-${{ matrix.vs_arch }}-ccache-${{ github.run_id }}
|
||||||
@@ -242,7 +246,7 @@ jobs:
|
|||||||
|
|
||||||
- name: 📦 Install vcpkg
|
- name: 📦 Install vcpkg
|
||||||
uses: friendlyanon/setup-vcpkg@v1
|
uses: friendlyanon/setup-vcpkg@v1
|
||||||
with: { committish: ef7dbf94b9198bc58f45951adcf1f041fcbc5ea0 }
|
with: { committish: 66c0373dc7fca549e5803087b9487edfe3aca0a1 }
|
||||||
|
|
||||||
- name: ⬇️ Install dependencies
|
- name: ⬇️ Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -279,7 +283,6 @@ jobs:
|
|||||||
-DIMHEX_COMMIT_HASH_LONG="$env:GITHUB_SHA" `
|
-DIMHEX_COMMIT_HASH_LONG="$env:GITHUB_SHA" `
|
||||||
-DIMHEX_COMMIT_BRANCH="$($env:GITHUB_REF -replace '.*/', '')" `
|
-DIMHEX_COMMIT_BRANCH="$($env:GITHUB_REF -replace '.*/', '')" `
|
||||||
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" `
|
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" `
|
||||||
-DCPACK_WIX_VERSION="4" `
|
|
||||||
-DCPACK_WIX_ROOT="$($env:USERPROFILE -replace '\\','/')/.dotnet/tools" `
|
-DCPACK_WIX_ROOT="$($env:USERPROFILE -replace '\\','/')/.dotnet/tools" `
|
||||||
.
|
.
|
||||||
|
|
||||||
@@ -290,8 +293,8 @@ jobs:
|
|||||||
|
|
||||||
- name: 🕯️ Install WiX Toolkit
|
- name: 🕯️ Install WiX Toolkit
|
||||||
run: |
|
run: |
|
||||||
& "C:/Program Files/dotnet/dotnet.exe" tool install --global wix
|
& "C:/Program Files/dotnet/dotnet.exe" tool install --global wix@6.0.2
|
||||||
& "$($env:USERPROFILE -replace '\\','/')/.dotnet/tools/wix" extension add -g WixToolset.UI.wixext
|
& "$($env:USERPROFILE -replace '\\','/')/.dotnet/tools/wix" extension add --global WixToolset.UI.wixext/6.0.2
|
||||||
|
|
||||||
- name: 📦 Bundle MSI
|
- name: 📦 Bundle MSI
|
||||||
run: |
|
run: |
|
||||||
@@ -386,18 +389,7 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
attestations: write
|
attestations: write
|
||||||
|
|
||||||
strategy:
|
name: 🍎 macOS 10.15 x86_64
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- file_suffix: "-NoGPU"
|
|
||||||
name_suffix: "NoGPU"
|
|
||||||
custom_glfw: true
|
|
||||||
- file_suffix: ""
|
|
||||||
name_suffix: ""
|
|
||||||
custom_glfw: false
|
|
||||||
|
|
||||||
name: 🍎 macOS 15 x86_64 ${{ matrix.name_suffix }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 🧰 Checkout
|
- name: 🧰 Checkout
|
||||||
@@ -412,76 +404,55 @@ jobs:
|
|||||||
- name: 📜 Setup ccache
|
- name: 📜 Setup ccache
|
||||||
uses: hendrikmuhs/ccache-action@v1
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
with:
|
with:
|
||||||
key: ${{ runner.os }}${{ matrix.file_suffix }}-ccache-${{ github.run_id }}
|
key: ${{ runner.os }}-ccache-${{ github.run_id }}
|
||||||
restore-keys: ${{ runner.os }}${{ matrix.file_suffix }}-ccache
|
restore-keys: ${{ runner.os }}-ccache
|
||||||
max-size: 1G
|
max-size: 1G
|
||||||
|
|
||||||
- name: Set Xcode version
|
- name: Set Xcode version
|
||||||
run: sudo xcode-select -s /Library/Developer/CommandLineTools
|
run: |
|
||||||
|
sudo xcode-select --install || true
|
||||||
|
sudo xcode-select -s /Library/Developer/CommandLineTools
|
||||||
|
|
||||||
|
- name: 📦 Install MacPorts
|
||||||
|
run: |
|
||||||
|
wget https://github.com/macports/macports-base/releases/download/v2.11.6/MacPorts-2.11.6-15-Sequoia.pkg
|
||||||
|
sudo installer -pkg MacPorts-2.11.6-15-Sequoia.pkg -target /
|
||||||
|
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
|
||||||
|
echo "PATH=/opt/local/bin:/opt/local/sbin:$PATH" >> $GITHUB_ENV
|
||||||
|
echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV
|
||||||
|
echo "universal_target 10.15" | sudo tee -a /opt/local/etc/macports/macports.conf
|
||||||
|
echo "macos_deployment_target 10.15" | sudo tee -a /opt/local/etc/macports/macports.conf
|
||||||
|
echo "macosx_sdk_version 10.15" | sudo tee -a /opt/local/etc/macports/macports.conf
|
||||||
|
sudo port selfupdate
|
||||||
|
|
||||||
- name: ⬇️ Install dependencies
|
- name: ⬇️ Install dependencies
|
||||||
env:
|
env:
|
||||||
# Make brew not display useless errors
|
# Make brew not display useless errors
|
||||||
HOMEBREW_TESTS: 1
|
HOMEBREW_TESTS: 1
|
||||||
run: |
|
run: |
|
||||||
brew reinstall python --quiet || true
|
brew install llvm@21 automake
|
||||||
brew link --overwrite --quiet python 2>/dev/null || true
|
sudo -E port install mbedtls3 nlohmann-json ccache freetype libmagic pkgconfig curl glfw ninja zlib xz bzip2 zstd libssh2 md4c
|
||||||
brew bundle --quiet --file dist/macOS/Brewfile || true
|
|
||||||
rm -rf /usr/local/Cellar/capstone
|
|
||||||
|
|
||||||
- name: ⬇️ Install classic glfw
|
|
||||||
if: ${{! matrix.custom_glfw }}
|
|
||||||
run: |
|
|
||||||
brew install --quiet glfw || true
|
|
||||||
|
|
||||||
- name: ⬇️ Install .NET
|
- name: ⬇️ Install .NET
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.100'
|
dotnet-version: '8.0.100'
|
||||||
|
|
||||||
- name: 🧰 Checkout glfw
|
|
||||||
if: ${{ matrix.custom_glfw }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: glfw/glfw
|
|
||||||
path: glfw
|
|
||||||
|
|
||||||
# GLFW custom build (to allow software rendering)
|
|
||||||
- name: ⬇️ Patch and install custom glfw
|
|
||||||
if: ${{ matrix.custom_glfw }}
|
|
||||||
run: |
|
|
||||||
set -x
|
|
||||||
cd glfw
|
|
||||||
git apply ../dist/macOS/0001-glfw-SW.patch
|
|
||||||
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
|
|
||||||
cmake -G "Ninja" \
|
|
||||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
|
||||||
-DBUILD_SHARED_LIBS=ON \
|
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
|
||||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
|
||||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
|
||||||
..
|
|
||||||
ninja install
|
|
||||||
|
|
||||||
# MacOS cmake build
|
# MacOS cmake build
|
||||||
- name: 🛠️ Configure CMake
|
- name: 🛠️ Configure CMake
|
||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
CC=$(brew --prefix llvm)/bin/clang \
|
CC=$(brew --prefix llvm@21)/bin/clang \
|
||||||
CXX=$(brew --prefix llvm)/bin/clang++ \
|
CXX=$(brew --prefix llvm@21)/bin/clang++ \
|
||||||
OBJC=$(brew --prefix llvm)/bin/clang \
|
OBJC=$(brew --prefix llvm@21)/bin/clang \
|
||||||
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
|
OBJCXX=$(brew --prefix llvm@21)/bin/clang++ \
|
||||||
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \
|
|
||||||
cmake -G "Ninja" \
|
cmake -G "Ninja" \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \
|
||||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
||||||
-DIMHEX_GENERATE_PACKAGE=ON \
|
-DIMHEX_GENERATE_PACKAGE=ON \
|
||||||
-DIMHEX_SYSTEM_LIBRARY_PATH="$(brew --prefix llvm)/lib;$(brew --prefix llvm)/lib/unwind;$(brew --prefix llvm)/lib/c++;$(brew --prefix)/lib" \
|
-DIMHEX_SYSTEM_LIBRARY_PATH="$(brew --prefix llvm@21)/lib;$(brew --prefix llvm@21)/lib/unwind;$(brew --prefix llvm@21)/lib/c++;$(brew --prefix)/lib" \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||||
@@ -499,7 +470,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
cd build/install
|
cd build/install
|
||||||
mv imhex.app ImHex.app
|
|
||||||
codesign --remove-signature ImHex.app
|
codesign --remove-signature ImHex.app
|
||||||
codesign --force --deep --sign - ImHex.app
|
codesign --force --deep --sign - ImHex.app
|
||||||
|
|
||||||
@@ -528,7 +498,7 @@ jobs:
|
|||||||
break;
|
break;
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
mv *.dmg ../../imhex-${{ env.IMHEX_VERSION }}-macOS${{ matrix.file_suffix }}-x86_64.dmg
|
mv *.dmg ../../imhex-${{ env.IMHEX_VERSION }}-macOS-x86_64.dmg
|
||||||
|
|
||||||
- name: 🗝️ Generate build provenance attestations
|
- name: 🗝️ Generate build provenance attestations
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v2
|
||||||
@@ -541,12 +511,12 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
name: macOS DMG ${{ matrix.name_suffix }} x86_64
|
name: macOS DMG x86_64
|
||||||
path: ./*.dmg
|
path: ./*.dmg
|
||||||
|
|
||||||
macos-arm64:
|
macos-arm64:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
name: 🍎 macOS 15 arm64
|
name: 🍎 macOS 11 arm64
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }}
|
IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }}
|
||||||
@@ -594,7 +564,7 @@ jobs:
|
|||||||
|
|
||||||
macos-arm64-package:
|
macos-arm64-package:
|
||||||
runs-on: macos-15-intel
|
runs-on: macos-15-intel
|
||||||
name: 🍎 macOS 15 arm64 Packaging
|
name: 🍎 macOS 11 arm64 Packaging
|
||||||
needs: macos-arm64
|
needs: macos-arm64
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -620,7 +590,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
cd out
|
cd out
|
||||||
mv imhex.app ImHex.app
|
|
||||||
codesign --remove-signature ImHex.app
|
codesign --remove-signature ImHex.app
|
||||||
codesign --force --deep --entitlements Entitlements.plist --sign - ImHex.app
|
codesign --force --deep --entitlements Entitlements.plist --sign - ImHex.app
|
||||||
|
|
||||||
@@ -671,14 +640,21 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- release_num: "24.04"
|
- name: "Ubuntu"
|
||||||
- release_num: "25.04"
|
release_num: "24.04"
|
||||||
|
image: "ubuntu:24.04"
|
||||||
|
- name: "Ubuntu"
|
||||||
|
release_num: "25.10"
|
||||||
|
image: "ubuntu:25.10"
|
||||||
|
- name: "Debian"
|
||||||
|
release_num: "13"
|
||||||
|
image: "debian:13"
|
||||||
|
|
||||||
name: 🐧 Ubuntu ${{ matrix.release_num }}
|
name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }} x86_64
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
container:
|
container:
|
||||||
image: "ubuntu:${{ matrix.release_num }}"
|
image: "${{ matrix.image }}"
|
||||||
options: --privileged
|
options: --privileged
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -697,8 +673,8 @@ jobs:
|
|||||||
- name: 📜 Setup ccache
|
- name: 📜 Setup ccache
|
||||||
uses: hendrikmuhs/ccache-action@v1
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
with:
|
with:
|
||||||
key: Ubuntu-${{ matrix.release_num }}-ccache-${{ github.run_id }}
|
key: ${{ matrix.image }}-ccache-${{ github.run_id }}
|
||||||
restore-keys: Ubuntu-${{ matrix.release_num }}-ccache
|
restore-keys: ${{ matrix.image }}-ccache
|
||||||
max-size: 1G
|
max-size: 1G
|
||||||
|
|
||||||
- name: ⬇️ Install dependencies
|
- name: ⬇️ Install dependencies
|
||||||
@@ -743,7 +719,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp -r build/DEBIAN build/DebDir
|
cp -r build/DEBIAN build/DebDir
|
||||||
dpkg-deb -Zzstd --build build/DebDir
|
dpkg-deb -Zzstd --build build/DebDir
|
||||||
mv build/DebDir.deb imhex-${{ env.IMHEX_VERSION }}-Ubuntu-${{ matrix.release_num }}-x86_64.deb
|
mv build/DebDir.deb imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.deb
|
||||||
|
|
||||||
- name: 🗝️ Generate build provenance attestations
|
- name: 🗝️ Generate build provenance attestations
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v2
|
||||||
@@ -756,7 +732,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
name: Ubuntu ${{ matrix.release_num }} DEB x86_64
|
name: ${{ matrix.name }} ${{ matrix.release_num }} DEB x86_64
|
||||||
path: '*.deb'
|
path: '*.deb'
|
||||||
|
|
||||||
# AppImage build
|
# AppImage build
|
||||||
@@ -831,7 +807,7 @@ jobs:
|
|||||||
|
|
||||||
# ArchLinux build
|
# ArchLinux build
|
||||||
archlinux-build:
|
archlinux-build:
|
||||||
name: 🐧 ArchLinux
|
name: 🐧 ArchLinux x86_64
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
container:
|
container:
|
||||||
@@ -946,23 +922,11 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: Fedora
|
|
||||||
release_num: rawhide
|
|
||||||
mock_config: fedora-rawhide
|
|
||||||
- name: Fedora
|
- name: Fedora
|
||||||
release_num: 43
|
release_num: 43
|
||||||
mock_config: fedora-43
|
mock_config: fedora-43
|
||||||
- name: Fedora
|
|
||||||
release_num: 42
|
|
||||||
mock_config: fedora-42
|
|
||||||
- name: Fedora
|
|
||||||
release_num: 41
|
|
||||||
mock_config: fedora-41
|
|
||||||
- name: RHEL-AlmaLinux
|
|
||||||
release_num: 9
|
|
||||||
mock_config: "alma+epel-9"
|
|
||||||
|
|
||||||
name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }}
|
name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }} x86_64
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
container:
|
container:
|
||||||
@@ -1225,10 +1189,6 @@ jobs:
|
|||||||
webassembly-build:
|
webassembly-build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
name: 🌍 Web
|
name: 🌍 Web
|
||||||
permissions:
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
actions: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: 🧰 Checkout
|
- name: 🧰 Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -1248,17 +1208,20 @@ jobs:
|
|||||||
cache-source: cache
|
cache-source: cache
|
||||||
cache-target: /cache
|
cache-target: /cache
|
||||||
|
|
||||||
- name: 🛠️ Build using docker
|
- name: 🔨 Copy necessary files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p out/nightly
|
mkdir -p out/nightly
|
||||||
|
cp dist/web/serve.py out/nightly/start_imhex_web.py
|
||||||
|
|
||||||
|
- name: 🛠️ Build using docker
|
||||||
|
run: |
|
||||||
docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out/nightly --target raw
|
docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out/nightly --target raw
|
||||||
|
|
||||||
- name: ⬇️ Download Release
|
- name: ⬇️ Download Release artifact
|
||||||
if: ${{ github.event.repository.fork == false }}
|
if: ${{ github.event.repository.fork == false }}
|
||||||
uses: robinraju/release-downloader@v1
|
env:
|
||||||
with:
|
GH_TOKEN: ${{ github.token }}
|
||||||
latest: true
|
run: gh --repo $GITHUB_REPOSITORY release download --pattern "imhex-*-Web.zip"
|
||||||
fileName: 'imhex-*-Web.zip'
|
|
||||||
|
|
||||||
- name: 🔨 Fix permissions
|
- name: 🔨 Fix permissions
|
||||||
if: ${{ github.event.repository.fork == false }}
|
if: ${{ github.event.repository.fork == false }}
|
||||||
@@ -1271,10 +1234,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: out/
|
path: out/
|
||||||
|
|
||||||
- name: 🔨 Copy necessary files
|
|
||||||
run: |
|
|
||||||
cp dist/web/serve.py out/nightly/start_imhex_web.py
|
|
||||||
|
|
||||||
- name: ⬆️ Upload package
|
- name: ⬆️ Upload package
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -1293,7 +1252,7 @@ jobs:
|
|||||||
|
|
||||||
webassembly-deploy:
|
webassembly-deploy:
|
||||||
environment:
|
environment:
|
||||||
name: github-pages
|
name: ImHex Web
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
permissions:
|
permissions:
|
||||||
pages: write
|
pages: write
|
||||||
@@ -1306,11 +1265,64 @@ jobs:
|
|||||||
needs: webassembly-build
|
needs: webassembly-build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: 🧰 Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: 🌍 Deploy WebAssembly Build to GitHub Pages
|
- name: 🌍 Deploy WebAssembly Build to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
|
|
||||||
- name: 🗑️ Delete artifact
|
- name: 🗑️ Delete artifact
|
||||||
uses: geekyeggo/delete-artifact@v5
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
.github/scripts/delete-artifact.sh "github-pages"
|
||||||
|
|
||||||
|
webassembly-docker-image-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
needs: webassembly-build
|
||||||
|
name: 🐋 Deploy to ghcr.io
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 🧰 Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: ⬇️ Download artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: github-pages
|
name: ImHex Web
|
||||||
|
path: out
|
||||||
|
|
||||||
|
- name: 📜 Login to ghcr.io
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: ⛓️ Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ghcr.io/${{ github.repository }}/imhex-web
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=sha
|
||||||
|
|
||||||
|
- name: 🔨 Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
env:
|
||||||
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: dist/web/Host.Dockerfile
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
47
.github/workflows/dl-cache.yml
vendored
Normal file
47
.github/workflows/dl-cache.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# https://gist.github.com/iTrooz/d5bacca32c0974edc6c1ac3ad3ee82f3
|
||||||
|
# See https://github.com/cli/cli/issues/9125
|
||||||
|
# Extract archive with `tar -xf cache.tzst --transform 's@\.\./@#@g' -P` to avoid ../ errors
|
||||||
|
name: Download cache key
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
cache_key:
|
||||||
|
description: 'Cache key'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
jobs:
|
||||||
|
cache-download:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Query cache version
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
VERSION=$(gh api repos/$GITHUB_REPOSITORY/actions/caches \
|
||||||
|
--jq "
|
||||||
|
.actions_caches[]
|
||||||
|
| select(.ref == \"refs/heads/$GITHUB_REF_NAME\")
|
||||||
|
| select(.key == \"${{ github.event.inputs.cache_key }}\")
|
||||||
|
| .version
|
||||||
|
")
|
||||||
|
echo "version=$VERSION" | tee $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Restore cache
|
||||||
|
uses: iTrooz/cache/restore@restore_with_version
|
||||||
|
with:
|
||||||
|
# Path won't be actually used, we will match by 'version'.
|
||||||
|
path: .
|
||||||
|
key: ${{ github.event.inputs.cache_key }}
|
||||||
|
version: ${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Upload cached folder as artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: cache-artifact
|
||||||
|
path: |
|
||||||
|
/home/runner/work/**/*.tzst
|
||||||
22
.github/workflows/nightly_release.yml
vendored
22
.github/workflows/nightly_release.yml
vendored
@@ -37,13 +37,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
project_version=`cat ImHex/VERSION`
|
project_version=`cat ImHex/VERSION`
|
||||||
echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV
|
echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# TODO: Replace by Github CLI when github.com/cli/cli/pull/12435 is closed
|
||||||
- name: ⬇️ Download artifacts from latest workflow
|
- name: ⬇️ Download artifacts from latest workflow
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v6
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
branch: ${{ github.event.release.target_commitish }}
|
branch: master
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
skip_unpack: true
|
skip_unpack: true
|
||||||
|
|
||||||
@@ -125,4 +126,19 @@ jobs:
|
|||||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||||
with:
|
with:
|
||||||
snap: imhex-${{ env.IMHEX_VERSION }}-arm64.snap
|
snap: imhex-${{ env.IMHEX_VERSION }}-arm64.snap
|
||||||
release: edge
|
release: edge
|
||||||
|
|
||||||
|
website_update:
|
||||||
|
name: 🌍 Update ImHex Landing Website
|
||||||
|
needs: nightly-release
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
env:
|
||||||
|
WEBSITE_DISPATCH_TOKEN: ${{ secrets.WEBSITE_DISPATCH_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: ✉️ Dispatch Landing page update
|
||||||
|
if: ${{ env.WEBSITE_DISPATCH_TOKEN != '' }}
|
||||||
|
uses: peter-evans/repository-dispatch@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.WEBSITE_DISPATCH_TOKEN }}
|
||||||
|
repository: WerWolv/ImHexWebsite
|
||||||
|
event-type: update_page
|
||||||
102
.github/workflows/release.yml
vendored
102
.github/workflows/release.yml
vendored
@@ -7,6 +7,12 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
commit_hash:
|
||||||
|
type: string
|
||||||
|
description: 'The commit hash to build (defaults to the latest commit on the default branch)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release-update-repos:
|
release-update-repos:
|
||||||
@@ -25,7 +31,7 @@ jobs:
|
|||||||
project_version=`cat ImHex/VERSION`
|
project_version=`cat ImHex/VERSION`
|
||||||
tag_version="${{github.event.release.tag_name}}"
|
tag_version="${{github.event.release.tag_name}}"
|
||||||
tag_version="${tag_version:1}"
|
tag_version="${tag_version:1}"
|
||||||
if [ "$project_version" != "$tag_version" ]; then
|
if [ "$project_version" != "$tag_version" ] && [ ! -z "$tag_version" ]; then
|
||||||
echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release"
|
echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -41,6 +47,7 @@ jobs:
|
|||||||
tag: ImHex-v${{ env.IMHEX_VERSION }}
|
tag: ImHex-v${{ env.IMHEX_VERSION }}
|
||||||
repo: PatternLanguage
|
repo: PatternLanguage
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
skipIfReleaseExists: true
|
||||||
|
|
||||||
- name: 🎫 Create ImHex-Patterns release
|
- name: 🎫 Create ImHex-Patterns release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
@@ -51,6 +58,7 @@ jobs:
|
|||||||
tag: ImHex-v${{ env.IMHEX_VERSION }}
|
tag: ImHex-v${{ env.IMHEX_VERSION }}
|
||||||
repo: ImHex-Patterns
|
repo: ImHex-Patterns
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
skipIfReleaseExists: true
|
||||||
|
|
||||||
- name: 🎫 Create imhex-download-sdk release
|
- name: 🎫 Create imhex-download-sdk release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
@@ -61,11 +69,13 @@ jobs:
|
|||||||
tag: v${{ env.IMHEX_VERSION }}
|
tag: v${{ env.IMHEX_VERSION }}
|
||||||
repo: imhex-download-sdk
|
repo: imhex-download-sdk
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
skipIfReleaseExists: true
|
||||||
|
|
||||||
release-upload-artifacts:
|
release-upload-artifacts:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
name: Release Upload Artifacts
|
name: Release Upload Artifacts
|
||||||
|
outputs:
|
||||||
|
IMHEX_VERSION: ${{ steps.verify_version.outputs.IMHEX_VERSION }}
|
||||||
steps:
|
steps:
|
||||||
- name: 🧰 Checkout
|
- name: 🧰 Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -74,17 +84,19 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: 📜 Verify version and set version variable
|
- name: 📜 Verify version and set version variable
|
||||||
|
id: verify_version
|
||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
project_version=`cat ImHex/VERSION`
|
project_version=`cat ImHex/VERSION`
|
||||||
tag_version="${{github.event.release.tag_name}}"
|
tag_version="${{github.event.release.tag_name}}"
|
||||||
tag_version="${tag_version:1}"
|
tag_version="${tag_version:1}"
|
||||||
if [ "$project_version" != "$tag_version" ]; then
|
if [ "$project_version" != "$tag_version" ] && [ ! -z "$tag_version" ]; then
|
||||||
echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release"
|
echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV
|
echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV
|
||||||
|
echo "IMHEX_VERSION=$project_version" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: 🗜️ Create tarball from sources with dependencies
|
- name: 🗜️ Create tarball from sources with dependencies
|
||||||
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
|
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
|
||||||
@@ -97,6 +109,7 @@ jobs:
|
|||||||
branch: ${{ github.event.release.target_commitish }}
|
branch: ${{ github.event.release.target_commitish }}
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
skip_unpack: true
|
skip_unpack: true
|
||||||
|
commit: ${{ github.event.inputs.commit_hash }}
|
||||||
|
|
||||||
- name: 🗜️ Unzip files when needed
|
- name: 🗜️ Unzip files when needed
|
||||||
run: |
|
run: |
|
||||||
@@ -115,25 +128,87 @@ jobs:
|
|||||||
|
|
||||||
- name: 🟩 Rename artifacts when needed
|
- name: 🟩 Rename artifacts when needed
|
||||||
run: |
|
run: |
|
||||||
mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip
|
mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip || true
|
||||||
mv "Windows Portable arm64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip
|
mv "Windows Portable arm64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip || true
|
||||||
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
|
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip || true
|
||||||
mv "ImHex Web.zip" imhex-${{ env.IMHEX_VERSION }}-Web.zip
|
mv "ImHex Web.zip" imhex-${{ env.IMHEX_VERSION }}-Web.zip || true
|
||||||
rm artifact.tar || true
|
rm artifact.tar || true
|
||||||
|
|
||||||
|
- name: ⬆️ Upload Unsigned x86_64 Windows Installer
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
id: upload-installer-x86_64
|
||||||
|
with:
|
||||||
|
if-no-files-found: error
|
||||||
|
name: Windows Installer x86_64
|
||||||
|
path: |
|
||||||
|
imhex-*-x86_64.msi
|
||||||
|
|
||||||
|
- name: ⬆️ Upload Unsigned ARM64 Windows Installer
|
||||||
|
if: false
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
id: upload-installer-arm64
|
||||||
|
with:
|
||||||
|
if-no-files-found: error
|
||||||
|
name: Windows Installer ARM64
|
||||||
|
path: |
|
||||||
|
imhex-*-arm64.msi
|
||||||
|
|
||||||
|
- name: 🗑️ Delete unsigned installers
|
||||||
|
run: |
|
||||||
|
rm imhex-*-x86_64.msi
|
||||||
|
|
||||||
|
- name: 🗝️ Sign x86_64 Installer
|
||||||
|
uses: signpath/github-action-submit-signing-request@v1
|
||||||
|
with:
|
||||||
|
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||||
|
organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33'
|
||||||
|
project-slug: 'ImHex'
|
||||||
|
signing-policy-slug: 'release-signing'
|
||||||
|
github-artifact-id: '${{ steps.upload-installer-x86_64.outputs.artifact-id }}'
|
||||||
|
wait-for-completion: true
|
||||||
|
output-artifact-directory: '.'
|
||||||
|
|
||||||
|
- name: 🗝️ Sign ARM64 Installer
|
||||||
|
if: false
|
||||||
|
uses: signpath/github-action-submit-signing-request@v1
|
||||||
|
with:
|
||||||
|
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||||
|
organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33'
|
||||||
|
project-slug: 'ImHex'
|
||||||
|
signing-policy-slug: 'release-signing'
|
||||||
|
github-artifact-id: '${{ steps.upload-installer-arm64.outputs.artifact-id }}'
|
||||||
|
wait-for-completion: true
|
||||||
|
output-artifact-directory: '.'
|
||||||
|
|
||||||
- name: ⬆️ Upload everything to release
|
- name: ⬆️ Upload everything to release
|
||||||
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
|
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
|
||||||
with:
|
with:
|
||||||
files: '*'
|
files: '*'
|
||||||
|
|
||||||
|
release-update-aur:
|
||||||
|
name: Release update AUR package
|
||||||
|
needs: release-upload-artifacts
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: 🧰 Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: ImHex
|
||||||
|
|
||||||
|
- name: ⬇️ Download artifacts
|
||||||
|
run: |
|
||||||
|
tagname=${GITHUB_REF#refs/tags/}
|
||||||
|
version=${tagname#v}
|
||||||
|
wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-ArchLinux-x86_64.pkg.tar.zst
|
||||||
|
|
||||||
- name: ✒️ Prepare PKGBUILD
|
- name: ✒️ Prepare PKGBUILD
|
||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
cp ImHex/dist/Arch/PKGBUILD .
|
cp ImHex/dist/Arch/PKGBUILD .
|
||||||
|
|
||||||
hash=`md5sum imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst | cut -d ' ' -f 1`
|
hash=`md5sum imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst | cut -d ' ' -f 1`
|
||||||
|
|
||||||
sed -i 's/%version%/${{ env.IMHEX_VERSION }}/g' PKGBUILD
|
sed -i 's/%version%/${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}/g' PKGBUILD
|
||||||
sed -i "s/(SKIP)/($hash)/g" PKGBUILD
|
sed -i "s/(SKIP)/($hash)/g" PKGBUILD
|
||||||
|
|
||||||
- name: ⬆️ Publish AUR package
|
- name: ⬆️ Publish AUR package
|
||||||
@@ -147,9 +222,9 @@ jobs:
|
|||||||
pkgname: imhex-bin
|
pkgname: imhex-bin
|
||||||
pkgbuild: ./PKGBUILD
|
pkgbuild: ./PKGBUILD
|
||||||
commit_username: iTrooz
|
commit_username: iTrooz
|
||||||
commit_email: itrooz@protonmail.com
|
commit_email: hey@itrooz.fr
|
||||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||||
commit_message: Bump to version ${{ env.IMHEX_VERSION }}
|
commit_message: Bump to version ${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}
|
||||||
ssh_keyscan_types: rsa,ecdsa,ed25519
|
ssh_keyscan_types: rsa,ecdsa,ed25519
|
||||||
|
|
||||||
release-update-winget:
|
release-update-winget:
|
||||||
@@ -161,6 +236,7 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||||
|
|
||||||
- name: ⬆️ Update winget manifest
|
- name: ⬆️ Update winget manifest
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
env:
|
env:
|
||||||
@@ -193,7 +269,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||||
with:
|
with:
|
||||||
snap: imhex-${{ env.IMHEX_VERSION }}-x86_64.snap
|
snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-x86_64.snap
|
||||||
release: stable
|
release: stable
|
||||||
|
|
||||||
- name: ⬆️ Publish arm64 Snap package
|
- name: ⬆️ Publish arm64 Snap package
|
||||||
@@ -202,5 +278,5 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||||
with:
|
with:
|
||||||
snap: imhex-${{ env.IMHEX_VERSION }}-arm64.snap
|
snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-arm64.snap
|
||||||
release: stable
|
release: stable
|
||||||
@@ -1,33 +1,39 @@
|
|||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
option(IMHEX_PLUGINS_IN_SHARE "Put the plugins in share/imhex/plugins instead of lib[..]/imhex/plugins (Linux only)" OFF)
|
## General
|
||||||
option(IMHEX_STRIP_RELEASE "Strip the release builds" ON )
|
option(IMHEX_STRIP_RELEASE "Strip the release builds" ON )
|
||||||
option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF)
|
option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF)
|
||||||
option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF)
|
option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF)
|
||||||
option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF)
|
option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF)
|
||||||
option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF)
|
option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF)
|
||||||
option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals (Linux only)" OFF)
|
option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals (Linux only)" OFF)
|
||||||
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
|
|
||||||
option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON )
|
option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON )
|
||||||
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF)
|
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF)
|
||||||
option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF)
|
option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF)
|
||||||
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON )
|
|
||||||
option(IMHEX_BUILD_HARDENING "Enable hardening flags for build" ON )
|
option(IMHEX_BUILD_HARDENING "Enable hardening flags for build" ON )
|
||||||
option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
|
option(IMHEX_GENERATE_PACKAGE "Specify if a cpack package should be built. (Windows only)" OFF)
|
||||||
option(IMHEX_GENERATE_PACKAGE "Specify if a native package should be built. (Windows and MacOS only)" OFF)
|
option(IMHEX_MACOS_CREATE_BUNDLE "Creates a macOS .app bundle when building (macOS only)" ON )
|
||||||
option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF)
|
option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF)
|
||||||
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
|
|
||||||
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
|
|
||||||
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
|
|
||||||
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" ON )
|
|
||||||
option(IMHEX_ENABLE_PLUGIN_TESTS "Enable building plugin tests" ON )
|
|
||||||
option(IMHEX_ENABLE_IMGUI_TEST_ENGINE "Enable the ImGui Test Engine" OFF)
|
|
||||||
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
|
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
|
||||||
option(IMHEX_COMPRESS_DEBUG_INFO "Compress debug information" ON )
|
|
||||||
option(IMHEX_ENABLE_CXX_MODULES "Enable C++20 Module compilation. Testing only!" OFF)
|
option(IMHEX_ENABLE_CXX_MODULES "Enable C++20 Module compilation. Testing only!" OFF)
|
||||||
option(IMHEX_ENABLE_CPPCHECK "Enable cppcheck static analysis" OFF)
|
option(IMHEX_ENABLE_CPPCHECK "Enable cppcheck static analysis" OFF)
|
||||||
option(IMHEX_BUNDLE_PLUGIN_SDK "Enable bundling of Plugin SDK into install package" ON )
|
option(IMHEX_BUNDLE_PLUGIN_SDK "Enable bundling of Plugin SDK into install package" ON )
|
||||||
|
## Testing
|
||||||
|
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" ON )
|
||||||
|
option(IMHEX_ENABLE_IMGUI_TEST_ENGINE "Enable the ImGui Test Engine" OFF)
|
||||||
|
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
|
||||||
|
## Debug info
|
||||||
|
option(IMHEX_COMPRESS_DEBUG_INFO "Compress debug information" ON )
|
||||||
|
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
|
||||||
|
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
|
||||||
|
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON )
|
||||||
|
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
|
||||||
|
## Plugins
|
||||||
|
option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
|
||||||
|
option(IMHEX_ENABLE_PLUGIN_TESTS "Enable building plugin tests" ON )
|
||||||
|
option(IMHEX_INCLUDE_PLUGINS "Semicolon-separated list of plugins to include in the build (empty = build all)" "" )
|
||||||
|
option(IMHEX_EXCLUDE_PLUGINS "Semicolon-separated list of plugins to exclude from the build" "" )
|
||||||
|
|
||||||
set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}")
|
set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
|
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
|
||||||
|
|||||||
@@ -29,11 +29,11 @@
|
|||||||
|
|
||||||
## Supporting
|
## Supporting
|
||||||
|
|
||||||
If you like my work, please consider supporting me on GitHub Sponsors, Patreon or PayPal. Thanks a lot!
|
If you like my work, please consider supporting me on GitHub Sponsors, Ko-Fi or PayPal. Thanks a lot!
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/sponsors/WerWolv"><img src="https://werwolv.net/assets/github_banner.png" alt="GitHub donate button" /></a>
|
<a href="https://github.com/sponsors/WerWolv"><img src="https://werwolv.net/assets/github_banner.png" alt="GitHub donate button" /></a>
|
||||||
<a href="https://www.patreon.com/werwolv"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Patreon donate button" /></a>
|
<a href="https://ko-fi.com/WerWolv"><img src="https://werwolv.net/assets/kofi_banner.png" alt="Ko-Fi donate button" /></a>
|
||||||
<a href="https://werwolv.net/donate"><img src="https://werwolv.net/assets/paypal_banner.png" alt="PayPal donate button" /></a>
|
<a href="https://werwolv.net/donate"><img src="https://werwolv.net/assets/paypal_banner.png" alt="PayPal donate button" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -175,15 +175,11 @@ macro(detectOS)
|
|||||||
endif()
|
endif()
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
if(IMHEX_PLUGINS_IN_SHARE)
|
set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins")
|
||||||
set(PLUGINS_INSTALL_LOCATION "share/imhex/plugins")
|
|
||||||
else()
|
|
||||||
set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins")
|
|
||||||
|
|
||||||
# Add System plugin location for plugins to be loaded from
|
# Add System plugin location for plugins to be loaded from
|
||||||
# IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release
|
# IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release
|
||||||
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex")
|
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex")
|
||||||
endif()
|
|
||||||
|
|
||||||
else ()
|
else ()
|
||||||
message(FATAL_ERROR "Unknown / unsupported system!")
|
message(FATAL_ERROR "Unknown / unsupported system!")
|
||||||
@@ -205,11 +201,18 @@ macro(configurePackingResources)
|
|||||||
set(CPACK_GENERATOR "WIX")
|
set(CPACK_GENERATOR "WIX")
|
||||||
set(CPACK_PACKAGE_NAME "ImHex")
|
set(CPACK_PACKAGE_NAME "ImHex")
|
||||||
set(CPACK_PACKAGE_VENDOR "WerWolv")
|
set(CPACK_PACKAGE_VENDOR "WerWolv")
|
||||||
|
set(CPACK_WIX_VERSION 4)
|
||||||
|
set(CPACK_WIX_PRODUCT_GUID "*")
|
||||||
set(CPACK_WIX_UPGRADE_GUID "05000E99-9659-42FD-A1CF-05C554B39285")
|
set(CPACK_WIX_UPGRADE_GUID "05000E99-9659-42FD-A1CF-05C554B39285")
|
||||||
set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/dist/windows/icon.ico")
|
set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/dist/windows/icon.ico")
|
||||||
set(CPACK_WIX_UI_BANNER "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_banner.png")
|
set(CPACK_WIX_UI_BANNER "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_banner.png")
|
||||||
set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_dialog.png")
|
set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_dialog.png")
|
||||||
set(CPACK_WIX_CULTURES "en-US;de-DE;ja-JP;it-IT;pt-BR;zh-CN;zh-TW;ru-RU")
|
set(CPACK_WIX_CULTURES "en-US;de-DE;ja-JP;it-IT;pt-BR;zh-CN;zh-TW;ru-RU")
|
||||||
|
set(CPACK_WIX_TEMPLATE "${PROJECT_SOURCE_DIR}/resources/dist/windows/WIX.template.in")
|
||||||
|
set(CPACK_WIX_EXTENSIONS "WixToolset.UI.wixext")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE CPACK_WIX_EXTRA_SOURCES "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix/*.wxs")
|
||||||
|
|
||||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "ImHex")
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY "ImHex")
|
||||||
set_property(INSTALL "$<TARGET_FILE_NAME:main>"
|
set_property(INSTALL "$<TARGET_FILE_NAME:main>"
|
||||||
PROPERTY CPACK_START_MENU_SHORTCUTS "ImHex"
|
PROPERTY CPACK_START_MENU_SHORTCUTS "ImHex"
|
||||||
@@ -218,9 +221,9 @@ macro(configurePackingResources)
|
|||||||
endif()
|
endif()
|
||||||
elseif (APPLE OR ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin")
|
elseif (APPLE OR ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns")
|
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns")
|
||||||
set(BUNDLE_NAME "imhex.app")
|
set(BUNDLE_NAME "ImHex.app")
|
||||||
|
|
||||||
if (IMHEX_GENERATE_PACKAGE)
|
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||||
set(APPLICATION_TYPE MACOSX_BUNDLE)
|
set(APPLICATION_TYPE MACOSX_BUNDLE)
|
||||||
set_source_files_properties(${IMHEX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
set_source_files_properties(${IMHEX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||||
set(MACOSX_BUNDLE_ICON_FILE "AppIcon.icns")
|
set(MACOSX_BUNDLE_ICON_FILE "AppIcon.icns")
|
||||||
@@ -236,9 +239,9 @@ macro(configurePackingResources)
|
|||||||
string(TIMESTAMP CURR_YEAR "%Y")
|
string(TIMESTAMP CURR_YEAR "%Y")
|
||||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 - ${CURR_YEAR} WerWolv. All rights reserved." )
|
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 - ${CURR_YEAR} WerWolv. All rights reserved." )
|
||||||
if ("${CMAKE_GENERATOR}" STREQUAL "Xcode")
|
if ("${CMAKE_GENERATOR}" STREQUAL "Xcode")
|
||||||
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${BUNDLE_NAME}")
|
set(IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${BUNDLE_NAME}")
|
||||||
else ()
|
else ()
|
||||||
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}")
|
set(IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(PLUGINS_INSTALL_LOCATION "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
|
set(PLUGINS_INSTALL_LOCATION "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
|
||||||
@@ -256,7 +259,7 @@ macro(addPluginDirectories)
|
|||||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins")
|
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins")
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
if (IMHEX_GENERATE_PACKAGE)
|
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION})
|
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION})
|
||||||
endif ()
|
endif ()
|
||||||
else ()
|
else ()
|
||||||
@@ -312,7 +315,7 @@ macro(createPackage)
|
|||||||
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
|
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(_c_deps_FILENAMES AND NOT _c_deps STREQUAL "")
|
if(_c_deps_FILENAMES AND _c_deps AND NOT (_c_deps STREQUAL ""))
|
||||||
message(WARNING "Conflicting dependencies for library: \"${_c_deps}\"!")
|
message(WARNING "Conflicting dependencies for library: \"${_c_deps}\"!")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -348,24 +351,21 @@ macro(createPackage)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
if (IMHEX_GENERATE_PACKAGE)
|
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||||
set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS} "${IMHEX_SYSTEM_LIBRARY_PATH}")
|
set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS} "${IMHEX_SYSTEM_LIBRARY_PATH}")
|
||||||
include(PostprocessBundle)
|
include(PostprocessBundle)
|
||||||
|
|
||||||
set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})
|
set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})
|
||||||
|
|
||||||
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
|
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
|
||||||
|
set_property(TARGET main PROPERTY MACOSX_BUNDLE_BUNDLE_NAME "${MACOSX_BUNDLE_BUNDLE_NAME}")
|
||||||
|
|
||||||
# Fix rpath
|
# Fix rpath
|
||||||
install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:main>)")
|
install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:main>)")
|
||||||
install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:updater>)")
|
install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:updater>)")
|
||||||
|
|
||||||
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
|
|
||||||
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
|
|
||||||
|
|
||||||
downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
|
downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
|
||||||
|
|
||||||
install(FILES ${IMHEX_ICON} DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources")
|
|
||||||
install(TARGETS main BUNDLE DESTINATION ".")
|
install(TARGETS main BUNDLE DESTINATION ".")
|
||||||
install(TARGETS updater DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
|
install(TARGETS updater DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
|
||||||
install(
|
install(
|
||||||
@@ -431,7 +431,7 @@ macro(createPackage)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (IMHEX_GENERATE_PACKAGE)
|
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||||
set(CPACK_BUNDLE_NAME "ImHex")
|
set(CPACK_BUNDLE_NAME "ImHex")
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
@@ -560,6 +560,9 @@ function(detectBadClone)
|
|||||||
|
|
||||||
file (GLOB EXTERNAL_DIRS "lib/external/*" "lib/third_party/*")
|
file (GLOB EXTERNAL_DIRS "lib/external/*" "lib/third_party/*")
|
||||||
foreach (EXTERNAL_DIR ${EXTERNAL_DIRS})
|
foreach (EXTERNAL_DIR ${EXTERNAL_DIRS})
|
||||||
|
if(NOT IS_DIRECTORY "${EXTERNAL_DIR}")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
file(GLOB_RECURSE RESULT "${EXTERNAL_DIR}/*")
|
file(GLOB_RECURSE RESULT "${EXTERNAL_DIR}/*")
|
||||||
list(LENGTH RESULT ENTRY_COUNT)
|
list(LENGTH RESULT ENTRY_COUNT)
|
||||||
if(ENTRY_COUNT LESS_EQUAL 1)
|
if(ENTRY_COUNT LESS_EQUAL 1)
|
||||||
@@ -587,7 +590,9 @@ endfunction()
|
|||||||
macro(detectBundledPlugins)
|
macro(detectBundledPlugins)
|
||||||
file(GLOB PLUGINS_DIRS "plugins/*")
|
file(GLOB PLUGINS_DIRS "plugins/*")
|
||||||
|
|
||||||
if (NOT DEFINED IMHEX_INCLUDE_PLUGINS)
|
if (IMHEX_INCLUDE_PLUGINS)
|
||||||
|
set(PLUGINS ${IMHEX_INCLUDE_PLUGINS})
|
||||||
|
else()
|
||||||
foreach(PLUGIN_DIR ${PLUGINS_DIRS})
|
foreach(PLUGIN_DIR ${PLUGINS_DIRS})
|
||||||
if (EXISTS "${PLUGIN_DIR}/CMakeLists.txt")
|
if (EXISTS "${PLUGIN_DIR}/CMakeLists.txt")
|
||||||
get_filename_component(PLUGIN_NAME ${PLUGIN_DIR} NAME)
|
get_filename_component(PLUGIN_NAME ${PLUGIN_DIR} NAME)
|
||||||
@@ -596,8 +601,6 @@ macro(detectBundledPlugins)
|
|||||||
endif ()
|
endif ()
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
else()
|
|
||||||
set(PLUGINS ${IMHEX_INCLUDE_PLUGINS})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
foreach(PLUGIN_NAME ${PLUGINS})
|
foreach(PLUGIN_NAME ${PLUGINS})
|
||||||
@@ -608,9 +611,13 @@ macro(detectBundledPlugins)
|
|||||||
message(FATAL_ERROR "No bundled plugins enabled")
|
message(FATAL_ERROR "No bundled plugins enabled")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT ("builtin" IN_LIST PLUGINS))
|
set(REQUIRED_PLUGINS builtin fonts ui)
|
||||||
message(FATAL_ERROR "The 'builtin' plugin is required for ImHex to work!")
|
foreach(PLUGIN ${REQUIRED_PLUGINS})
|
||||||
endif ()
|
list(FIND PLUGINS ${PLUGIN} PLUGIN_INDEX)
|
||||||
|
if (PLUGIN_INDEX EQUAL -1)
|
||||||
|
message(FATAL_ERROR "Required plugin '${PLUGIN}' is not enabled!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
macro(setVariableInParent variable value)
|
macro(setVariableInParent variable value)
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ find_path(LIBMAGIC_INCLUDE_DIR magic.h)
|
|||||||
|
|
||||||
find_library(LIBMAGIC_LIBRARY NAMES magic)
|
find_library(LIBMAGIC_LIBRARY NAMES magic)
|
||||||
|
|
||||||
if (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
find_package_handle_standard_args(Magic DEFAULT_MSG
|
||||||
set(LIBMAGIC_FOUND TRUE)
|
|
||||||
endif (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
|
||||||
|
|
||||||
find_package_handle_standard_args("libmagic" DEFAULT_MSG
|
|
||||||
LIBMAGIC_LIBRARY
|
LIBMAGIC_LIBRARY
|
||||||
LIBMAGIC_INCLUDE_DIR
|
LIBMAGIC_INCLUDE_DIR
|
||||||
)
|
)
|
||||||
@@ -14,5 +10,5 @@ find_package_handle_standard_args("libmagic" DEFAULT_MSG
|
|||||||
mark_as_advanced(
|
mark_as_advanced(
|
||||||
LIBMAGIC_INCLUDE_DIR
|
LIBMAGIC_INCLUDE_DIR
|
||||||
LIBMAGIC_LIBRARY
|
LIBMAGIC_LIBRARY
|
||||||
LIBMAGIC_FOUND
|
Magic_FOUND
|
||||||
)
|
)
|
||||||
|
|||||||
4
dist/AppImage/AppImageBuilder.yml
vendored
4
dist/AppImage/AppImageBuilder.yml
vendored
@@ -15,8 +15,8 @@ AppDir:
|
|||||||
- "{{ARCHITECTURE_PACKAGE}}"
|
- "{{ARCHITECTURE_PACKAGE}}"
|
||||||
allow_unauthenticated: true
|
allow_unauthenticated: true
|
||||||
sources:
|
sources:
|
||||||
- sourceline: 'deb [arch=amd64] http://us.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse'
|
- sourceline: 'deb [arch=amd64] https://us.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse'
|
||||||
- sourceline: 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe multiverse'
|
- sourceline: 'deb [arch=arm64] https://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe multiverse'
|
||||||
include:
|
include:
|
||||||
- libgdk-pixbuf2.0-0
|
- libgdk-pixbuf2.0-0
|
||||||
- libgdk-pixbuf2.0-common
|
- libgdk-pixbuf2.0-common
|
||||||
|
|||||||
28
dist/AppImage/Dockerfile
vendored
28
dist/AppImage/Dockerfile
vendored
@@ -30,9 +30,9 @@ ARG LTO=ON
|
|||||||
ARG BUILD_TYPE=RelWithDebInfo
|
ARG BUILD_TYPE=RelWithDebInfo
|
||||||
ARG GIT_COMMIT_HASH
|
ARG GIT_COMMIT_HASH
|
||||||
ARG GIT_BRANCH
|
ARG GIT_BRANCH
|
||||||
ARG ARCHITECTURE_PACKAGE
|
ARG ARCHITECTURE_PACKAGE=x86_64
|
||||||
ARG ARCHITECTURE_FILE_NAME
|
ARG ARCHITECTURE_FILE_NAME=amd64
|
||||||
ARG ARCHITECTURE_APPIMAGE_BUILDER
|
ARG ARCHITECTURE_APPIMAGE_BUILDER=x86_64
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# Ubuntu sh doesnt support string substitution
|
# Ubuntu sh doesnt support string substitution
|
||||||
@@ -42,16 +42,18 @@ RUN <<EOF
|
|||||||
# Prepare ImHex build
|
# Prepare ImHex build
|
||||||
set -xe
|
set -xe
|
||||||
|
|
||||||
CC=gcc-14 CXX=g++-14 cmake -G "Ninja" \
|
CC=gcc-14 CXX=g++-14 cmake -G "Ninja" \
|
||||||
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
|
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
|
||||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||||
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
|
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
|
||||||
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
|
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
|
||||||
-DIMHEX_ENABLE_LTO=${LTO} \
|
-DIMHEX_ENABLE_LTO=${LTO} \
|
||||||
-DIMHEX_PLUGINS_IN_SHARE=ON \
|
-DIMHEX_BUNDLE_PLUGIN_SDK=OFF \
|
||||||
|
`# To prevent using a libdir with an architecture-specific name` \
|
||||||
|
-DCMAKE_INSTALL_LIBDIR="lib" \
|
||||||
/imhex
|
/imhex
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
2
dist/DEBIAN/control.in
vendored
2
dist/DEBIAN/control.in
vendored
@@ -4,7 +4,7 @@ Section: editors
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
License: GNU GPL-2
|
License: GNU GPL-2
|
||||||
Depends: libfontconfig1, libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal, libssh2-1, md4c
|
Depends: libfontconfig1, libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal, libssh2-1, libmd4c0
|
||||||
Maintainer: WerWolv <hey@werwolv.net>
|
Maintainer: WerWolv <hey@werwolv.net>
|
||||||
Description: ImHex Hex Editor
|
Description: ImHex Hex Editor
|
||||||
A Hex Editor for Reverse Engineers, Programmers and
|
A Hex Editor for Reverse Engineers, Programmers and
|
||||||
|
|||||||
2
dist/ImHex.run.xml
vendored
2
dist/ImHex.run.xml
vendored
@@ -8,6 +8,6 @@
|
|||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration default="false" name="CMake Debug" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory">
|
<configuration default="false" name="CMake Debug" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory">
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
4
dist/compiling/macos.md
vendored
4
dist/compiling/macos.md
vendored
@@ -3,7 +3,7 @@
|
|||||||
On macOS, ImHex is built through regular GCC and LLVM clang.
|
On macOS, ImHex is built through regular GCC and LLVM clang.
|
||||||
|
|
||||||
1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`
|
1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`
|
||||||
2. Install all the dependencies using `brew bundle --no-lock --file dist/macOS/Brewfile`
|
2. Install all the dependencies using `brew bundle --file dist/macOS/Brewfile`
|
||||||
3. Build ImHex itself using the following commands:
|
3. Build ImHex itself using the following commands:
|
||||||
```sh
|
```sh
|
||||||
cd ImHex
|
cd ImHex
|
||||||
@@ -20,3 +20,5 @@ cmake -G "Ninja" \
|
|||||||
..
|
..
|
||||||
ninja install
|
ninja install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If your MacOS installation doesn't have graphic acceleration, you can check the [MacOS NoGPU guide](./macos_nogpu.md)
|
||||||
10
dist/compiling/macos_nogpu.md
vendored
Normal file
10
dist/compiling/macos_nogpu.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
### Compiling and running ImHex on macOS without a GPU
|
||||||
|
|
||||||
|
In order to run ImHex on a macOS installation without a GPU, you need a custom build of GLFW. You can build it this way:
|
||||||
|
|
||||||
|
Note: only tested on macOS x86
|
||||||
|
|
||||||
|
1. `git clone --depth 1 https://github.com/glfw/glfw`
|
||||||
|
2. `git apply {IMHEX_DIR}/dist/macOS/0001-glfw-SW.patch` (file is [here](../macOS/0001-glfw-SW.patch) in the ImHex repository. [Source](https://github.com/glfw/glfw/issues/2080).)
|
||||||
|
3. `cmake -G "Ninja" -DBUILD_SHARED_LIBS=ON ..`
|
||||||
|
4. `ninja install`, or `ninja` and figure out how to make ImHex detect the shared library
|
||||||
1
dist/flatpak/net.werwolv.ImHex.yaml
vendored
1
dist/flatpak/net.werwolv.ImHex.yaml
vendored
@@ -120,6 +120,7 @@ modules:
|
|||||||
- -DUSE_SYSTEM_FMT=ON
|
- -DUSE_SYSTEM_FMT=ON
|
||||||
- -DUSE_SYSTEM_YARA=ON
|
- -DUSE_SYSTEM_YARA=ON
|
||||||
- -DIMHEX_OFFLINE_BUILD=ON
|
- -DIMHEX_OFFLINE_BUILD=ON
|
||||||
|
- -DIMHEX_BUNDLE_PLUGIN_SDK=OFF
|
||||||
- -DCMAKE_INSTALL_LIBDIR=lib
|
- -DCMAKE_INSTALL_LIBDIR=lib
|
||||||
- -DCMAKE_INSTALL_RPATH='$ORIGIN/../lib:$ORIGIN/../lib64'
|
- -DCMAKE_INSTALL_RPATH='$ORIGIN/../lib:$ORIGIN/../lib64'
|
||||||
sources:
|
sources:
|
||||||
|
|||||||
2
dist/macOS/0001-glfw-SW.patch
vendored
2
dist/macOS/0001-glfw-SW.patch
vendored
@@ -1,5 +1,5 @@
|
|||||||
From 9c8665af4c2e2ce66555c15c05c72027bfdf0cb6 Mon Sep 17 00:00:00 2001
|
From 9c8665af4c2e2ce66555c15c05c72027bfdf0cb6 Mon Sep 17 00:00:00 2001
|
||||||
From: iTrooz <itrooz@protonmail.com>
|
From: iTrooz <hey@itrooz.fr>
|
||||||
Date: Mon, 29 Aug 2022 17:29:38 +0200
|
Date: Mon, 29 Aug 2022 17:29:38 +0200
|
||||||
Subject: [PATCH] Use software rendering on MacOS
|
Subject: [PATCH] Use software rendering on MacOS
|
||||||
|
|
||||||
|
|||||||
12
dist/macOS/arm64.Dockerfile
vendored
12
dist/macOS/arm64.Dockerfile
vendored
@@ -1,7 +1,7 @@
|
|||||||
# This base image is also known as "crosscompile". See arm64.crosscompile.Dockerfile
|
# This base image is also known as "crosscompile". See arm64.crosscompile.Dockerfile
|
||||||
FROM ghcr.io/werwolv/macos-crosscompile:4c4af2d1a6a102fab93cc9cd660280c2ec9d72af as build
|
FROM ghcr.io/werwolv/macos-crosscompile:6d89b20ac5ebedb6f680f94637591c94cb36f40b as build
|
||||||
|
|
||||||
ENV MACOSX_DEPLOYMENT_TARGET 13.0
|
ENV MACOSX_DEPLOYMENT_TARGET 11.0
|
||||||
|
|
||||||
# -- DOWNLOADING STUFF
|
# -- DOWNLOADING STUFF
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ if [ "$CUSTOM_GLFW" ]; then
|
|||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
CC=o64-clang CXX=o64-clang++ cmake -G "Ninja" \
|
CC=o64-clang CXX=o64-clang++ cmake -G "Ninja" \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||||
-DBUILD_SHARED_LIBS=ON \
|
-DBUILD_SHARED_LIBS=ON \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
@@ -150,9 +151,7 @@ EOF
|
|||||||
# Build ImHex
|
# Build ImHex
|
||||||
## Copy ImHex
|
## Copy ImHex
|
||||||
COPY --from=imhex / /mnt/ImHex
|
COPY --from=imhex / /mnt/ImHex
|
||||||
## Patch ImHex with hacks
|
## Configure ImHex build
|
||||||
# COPY toolchain.cmake.2 /osxcross/target/toolchain.cmake
|
|
||||||
# Configure ImHex build
|
|
||||||
RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps \
|
RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps \
|
||||||
cd /mnt/ImHex && \
|
cd /mnt/ImHex && \
|
||||||
# compilers
|
# compilers
|
||||||
@@ -170,6 +169,7 @@ RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/
|
|||||||
-DIMHEX_STRICT_WARNINGS=OFF \
|
-DIMHEX_STRICT_WARNINGS=OFF \
|
||||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||||
-DCMAKE_INSTALL_PREFIX=/mnt/ImHex/build/install \
|
-DCMAKE_INSTALL_PREFIX=/mnt/ImHex/build/install \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||||
-B build
|
-B build
|
||||||
## Build ImHex
|
## Build ImHex
|
||||||
RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps <<EOF
|
RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps <<EOF
|
||||||
@@ -184,4 +184,4 @@ EOF
|
|||||||
|
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=build /mnt/ImHex/build/install/imhex.app imhex.app
|
COPY --from=build /mnt/ImHex/build/install/ImHex.app ImHex.app
|
||||||
|
|||||||
11
dist/macOS/osx_10_15/x64-osx.cmake
vendored
Normal file
11
dist/macOS/osx_10_15/x64-osx.cmake
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||||
|
set(VCPKG_BUILD_TYPE release)
|
||||||
|
set(VCPKG_CRT_LINKAGE dynamic)
|
||||||
|
set(VCPKG_LIBRARY_LINKAGE static)
|
||||||
|
|
||||||
|
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
||||||
|
set(VCPKG_OSX_ARCHITECTURES x86_64)
|
||||||
|
set(VCPKG_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "" FORCE)
|
||||||
|
set(VCPKG_C_FLAGS "-mmacosx-version-min=10.15")
|
||||||
|
set(VCPKG_CXX_FLAGS "-mmacosx-version-min=10.15")
|
||||||
|
set(ENV{MACOSX_DEPLOYMENT_TARGET} "10.15")
|
||||||
1
dist/snap/snapcraft.yaml
vendored
1
dist/snap/snapcraft.yaml
vendored
@@ -41,6 +41,7 @@ parts:
|
|||||||
- -DCMAKE_C_COMPILER_LAUNCHER=${CCACHE}
|
- -DCMAKE_C_COMPILER_LAUNCHER=${CCACHE}
|
||||||
- -DCMAKE_CXX_COMPILER_LAUNCHER=${CCACHE}
|
- -DCMAKE_CXX_COMPILER_LAUNCHER=${CCACHE}
|
||||||
- -DIMHEX_PATTERNS_PULL_MASTER=ON
|
- -DIMHEX_PATTERNS_PULL_MASTER=ON
|
||||||
|
- -DIMHEX_BUNDLE_PLUGIN_SDK=OFF
|
||||||
cmake-generator: Ninja
|
cmake-generator: Ninja
|
||||||
build-packages:
|
build-packages:
|
||||||
- cmake
|
- cmake
|
||||||
|
|||||||
2
dist/vcpkg.json
vendored
2
dist/vcpkg.json
vendored
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vcpkg",
|
"name": "vcpkg",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"builtin-baseline": "7e21420f775f72ae938bdeb5e6068f722088f06a",
|
"builtin-baseline": "66c0373dc7fca549e5803087b9487edfe3aca0a1",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"libmagic",
|
"libmagic",
|
||||||
"freetype",
|
"freetype",
|
||||||
|
|||||||
12
dist/web/Dockerfile
vendored
12
dist/web/Dockerfile
vendored
@@ -2,7 +2,7 @@ FROM emscripten/emsdk:4.0.21 AS build
|
|||||||
|
|
||||||
# Used to invalidate layer cache but not mount cache
|
# Used to invalidate layer cache but not mount cache
|
||||||
# See https://github.com/moby/moby/issues/41715#issuecomment-733976493
|
# See https://github.com/moby/moby/issues/41715#issuecomment-733976493
|
||||||
ARG UNIQUEKEY 1
|
ARG UNIQUEKEY=1
|
||||||
|
|
||||||
RUN apt update
|
RUN apt update
|
||||||
RUN apt install -y git ccache autoconf automake libtool pkg-config ninja-build
|
RUN apt install -y git ccache autoconf automake libtool pkg-config ninja-build
|
||||||
@@ -12,13 +12,13 @@ RUN <<EOF
|
|||||||
# Note: we are a patch on the libmagic port
|
# Note: we are a patch on the libmagic port
|
||||||
set -xe
|
set -xe
|
||||||
|
|
||||||
git clone https://github.com/microsoft/vcpkg /vcpkg
|
git clone --depth 1 https://github.com/microsoft/vcpkg /vcpkg
|
||||||
git -C /vcpkg pull
|
|
||||||
/vcpkg/bootstrap-vcpkg.sh
|
/vcpkg/bootstrap-vcpkg.sh
|
||||||
sed -i 's/vcpkg_install_make(${EXTRA_ARGS})/vcpkg_install_make(${EXTRA_ARGS} SUBPATH src)/g' /vcpkg/ports/libmagic/portfile.cmake
|
sed -i 's/vcpkg_install_make(${EXTRA_ARGS})/vcpkg_install_make(${EXTRA_ARGS} SUBPATH src)/g' /vcpkg/ports/libmagic/portfile.cmake
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Patch vcpkg build instructions to add -pthread
|
# Patch vcpkg build instructions to add -pthread flag
|
||||||
|
# Even dependencies must be built with -pthread to be able to use USE_PTHREADS=1
|
||||||
RUN <<EOF
|
RUN <<EOF
|
||||||
set -xe
|
set -xe
|
||||||
|
|
||||||
@@ -50,6 +50,7 @@ ENV CCACHE_DIR=/cache/ccache
|
|||||||
|
|
||||||
RUN mkdir /build
|
RUN mkdir /build
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
ARG BUILD_TYPE=Release
|
||||||
RUN --mount=type=cache,target=/cache \
|
RUN --mount=type=cache,target=/cache \
|
||||||
--mount=type=bind,source=.,target=/imhex <<EOF
|
--mount=type=bind,source=.,target=/imhex <<EOF
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ ccache -zs
|
|||||||
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \
|
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \
|
||||||
-DLIBROMFS_COMPRESS_RESOURCES=OFF \
|
-DLIBROMFS_COMPRESS_RESOURCES=OFF \
|
||||||
-DIMHEX_ENABLE_PLUGIN_TESTS=OFF \
|
-DIMHEX_ENABLE_PLUGIN_TESTS=OFF \
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
-DCMAKE_BUILD_TYPE=${BUILD_TYPE}
|
||||||
|
|
||||||
ninja -j $JOBS
|
ninja -j $JOBS
|
||||||
|
|
||||||
@@ -106,3 +107,4 @@ COPY --from=build [ \
|
|||||||
|
|
||||||
FROM nginx
|
FROM nginx
|
||||||
COPY --from=raw . /usr/share/nginx/html
|
COPY --from=raw . /usr/share/nginx/html
|
||||||
|
RUN chmod -R 755 /usr/share/nginx/html
|
||||||
|
|||||||
9
dist/web/Host.Dockerfile
vendored
Normal file
9
dist/web/Host.Dockerfile
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
WORKDIR /imhex
|
||||||
|
|
||||||
|
COPY ./out/ .
|
||||||
|
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
CMD [ "python", "/imhex/start_imhex_web.py" ]
|
||||||
1
dist/web/compose.yml
vendored
1
dist/web/compose.yml
vendored
@@ -1,5 +1,4 @@
|
|||||||
# docker compose -f dist/web/compose.yml up --build
|
# docker compose -f dist/web/compose.yml up --build
|
||||||
version: '3'
|
|
||||||
services:
|
services:
|
||||||
imhex_web:
|
imhex_web:
|
||||||
image: imhex_web:latest
|
image: imhex_web:latest
|
||||||
|
|||||||
2
dist/web/serve.py
vendored
2
dist/web/serve.py
vendored
@@ -10,6 +10,6 @@ class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
os.chdir(".")
|
os.chdir(".")
|
||||||
httpd = http.server.HTTPServer(("localhost", 9090), MyHttpRequestHandler)
|
httpd = http.server.HTTPServer(("0.0.0.0", 9090), MyHttpRequestHandler)
|
||||||
print(f"Serving {os.getcwd()} on http://{httpd.server_address[0]}:{httpd.server_address[1]}")
|
print(f"Serving {os.getcwd()} on http://{httpd.server_address[0]}:{httpd.server_address[1]}")
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|||||||
16
dist/web/source/index.html
vendored
16
dist/web/source/index.html
vendored
@@ -15,17 +15,17 @@
|
|||||||
|
|
||||||
<!-- Open Graph / Facebook -->
|
<!-- Open Graph / Facebook -->
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
<meta property="og:url" content="https://imhex.werwolv.net/">
|
<meta property="og:url" content="https://web.imhex.werwolv.net/">
|
||||||
<meta property="og:title" content="ImHex Web - Online Hex Editor">
|
<meta property="og:title" content="ImHex Web - Online Hex Editor">
|
||||||
<meta property="og:image" content="https://imhex.werwolv.net/assets/splash_wasm.png">
|
<meta property="og:image" content="splash_wasm.png">
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta property="twitter:card" content="summary_large_image">
|
<meta property="twitter:card" content="summary_large_image">
|
||||||
<meta property="twitter:url" content="https://imhex.werwolv.net/">
|
<meta property="twitter:url" content="https://web.imhex.werwolv.net/">
|
||||||
<meta property="twitter:title" content="ImHex Web - Online Hex Editor">
|
<meta property="twitter:title" content="ImHex Web - Online Hex Editor">
|
||||||
<meta property="twitter:description"
|
<meta property="twitter:description"
|
||||||
content="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.">
|
content="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.">
|
||||||
<meta property="twitter:image" content="https://imhex.werwolv.net/assets/splash_wasm.png">
|
<meta property="twitter:image" content="splash_wasm.png">
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="style.css">
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@
|
|||||||
"email": "hey@werwolv.net",
|
"email": "hey@werwolv.net",
|
||||||
"founder": "WerWolv",
|
"founder": "WerWolv",
|
||||||
"slogan": "A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.",
|
"slogan": "A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.",
|
||||||
"url": "https://imhex.werwolv.net",
|
"url": "https://web.imhex.werwolv.net",
|
||||||
"logo": "https://imhex.werwolv.net/assets/logos/logo.svg"
|
"logo": "https://web.imhex.werwolv.net/icon.svg"
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -96,7 +96,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
<div id="canvas-wrapper" class="imhex-web-canvas-wrapper">
|
||||||
|
<canvas class="imhex-web-canvas canvas-fixed" id="canvas" ></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="wasm-config.js"></script>
|
<script src="wasm-config.js"></script>
|
||||||
<script async src="imhex.js"></script>
|
<script async src="imhex.js"></script>
|
||||||
|
|||||||
BIN
dist/web/source/splash_wasm.png
vendored
Normal file
BIN
dist/web/source/splash_wasm.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 478 KiB |
19
dist/web/source/style.css
vendored
19
dist/web/source/style.css
vendored
@@ -185,4 +185,23 @@ a:hover {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imhex-web-canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
image-rendering: smooth;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imhex-web-canvas-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-size: 100% 100%;
|
||||||
}
|
}
|
||||||
96
dist/web/source/wasm-config.js
vendored
96
dist/web/source/wasm-config.js
vendored
@@ -59,8 +59,14 @@ monkeyPatch((file, done) => {
|
|||||||
const mibTotal = (wasmSize / 1024**2).toFixed(1);
|
const mibTotal = (wasmSize / 1024**2).toFixed(1);
|
||||||
|
|
||||||
let root = document.querySelector(':root');
|
let root = document.querySelector(':root');
|
||||||
root.style.setProperty("--progress", `${percent}%`)
|
if (root != null) {
|
||||||
document.getElementById("progress-bar-content").innerHTML = `${percent}% [${mibNow} MiB / ${mibTotal} MiB]`;
|
root.style.setProperty("--progress", `${percent}%`)
|
||||||
|
let progressBar = document.getElementById("progress-bar-content");
|
||||||
|
|
||||||
|
if (progressBar != null) {
|
||||||
|
progressBar.innerHTML = `${percent}% [${mibNow} MiB / ${mibTotal} MiB]`;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function glfwSetCursorCustom(wnd, shape) {
|
function glfwSetCursorCustom(wnd, shape) {
|
||||||
@@ -100,81 +106,17 @@ var notWorkingTimer = setTimeout(() => {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
var Module = {
|
var Module = {
|
||||||
preRun: [],
|
preRun: () => {
|
||||||
|
ENV.IMHEX_SKIP_SPLASH_SCREEN = "1";
|
||||||
|
},
|
||||||
postRun: function() {
|
postRun: function() {
|
||||||
// Patch the emscripten GLFW module to send mouse and touch events in the right order
|
|
||||||
// For ImGui interactions to correctly work with touch input, MousePos events need
|
|
||||||
// to be processed first and then MouseButton events in the next frame. By default,
|
|
||||||
// GLFW does the exact opposite, which causes buttons to require two taps to register
|
|
||||||
// and windows get "stuck" to the cursor when dragged or resized
|
|
||||||
GLFW.onMousemove = event => {
|
|
||||||
if (event.type === "touchmove") {
|
|
||||||
event.preventDefault();
|
|
||||||
let primaryChanged = false;
|
|
||||||
for (let i of event.changedTouches) {
|
|
||||||
if (GLFW.primaryTouchId === i.identifier) {
|
|
||||||
Browser.setMouseCoords(i.pageX, i.pageY);
|
|
||||||
primaryChanged = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!primaryChanged) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Browser.calculateMouseEvent(event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
GLFW.onMouseButtonChanged = (event, status) => {
|
|
||||||
if (!GLFW.active) return;
|
|
||||||
if (event.target != Module["canvas"]) return;
|
|
||||||
const isTouchType = event.type === "touchstart" || event.type === "touchend" || event.type === "touchcancel";
|
|
||||||
let eventButton = 0;
|
|
||||||
if (isTouchType) {
|
|
||||||
event.preventDefault();
|
|
||||||
let primaryChanged = false;
|
|
||||||
if (GLFW.primaryTouchId === null && event.type === "touchstart" && event.targetTouches.length > 0) {
|
|
||||||
const chosenTouch = event.targetTouches[0];
|
|
||||||
GLFW.primaryTouchId = chosenTouch.identifier;
|
|
||||||
Browser.setMouseCoords(chosenTouch.pageX, chosenTouch.pageY);
|
|
||||||
primaryChanged = true;
|
|
||||||
} else if (event.type === "touchend" || event.type === "touchcancel") {
|
|
||||||
for (let i of event.changedTouches) {
|
|
||||||
if (GLFW.primaryTouchId === i.identifier) {
|
|
||||||
GLFW.primaryTouchId = null;
|
|
||||||
primaryChanged = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!primaryChanged) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Browser.calculateMouseEvent(event);
|
|
||||||
eventButton = GLFW.DOMToGLFWMouseButton(event);
|
|
||||||
}
|
|
||||||
if (status == 1) {
|
|
||||||
GLFW.active.buttons |= (1 << eventButton);
|
|
||||||
try {
|
|
||||||
event.target.setCapture();
|
|
||||||
} catch (e) {}
|
|
||||||
} else {
|
|
||||||
GLFW.active.buttons &= ~(1 << eventButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GLFW.active.cursorPosFunc) {
|
|
||||||
getWasmTableEntry(GLFW.active.cursorPosFunc)(GLFW.active.id, Browser.mouseX, Browser.mouseY);
|
|
||||||
}
|
|
||||||
if (GLFW.active.mouseButtonFunc) {
|
|
||||||
getWasmTableEntry(GLFW.active.mouseButtonFunc)(GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
onRuntimeInitialized: function() {
|
onRuntimeInitialized: function() {
|
||||||
// Triggered when the wasm module is loaded and ready to use.
|
// Triggered when the wasm module is loaded and ready to use.
|
||||||
document.getElementById("loading").style.display = "none"
|
let loading = document.getElementById("loading");
|
||||||
|
if (loading != null)
|
||||||
|
document.getElementById("loading").style.display = "none"
|
||||||
document.getElementById("canvas").style.display = "initial"
|
document.getElementById("canvas").style.display = "initial"
|
||||||
|
|
||||||
clearTimeout(notWorkingTimer);
|
clearTimeout(notWorkingTimer);
|
||||||
@@ -257,16 +199,6 @@ if (urlParams.has("lang")) {
|
|||||||
Module["arguments"].push(urlParams.get("save-editor"));
|
Module["arguments"].push(urlParams.get("save-editor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', js_resizeCanvas, false);
|
|
||||||
function js_resizeCanvas() {
|
|
||||||
let canvas = document.getElementById('canvas');
|
|
||||||
|
|
||||||
canvas.top = document.documentElement.clientTop;
|
|
||||||
canvas.left = document.documentElement.clientLeft;
|
|
||||||
canvas.width = Math.min(document.documentElement.clientWidth, window.innerWidth || 0);
|
|
||||||
canvas.height = Math.min(document.documentElement.clientHeight, window.innerHeight || 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent some default browser shortcuts from preventing ImHex ones to work
|
// Prevent some default browser shortcuts from preventing ImHex ones to work
|
||||||
document.addEventListener('keydown', e => {
|
document.addEventListener('keydown', e => {
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey) {
|
||||||
|
|||||||
2
lib/external/disassembler
vendored
2
lib/external/disassembler
vendored
Submodule lib/external/disassembler updated: 7235352627...c66e624157
2
lib/external/libwolv
vendored
2
lib/external/libwolv
vendored
Submodule lib/external/libwolv updated: 5be84628dc...1063613e87
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
Submodule lib/external/pattern_language updated: 33057885fd...9adb36901a
@@ -57,6 +57,9 @@ set(LIBIMHEX_SOURCES
|
|||||||
source/ui/toast.cpp
|
source/ui/toast.cpp
|
||||||
source/ui/banner.cpp
|
source/ui/banner.cpp
|
||||||
|
|
||||||
|
source/mcp/client.cpp
|
||||||
|
source/mcp/server.cpp
|
||||||
|
|
||||||
source/subcommands/subcommands.cpp
|
source/subcommands/subcommands.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -145,62 +145,17 @@ EXPORT_MODULE namespace hex {
|
|||||||
* @brief Returns the icon of the achievement
|
* @brief Returns the icon of the achievement
|
||||||
* @return Icon of the achievement
|
* @return Icon of the achievement
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] const ImGuiExt::Texture &getIcon() const {
|
[[nodiscard]] const char* getIcon() const {
|
||||||
if (m_iconData.empty())
|
return m_icon.c_str();
|
||||||
return m_icon;
|
|
||||||
|
|
||||||
if (m_icon.isValid())
|
|
||||||
return m_icon;
|
|
||||||
|
|
||||||
m_icon = ImGuiExt::Texture::fromImage(m_iconData.data(), m_iconData.size(), ImGuiExt::Texture::Filter::Linear);
|
|
||||||
|
|
||||||
return m_icon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the icon of the achievement
|
* @brief Sets the icon of the achievement
|
||||||
* @param data Icon data
|
* @param icon Icon glyph
|
||||||
* @return Reference to the achievement
|
* @return Reference to the achievement
|
||||||
*/
|
*/
|
||||||
Achievement& setIcon(std::span<const std::byte> data) {
|
Achievement& setIcon(std::string icon) {
|
||||||
m_iconData.reserve(data.size());
|
m_icon = std::move(icon);
|
||||||
for (auto &byte : data)
|
|
||||||
m_iconData.emplace_back(static_cast<u8>(byte));
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets the icon of the achievement
|
|
||||||
* @param data Icon data
|
|
||||||
* @return Reference to the achievement
|
|
||||||
*/
|
|
||||||
Achievement& setIcon(std::span<const u8> data) {
|
|
||||||
m_iconData.assign(data.begin(), data.end());
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets the icon of the achievement
|
|
||||||
* @param data Icon data
|
|
||||||
* @return Reference to the achievement
|
|
||||||
*/
|
|
||||||
Achievement& setIcon(std::vector<u8> data) {
|
|
||||||
m_iconData = std::move(data);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets the icon of the achievement
|
|
||||||
* @param data Icon data
|
|
||||||
* @return Reference to the achievement
|
|
||||||
*/
|
|
||||||
Achievement& setIcon(const std::vector<std::byte> &data) {
|
|
||||||
m_iconData.reserve(data.size());
|
|
||||||
for (auto &byte : data)
|
|
||||||
m_iconData.emplace_back(static_cast<u8>(byte));
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -284,8 +239,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
std::function<void(Achievement &)> m_clickCallback;
|
std::function<void(Achievement &)> m_clickCallback;
|
||||||
|
|
||||||
std::vector<u8> m_iconData;
|
std::string m_icon;
|
||||||
mutable ImGuiExt::Texture m_icon;
|
|
||||||
|
|
||||||
u32 m_progress = 0;
|
u32 m_progress = 0;
|
||||||
u32 m_maxProgress = 1;
|
u32 m_maxProgress = 1;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
void stopServices();
|
void stopServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerService(const UnlocalizedString &unlocalizedString, const impl::Callback &callback);
|
void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <hex/mcp/server.hpp>
|
||||||
|
|
||||||
EXPORT_MODULE namespace hex {
|
EXPORT_MODULE namespace hex {
|
||||||
|
|
||||||
/* Network Communication Interface Registry. Allows adding new communication interface endpoints */
|
/* Network Communication Interface Registry. Allows adding new communication interface endpoints */
|
||||||
@@ -22,4 +24,19 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ContentRegistry::MCP {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
std::unique_ptr<mcp::Server>& getMcpServerInstance();
|
||||||
|
|
||||||
|
void setEnabled(bool enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled();
|
||||||
|
bool isConnected();
|
||||||
|
|
||||||
|
void registerTool(std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json ¶ms)> function);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <bit>
|
||||||
|
|
||||||
EXPORT_MODULE namespace hex {
|
EXPORT_MODULE namespace hex {
|
||||||
|
|
||||||
@@ -22,8 +23,10 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
|
||||||
|
struct DoNotUseThisByItselfTag {};
|
||||||
|
|
||||||
using DisplayFunction = std::function<std::string()>;
|
using DisplayFunction = std::function<std::string()>;
|
||||||
using EditingFunction = std::function<std::vector<u8>(std::string, std::endian)>;
|
using EditingFunction = std::function<std::optional<std::vector<u8>>(std::string&, std::endian, DoNotUseThisByItselfTag)>;
|
||||||
using GeneratorFunction = std::function<DisplayFunction(const std::vector<u8> &, std::endian, NumberDisplayStyle)>;
|
using GeneratorFunction = std::function<DisplayFunction(const std::vector<u8> &, std::endian, NumberDisplayStyle)>;
|
||||||
|
|
||||||
struct Entry {
|
struct Entry {
|
||||||
@@ -38,6 +41,35 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace EditWidget {
|
||||||
|
|
||||||
|
class Widget {
|
||||||
|
public:
|
||||||
|
using Function = std::function<std::vector<u8>(const std::string&, std::endian)>;
|
||||||
|
|
||||||
|
explicit Widget(const Function &function) : m_function(function) {}
|
||||||
|
|
||||||
|
virtual ~Widget() = default;
|
||||||
|
virtual std::optional<std::vector<u8>> draw(std::string &value, std::endian endian) = 0;
|
||||||
|
std::optional<std::vector<u8>> operator()(std::string &value, std::endian endian, impl::DoNotUseThisByItselfTag) {
|
||||||
|
return draw(value, endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> getBytes(const std::string &value, std::endian endian) const {
|
||||||
|
return m_function(value, endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Function m_function;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextInput : Widget {
|
||||||
|
explicit TextInput(const Function &function) : Widget(function) {}
|
||||||
|
std::optional<std::vector<u8>> draw(std::string &value, std::endian endian) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds a new entry to the data inspector
|
* @brief Adds a new entry to the data inspector
|
||||||
* @param unlocalizedName The unlocalized name of the entry
|
* @param unlocalizedName The unlocalized name of the entry
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
void addProviderName(const UnlocalizedString &unlocalizedName, const char *icon);
|
void addProviderName(const UnlocalizedString &unlocalizedName, const char *icon);
|
||||||
|
|
||||||
using ProviderCreationFunction = std::function<std::unique_ptr<prv::Provider>()>;
|
using ProviderCreationFunction = std::function<std::shared_ptr<prv::Provider>()>;
|
||||||
void add(const std::string &typeName, ProviderCreationFunction creationFunction);
|
void add(const std::string &typeName, ProviderCreationFunction creationFunction);
|
||||||
|
|
||||||
struct Entry {
|
struct Entry {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface& setTooltip(const std::string &tooltip) {
|
Interface& setTooltip(const UnlocalizedString &tooltip) {
|
||||||
m_tooltip = tooltip;
|
m_tooltip = tooltip;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@@ -239,6 +239,14 @@ EXPORT_MODULE namespace hex {
|
|||||||
nlohmann::json store() override { return {}; }
|
nlohmann::json store() override { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Spacer : public Widget {
|
||||||
|
public:
|
||||||
|
bool draw(const std::string &name) override;
|
||||||
|
|
||||||
|
void load(const nlohmann::json &) override {}
|
||||||
|
nlohmann::json store() override { return {}; }
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
@@ -290,8 +298,8 @@ EXPORT_MODULE namespace hex {
|
|||||||
public:
|
public:
|
||||||
SettingsValue(nlohmann::json value) : m_value(std::move(value)) {}
|
SettingsValue(nlohmann::json value) : m_value(std::move(value)) {}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))
|
||||||
T get(std::common_type_t<T> defaultValue) const {
|
[[nodiscard]] T get(T defaultValue) const {
|
||||||
try {
|
try {
|
||||||
auto result = m_value;
|
auto result = m_value;
|
||||||
if (result.is_number() && std::same_as<T, bool>)
|
if (result.is_number() && std::same_as<T, bool>)
|
||||||
@@ -308,8 +316,8 @@ EXPORT_MODULE namespace hex {
|
|||||||
nlohmann::json m_value;
|
nlohmann::json m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))
|
||||||
[[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &defaultValue) {
|
[[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T defaultValue) {
|
||||||
auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);
|
auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -326,8 +334,8 @@ EXPORT_MODULE namespace hex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))
|
||||||
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &value) {
|
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T value) {
|
||||||
impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;
|
impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;
|
||||||
impl::runOnChangeHandlers(unlocalizedCategory, unlocalizedName, value);
|
impl::runOnChangeHandlers(unlocalizedCategory, unlocalizedName, value);
|
||||||
|
|
||||||
@@ -336,10 +344,75 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
using OnChangeCallback = std::function<void(const SettingsValue &)>;
|
using OnChangeCallback = std::function<void(const SettingsValue &)>;
|
||||||
u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback);
|
u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback);
|
||||||
|
void removeOnChangeHandler(u64 id);
|
||||||
|
|
||||||
using OnSaveCallback = std::function<void()>;
|
using OnSaveCallback = std::function<void()>;
|
||||||
u64 onSave(const OnSaveCallback &callback);
|
u64 onSave(const OnSaveCallback &callback);
|
||||||
|
|
||||||
|
template<typename T, wolv::type::StaticString UnlocalizedCategory, wolv::type::StaticString UnlocalizedName>
|
||||||
|
requires (!(std::is_reference_v<T> || std::is_const_v<T>))
|
||||||
|
class SettingsVariable {
|
||||||
|
public:
|
||||||
|
explicit(false) SettingsVariable(T defaultValue) noexcept : m_defaultValue(std::move(defaultValue)) { }
|
||||||
|
|
||||||
|
SettingsVariable(const SettingsVariable&) = delete;
|
||||||
|
SettingsVariable& operator=(const SettingsVariable&) = delete;
|
||||||
|
|
||||||
|
SettingsVariable(SettingsVariable&&) = delete;
|
||||||
|
SettingsVariable& operator=(SettingsVariable&&) = delete;
|
||||||
|
|
||||||
|
~SettingsVariable() {
|
||||||
|
if (m_onChangeId > 0)
|
||||||
|
removeOnChangeHandler(m_onChangeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] T get() const {
|
||||||
|
registerChangeHandler();
|
||||||
|
if (!m_value.has_value()) {
|
||||||
|
m_value = read<T>(
|
||||||
|
UnlocalizedCategory.value.data(),
|
||||||
|
UnlocalizedName.value.data(),
|
||||||
|
m_defaultValue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_value.value_or(m_defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(T value) {
|
||||||
|
registerChangeHandler();
|
||||||
|
write<T>(
|
||||||
|
UnlocalizedCategory.value.data(),
|
||||||
|
UnlocalizedName.value.data(),
|
||||||
|
std::move(value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit(false) operator T() const {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsVariable& operator=(T value) {
|
||||||
|
set(std::move(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void registerChangeHandler() const {
|
||||||
|
if (m_onChangeId > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_onChangeId = onChange(UnlocalizedCategory.value.data(), UnlocalizedName.value.data(), [this](const SettingsValue &value) {
|
||||||
|
m_value = value.get<T>(m_defaultValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::optional<T> m_value;
|
||||||
|
T m_defaultValue;
|
||||||
|
mutable u64 m_onChangeId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -68,6 +68,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
constexpr static auto SeparatorValue = "$SEPARATOR$";
|
constexpr static auto SeparatorValue = "$SEPARATOR$";
|
||||||
constexpr static auto SubMenuValue = "$SUBMENU$";
|
constexpr static auto SubMenuValue = "$SUBMENU$";
|
||||||
|
constexpr static auto TaskBarMenuValue = "$TASKBAR$";
|
||||||
|
|
||||||
const std::multimap<u32, MainMenuItem>& getMainMenuItems();
|
const std::multimap<u32, MainMenuItem>& getMainMenuItems();
|
||||||
|
|
||||||
@@ -199,6 +200,19 @@ EXPORT_MODULE namespace hex {
|
|||||||
*/
|
*/
|
||||||
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, View *view = nullptr);
|
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, View *view = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a new main menu entry
|
||||||
|
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
|
||||||
|
* @param priority The priority of the entry. Lower values are displayed first
|
||||||
|
* @param function The function to call when the entry is clicked
|
||||||
|
* @param enabledCallback The function to call to determine if the entry is enabled
|
||||||
|
*/
|
||||||
|
void addTaskBarMenuItem(
|
||||||
|
std::vector<UnlocalizedString> unlocalizedMainMenuNames,
|
||||||
|
u32 priority,
|
||||||
|
const impl::MenuCallback &function,
|
||||||
|
const impl::EnabledCallback& enabledCallback
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds a new welcome screen entry
|
* @brief Adds a new welcome screen entry
|
||||||
@@ -220,10 +234,10 @@ EXPORT_MODULE namespace hex {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds a menu item to the toolbar
|
* @brief Adds a menu item to the toolbar
|
||||||
* @param unlocalizedName Unlocalized name of the menu item
|
* @param unlocalizedNames Unlocalized name of the menu item
|
||||||
* @param color Color of the toolbar icon
|
* @param color Color of the toolbar icon
|
||||||
*/
|
*/
|
||||||
void addMenuItemToToolbar(const UnlocalizedString &unlocalizedName, ImGuiCustomCol color);
|
void addMenuItemToToolbar(const std::vector<UnlocalizedString> &unlocalizedNames, ImGuiCustomCol color);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reconstructs the toolbar items list after they have been modified
|
* @brief Reconstructs the toolbar items list after they have been modified
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace hex {
|
|||||||
* This event is responsible for (optionally) initializing the provider and calling EventProviderOpened
|
* This event is responsible for (optionally) initializing the provider and calling EventProviderOpened
|
||||||
* (although the event can also be called manually without problem)
|
* (although the event can also be called manually without problem)
|
||||||
*/
|
*/
|
||||||
EVENT_DEF(EventProviderCreated, prv::Provider *);
|
EVENT_DEF(EventProviderCreated, std::shared_ptr<prv::Provider>);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called as a continuation of EventProviderCreated
|
* @brief Called as a continuation of EventProviderCreated
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ namespace hex {
|
|||||||
/**
|
/**
|
||||||
* @brief Creates a provider from its unlocalized name, and add it to the provider list
|
* @brief Creates a provider from its unlocalized name, and add it to the provider list
|
||||||
*/
|
*/
|
||||||
EVENT_DEF(RequestCreateProvider, std::string, bool, bool, hex::prv::Provider **);
|
EVENT_DEF(RequestCreateProvider, std::string, bool, bool, std::shared_ptr<hex::prv::Provider> *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Used internally when opening a provider through the API
|
||||||
|
*/
|
||||||
|
EVENT_DEF(RequestOpenProvider, std::shared_ptr<prv::Provider>);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move the data from all PerProvider instances from one provider to another
|
* @brief Move the data from all PerProvider instances from one provider to another
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
||||||
* @param select Whether to select the provider after adding it
|
* @param select Whether to select the provider after adding it
|
||||||
*/
|
*/
|
||||||
void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface = false, bool select = true);
|
void add(std::shared_ptr<prv::Provider> &&provider, bool skipLoadInterface = false, bool select = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new provider and adds it to the list of providers
|
* @brief Creates a new provider and adds it to the list of providers
|
||||||
@@ -111,12 +111,18 @@ EXPORT_MODULE namespace hex {
|
|||||||
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
||||||
* @param select Whether to select the provider after adding it
|
* @param select Whether to select the provider after adding it
|
||||||
*/
|
*/
|
||||||
prv::Provider* createProvider(
|
std::shared_ptr<prv::Provider> createProvider(
|
||||||
const UnlocalizedString &unlocalizedName,
|
const UnlocalizedString &unlocalizedName,
|
||||||
bool skipLoadInterface = false,
|
bool skipLoadInterface = false,
|
||||||
bool select = true
|
bool select = true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opens a provider, making its data available to ImHex and handling any error that may occur
|
||||||
|
* @param provider The provider to open
|
||||||
|
*/
|
||||||
|
void openProvider(std::shared_ptr<prv::Provider> provider);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -62,6 +62,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
void setMainWindowSize(u32 width, u32 height);
|
void setMainWindowSize(u32 width, u32 height);
|
||||||
void setMainDockSpaceId(ImGuiID id);
|
void setMainDockSpaceId(ImGuiID id);
|
||||||
void setMainWindowHandle(GLFWwindow *window);
|
void setMainWindowHandle(GLFWwindow *window);
|
||||||
|
void setMainWindowFocusState(bool focused);
|
||||||
|
|
||||||
void setGlobalScale(float scale);
|
void setGlobalScale(float scale);
|
||||||
void setNativeScale(float scale);
|
void setNativeScale(float scale);
|
||||||
@@ -161,6 +162,12 @@ EXPORT_MODULE namespace hex {
|
|||||||
*/
|
*/
|
||||||
GLFWwindow* getMainWindowHandle();
|
GLFWwindow* getMainWindowHandle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the main window is currently focused
|
||||||
|
* @return Whether the main window is focused
|
||||||
|
*/
|
||||||
|
bool isMainWindowFocused();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks if borderless window mode is enabled currently
|
* @brief Checks if borderless window mode is enabled currently
|
||||||
* @return Whether borderless window mode is enabled
|
* @return Whether borderless window mode is enabled
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <hex/trace/exceptions.hpp>
|
||||||
|
|
||||||
EXPORT_MODULE namespace hex {
|
EXPORT_MODULE namespace hex {
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
class Task {
|
class Task {
|
||||||
public:
|
public:
|
||||||
Task() = default;
|
Task() = default;
|
||||||
Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);
|
Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);
|
||||||
|
|
||||||
Task(const Task&) = delete;
|
Task(const Task&) = delete;
|
||||||
Task(Task &&other) noexcept;
|
Task(Task &&other) noexcept;
|
||||||
@@ -94,7 +95,16 @@ EXPORT_MODULE namespace hex {
|
|||||||
std::atomic_flag m_hadException;
|
std::atomic_flag m_hadException;
|
||||||
std::string m_exceptionMessage;
|
std::string m_exceptionMessage;
|
||||||
|
|
||||||
struct TaskInterruptor { virtual ~TaskInterruptor() = default; };
|
struct TaskInterruptor: public std::exception {
|
||||||
|
TaskInterruptor() {
|
||||||
|
trace::disableExceptionCaptureForCurrentThread();
|
||||||
|
}
|
||||||
|
virtual ~TaskInterruptor() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] const char* what() const noexcept override {
|
||||||
|
return "Task Interrupted";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
friend class TaskHolder;
|
friend class TaskHolder;
|
||||||
friend class TaskManager;
|
friend class TaskManager;
|
||||||
@@ -242,6 +252,8 @@ EXPORT_MODULE namespace hex {
|
|||||||
static const std::list<std::shared_ptr<Task>>& getRunningTasks();
|
static const std::list<std::shared_ptr<Task>>& getRunningTasks();
|
||||||
static void runDeferredCalls();
|
static void runDeferredCalls();
|
||||||
|
|
||||||
|
static void addTaskCompletionCallback(const std::function<void(Task&)>& function);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);
|
static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include <hex/ui/imgui_imhex_extensions.h>
|
#include <hex/ui/imgui_imhex_extensions.h>
|
||||||
|
|
||||||
|
struct ImRect;
|
||||||
|
|
||||||
EXPORT_MODULE namespace hex {
|
EXPORT_MODULE namespace hex {
|
||||||
|
|
||||||
class TutorialManager {
|
class TutorialManager {
|
||||||
@@ -22,6 +24,8 @@ EXPORT_MODULE namespace hex {
|
|||||||
Right = 8
|
Right = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using DrawFunction = std::function<void()>;
|
||||||
|
|
||||||
struct Tutorial {
|
struct Tutorial {
|
||||||
Tutorial() = delete;
|
Tutorial() = delete;
|
||||||
Tutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) :
|
Tutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) :
|
||||||
@@ -101,6 +105,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
std::vector<Highlight> m_highlights;
|
std::vector<Highlight> m_highlights;
|
||||||
std::optional<Message> m_message;
|
std::optional<Message> m_message;
|
||||||
std::function<void()> m_onAppear, m_onComplete;
|
std::function<void()> m_onAppear, m_onComplete;
|
||||||
|
DrawFunction m_drawFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
Step& addStep();
|
Step& addStep();
|
||||||
@@ -146,6 +151,7 @@ EXPORT_MODULE namespace hex {
|
|||||||
* @param unlocalizedName Name of tutorial to start
|
* @param unlocalizedName Name of tutorial to start
|
||||||
*/
|
*/
|
||||||
static void startTutorial(const UnlocalizedString &unlocalizedName);
|
static void startTutorial(const UnlocalizedString &unlocalizedName);
|
||||||
|
static void stopCurrentTutorial();
|
||||||
|
|
||||||
static void startHelpHover();
|
static void startHelpHover();
|
||||||
static void addInteractiveHelpText(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, UnlocalizedString unlocalizedString);
|
static void addInteractiveHelpText(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, UnlocalizedString unlocalizedString);
|
||||||
@@ -166,6 +172,10 @@ EXPORT_MODULE namespace hex {
|
|||||||
*/
|
*/
|
||||||
static void reset();
|
static void reset();
|
||||||
|
|
||||||
|
static void setRenderer(std::function<DrawFunction(const std::string &)> renderer);
|
||||||
|
|
||||||
|
static void postElementRendered(ImGuiID id, const ImRect &boundingBox);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TutorialManager() = delete;
|
TutorialManager() = delete;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
@@ -49,9 +50,15 @@ namespace hex::dp {
|
|||||||
virtual void store(nlohmann::json &j) const { std::ignore = j; }
|
virtual void store(nlohmann::json &j) const { std::ignore = j; }
|
||||||
virtual void load(const nlohmann::json &j) { std::ignore = j; }
|
virtual void load(const nlohmann::json &j) { std::ignore = j; }
|
||||||
|
|
||||||
struct NodeError {
|
struct NodeError: public std::exception {
|
||||||
Node *node;
|
Node *node;
|
||||||
std::string message;
|
std::string message;
|
||||||
|
|
||||||
|
NodeError(Node *node, std::string message) : node(node), message(std::move(message)) {}
|
||||||
|
|
||||||
|
[[nodiscard]] const char* what() const noexcept override {
|
||||||
|
return this->message.c_str();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void resetOutputData() {
|
void resetOutputData() {
|
||||||
@@ -102,7 +109,7 @@ namespace hex::dp {
|
|||||||
void unmarkInputProcessed(u32 index);
|
void unmarkInputProcessed(u32 index);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
[[noreturn]] void throwNodeError(const std::string &message);
|
[[noreturn]] void throwNodeError(const std::string &msg);
|
||||||
|
|
||||||
void setOverlayData(u64 address, const std::vector<u8> &data);
|
void setOverlayData(u64 address, const std::vector<u8> &data);
|
||||||
void setAttributes(std::vector<Attribute> attributes);
|
void setAttributes(std::vector<Attribute> attributes);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <hex/api/imhex_api/system.hpp>
|
#include <hex/api/imhex_api/system.hpp>
|
||||||
|
#include <hex/helpers/logger.hpp>
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
@@ -9,6 +10,9 @@ namespace hex {
|
|||||||
class AutoResetBase {
|
class AutoResetBase {
|
||||||
public:
|
public:
|
||||||
virtual ~AutoResetBase() = default;
|
virtual ~AutoResetBase() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend void ImHexApi::System::impl::cleanup();
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19,16 +23,20 @@ namespace hex {
|
|||||||
public:
|
public:
|
||||||
using Type = T;
|
using Type = T;
|
||||||
|
|
||||||
AutoReset() {
|
AutoReset() noexcept {
|
||||||
ImHexApi::System::impl::addAutoResetObject(this);
|
try {
|
||||||
|
ImHexApi::System::impl::addAutoResetObject(this);
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
log::error("Failed to register AutoReset object: {}", e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoReset(const T &value) : AutoReset() {
|
explicit(false) AutoReset(const T &value) : AutoReset() {
|
||||||
m_value = value;
|
m_value = value;
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoReset(T &&value) noexcept : AutoReset() {
|
explicit(false) AutoReset(T &&value) noexcept : AutoReset() {
|
||||||
m_value = std::move(value);
|
m_value = std::move(value);
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
}
|
}
|
||||||
@@ -61,29 +69,27 @@ namespace hex {
|
|||||||
return m_value;
|
return m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
T& operator=(const T &value) {
|
AutoReset& operator=(const T &value) {
|
||||||
m_value = value;
|
m_value = value;
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
return m_value;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
T& operator=(T &&value) noexcept {
|
AutoReset& operator=(T &&value) noexcept {
|
||||||
m_value = std::move(value);
|
m_value = std::move(value);
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
return m_value;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValid() const {
|
[[nodiscard]] bool isValid() const {
|
||||||
return m_valid;
|
return m_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend void ImHexApi::System::impl::cleanup();
|
|
||||||
|
|
||||||
void reset() override {
|
void reset() override {
|
||||||
if constexpr (requires { m_value.reset(); }) {
|
if constexpr (requires(T t) { t.reset(); }) {
|
||||||
m_value.reset();
|
m_value.reset();
|
||||||
} else if constexpr (requires { m_value.clear(); }) {
|
} else if constexpr (requires(T t) { t.clear(); }) {
|
||||||
m_value.clear();
|
m_value.clear();
|
||||||
} else if constexpr (std::is_pointer_v<T>) {
|
} else if constexpr (std::is_pointer_v<T>) {
|
||||||
m_value = nullptr; // cppcheck-suppress nullPointer
|
m_value = nullptr; // cppcheck-suppress nullPointer
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
static_assert(false, "Debug variables are only intended for use during development.");
|
static_assert(false, "Debug variables are only intended for use during development.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace hex::trace {
|
||||||
|
struct StackTraceResult;
|
||||||
|
}
|
||||||
|
|
||||||
namespace hex::dbg {
|
namespace hex::dbg {
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
@@ -47,4 +51,6 @@ namespace hex::dbg {
|
|||||||
bool debugModeEnabled();
|
bool debugModeEnabled();
|
||||||
void setDebugModeEnabled(bool enabled);
|
void setDebugModeEnabled(bool enabled);
|
||||||
|
|
||||||
|
void printStackTrace(const trace::StackTraceResult &stackTrace);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,9 @@ namespace hex::menu {
|
|||||||
bool beginMenu(const char *label, bool enabled = true);
|
bool beginMenu(const char *label, bool enabled = true);
|
||||||
void endMenu();
|
void endMenu();
|
||||||
|
|
||||||
|
bool beginTaskBarMenu();
|
||||||
|
void endTaskBarMenu();
|
||||||
|
|
||||||
bool beginMenuEx(const char* label, const char* icon, bool enabled = true);
|
bool beginMenuEx(const char* label, const char* icon, bool enabled = true);
|
||||||
|
|
||||||
bool menuItem(const char *label, const Shortcut &shortcut = Shortcut::None, bool selected = false, bool enabled = true);
|
bool menuItem(const char *label, const Shortcut &shortcut = Shortcut::None, bool selected = false, bool enabled = true);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include <opengl_support.h>
|
#include <opengl_support.h>
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
@@ -935,7 +936,7 @@ namespace hex::gl {
|
|||||||
void attachTexture(const Texture &texture) const;
|
void attachTexture(const Texture &texture) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint m_frameBuffer, m_renderBuffer;
|
GLuint m_frameBuffer = 0, m_renderBuffer = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AxesVectors {
|
class AxesVectors {
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ namespace hex {
|
|||||||
|
|
||||||
void startProgram(const std::vector<std::string> &command);
|
void startProgram(const std::vector<std::string> &command);
|
||||||
int executeCommand(const std::string &command);
|
int executeCommand(const std::string &command);
|
||||||
|
std::optional<std::string> executeCommandWithOutput(const std::string &command);
|
||||||
|
void executeCommandDetach(const std::string &command);
|
||||||
void openWebpage(std::string url);
|
void openWebpage(std::string url);
|
||||||
|
|
||||||
extern "C" void registerFont(const char *fontName, const char *fontPath);
|
extern "C" void registerFont(const char *fontName, const char *fontPath);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
void macosInstallEventListener();
|
void macosInstallEventListener();
|
||||||
|
|
||||||
void toastMessageMacos(const char *title, const char *message);
|
void toastMessageMacos(const char *title, const char *message);
|
||||||
|
void macosSetupDockMenu(void);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
15
lib/libimhex/include/hex/mcp/client.hpp
Normal file
15
lib/libimhex/include/hex/mcp/client.hpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace hex::mcp {
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
public:
|
||||||
|
Client() = default;
|
||||||
|
~Client() = default;
|
||||||
|
|
||||||
|
int run(std::istream &input, std::ostream &output);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
123
lib/libimhex/include/hex/mcp/server.hpp
Normal file
123
lib/libimhex/include/hex/mcp/server.hpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hex.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <wolv/net/socket_server.hpp>
|
||||||
|
|
||||||
|
namespace hex::mcp {
|
||||||
|
|
||||||
|
class JsonRpc {
|
||||||
|
public:
|
||||||
|
explicit JsonRpc(std::string request) : m_request(std::move(request)){ }
|
||||||
|
|
||||||
|
struct MethodNotFoundException : std::exception {};
|
||||||
|
struct InvalidParametersException : std::exception {};
|
||||||
|
|
||||||
|
enum class ErrorCode: i16 {
|
||||||
|
ParseError = -32700,
|
||||||
|
InvalidRequest = -32600,
|
||||||
|
MethodNotFound = -32601,
|
||||||
|
InvalidParams = -32602,
|
||||||
|
InternalError = -32603,
|
||||||
|
};
|
||||||
|
|
||||||
|
using Callback = std::function<nlohmann::json(const std::string &method, const nlohmann::json ¶ms)>;
|
||||||
|
std::optional<std::string> execute(const Callback &callback);
|
||||||
|
void setError(ErrorCode code, std::string message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<nlohmann::json> handleMessage(const nlohmann::json &request, const Callback &callback);
|
||||||
|
std::optional<nlohmann::json> handleBatchedMessages(const nlohmann::json &request, const Callback &callback);
|
||||||
|
|
||||||
|
nlohmann::json createDefaultMessage();
|
||||||
|
nlohmann::json createErrorMessage(ErrorCode code, const std::string &message);
|
||||||
|
nlohmann::json createResponseMessage(const nlohmann::json &result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_request;
|
||||||
|
std::optional<int> m_id;
|
||||||
|
|
||||||
|
struct Error {
|
||||||
|
ErrorCode code;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
std::optional<Error> m_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextContent {
|
||||||
|
std::string text;
|
||||||
|
|
||||||
|
operator nlohmann::json() const {
|
||||||
|
nlohmann::json result;
|
||||||
|
result["content"] = nlohmann::json::array({
|
||||||
|
nlohmann::json::object({
|
||||||
|
{ "type", "text" },
|
||||||
|
{ "text", text }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StructuredContent {
|
||||||
|
std::string text;
|
||||||
|
nlohmann::json data;
|
||||||
|
|
||||||
|
operator nlohmann::json() const {
|
||||||
|
nlohmann::json result;
|
||||||
|
result["content"] = nlohmann::json::array({
|
||||||
|
nlohmann::json::object({
|
||||||
|
{ "type", "text" },
|
||||||
|
{ "text", text }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
result["structuredContent"] = data;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
constexpr static auto McpInternalPort = 19743;
|
||||||
|
|
||||||
|
Server();
|
||||||
|
~Server();
|
||||||
|
|
||||||
|
void listen();
|
||||||
|
void shutdown();
|
||||||
|
void disconnect();
|
||||||
|
bool isConnected();
|
||||||
|
|
||||||
|
void addPrimitive(std::string type, std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json ¶ms)> function);
|
||||||
|
|
||||||
|
struct ClientInfo {
|
||||||
|
std::string name;
|
||||||
|
std::string version;
|
||||||
|
std::string protocolVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ClientInfo& getClientInfo() const {
|
||||||
|
return m_clientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nlohmann::json handleInitialize(const nlohmann::json ¶ms);
|
||||||
|
void handleNotifications(const std::string &method, const nlohmann::json ¶ms);
|
||||||
|
|
||||||
|
struct Primitive {
|
||||||
|
nlohmann::json capabilities;
|
||||||
|
std::function<nlohmann::json(const nlohmann::json ¶ms)> function;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, std::map<std::string, Primitive>> m_primitives;
|
||||||
|
|
||||||
|
wolv::net::SocketServer m_server;
|
||||||
|
bool m_connected = false;
|
||||||
|
ClientInfo m_clientInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ namespace hex::prv {
|
|||||||
CachedProvider(size_t cacheBlockSize = 4096, size_t maxBlocks = 1024);
|
CachedProvider(size_t cacheBlockSize = 4096, size_t maxBlocks = 1024);
|
||||||
~CachedProvider() override;
|
~CachedProvider() override;
|
||||||
|
|
||||||
bool open() override;
|
OpenResult open() override;
|
||||||
void close() override;
|
void close() override;
|
||||||
|
|
||||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace hex::prv {
|
|||||||
[[nodiscard]] bool isSavable() const override { return m_name.empty(); }
|
[[nodiscard]] bool isSavable() const override { return m_name.empty(); }
|
||||||
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
|
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
|
||||||
|
|
||||||
[[nodiscard]] bool open() override;
|
[[nodiscard]] OpenResult open() override;
|
||||||
void close() override { }
|
void close() override { }
|
||||||
|
|
||||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||||
|
|||||||
@@ -72,6 +72,21 @@ namespace hex::prv {
|
|||||||
[[nodiscard]] virtual std::vector<Description> getDataDescription() const = 0;
|
[[nodiscard]] virtual std::vector<Description> getDataDescription() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IProviderDataBackupable {
|
||||||
|
public:
|
||||||
|
explicit IProviderDataBackupable(Provider *provider);
|
||||||
|
virtual ~IProviderDataBackupable() = default;
|
||||||
|
|
||||||
|
void createBackupIfNeeded(const std::fs::path &inputFilePath);
|
||||||
|
private:
|
||||||
|
Provider *m_provider = nullptr;
|
||||||
|
bool m_backupCreated = false;
|
||||||
|
|
||||||
|
bool m_shouldCreateBackups = true;
|
||||||
|
u64 m_maxSize;
|
||||||
|
std::string m_backupExtension;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represent the data source for a tab in the UI
|
* @brief Represent the data source for a tab in the UI
|
||||||
*/
|
*/
|
||||||
@@ -79,6 +94,65 @@ namespace hex::prv {
|
|||||||
public:
|
public:
|
||||||
constexpr static u64 MaxPageSize = 0xFFFF'FFFF'FFFF'FFFF;
|
constexpr static u64 MaxPageSize = 0xFFFF'FFFF'FFFF'FFFF;
|
||||||
|
|
||||||
|
class OpenResult {
|
||||||
|
public:
|
||||||
|
OpenResult() : m_result(std::monostate{}) {}
|
||||||
|
|
||||||
|
[[nodiscard]] static OpenResult failure(std::string errorMessage) {
|
||||||
|
OpenResult result;
|
||||||
|
result.m_result = std::move(errorMessage);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static OpenResult warning(std::string warningMessage) {
|
||||||
|
OpenResult result;
|
||||||
|
result.m_result = std::move(warningMessage);
|
||||||
|
result.m_warning = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static OpenResult redirect(Provider *provider) {
|
||||||
|
OpenResult result;
|
||||||
|
result.m_result = provider;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isSuccess() const {
|
||||||
|
return std::holds_alternative<std::monostate>(m_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isFailure() const {
|
||||||
|
return std::holds_alternative<std::string>(m_result) && !m_warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isWarning() const {
|
||||||
|
return std::holds_alternative<std::string>(m_result) && m_warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isRedirecting() const {
|
||||||
|
return std::holds_alternative<Provider*>(m_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Provider* getRedirectProvider() const {
|
||||||
|
if (std::holds_alternative<Provider*>(m_result)) {
|
||||||
|
return std::get<Provider*>(m_result);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string_view getErrorMessage() const {
|
||||||
|
if (std::holds_alternative<std::string>(m_result)) {
|
||||||
|
return std::get<std::string>(m_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::variant<std::monostate, std::string, Provider*> m_result;
|
||||||
|
bool m_warning = false;
|
||||||
|
};
|
||||||
|
|
||||||
Provider();
|
Provider();
|
||||||
virtual ~Provider();
|
virtual ~Provider();
|
||||||
Provider(const Provider&) = delete;
|
Provider(const Provider&) = delete;
|
||||||
@@ -94,7 +168,7 @@ namespace hex::prv {
|
|||||||
* @note This is not related to the EventProviderOpened event
|
* @note This is not related to the EventProviderOpened event
|
||||||
* @return true if the provider was opened successfully, else false
|
* @return true if the provider was opened successfully, else false
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] virtual bool open() = 0;
|
[[nodiscard]] virtual OpenResult open() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes this provider
|
* @brief Closes this provider
|
||||||
@@ -262,9 +336,6 @@ namespace hex::prv {
|
|||||||
void skipLoadInterface() { m_skipLoadInterface = true; }
|
void skipLoadInterface() { m_skipLoadInterface = true; }
|
||||||
[[nodiscard]] bool shouldSkipLoadInterface() const { return m_skipLoadInterface; }
|
[[nodiscard]] bool shouldSkipLoadInterface() const { return m_skipLoadInterface; }
|
||||||
|
|
||||||
void setErrorMessage(const std::string &errorMessage) { m_errorMessage = errorMessage; }
|
|
||||||
[[nodiscard]] const std::string& getErrorMessage() const { return m_errorMessage; }
|
|
||||||
|
|
||||||
template<std::derived_from<undo::Operation> T>
|
template<std::derived_from<undo::Operation> T>
|
||||||
bool addUndoableOperation(auto && ... args) {
|
bool addUndoableOperation(auto && ... args) {
|
||||||
return m_undoRedoStack.add<T>(std::forward<decltype(args)...>(args)...);
|
return m_undoRedoStack.add<T>(std::forward<decltype(args)...>(args)...);
|
||||||
@@ -296,8 +367,6 @@ namespace hex::prv {
|
|||||||
*/
|
*/
|
||||||
bool m_skipLoadInterface = false;
|
bool m_skipLoadInterface = false;
|
||||||
|
|
||||||
std::string m_errorMessage = "Unspecified error";
|
|
||||||
|
|
||||||
u64 m_pageSize = MaxPageSize;
|
u64 m_pageSize = MaxPageSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace hex::prv {
|
namespace hex::prv {
|
||||||
@@ -40,6 +41,8 @@ namespace hex::prv::undo {
|
|||||||
|
|
||||||
bool add(std::unique_ptr<Operation> &&operation);
|
bool add(std::unique_ptr<Operation> &&operation);
|
||||||
|
|
||||||
|
static std::recursive_mutex& getMutex();
|
||||||
|
|
||||||
const std::vector<std::unique_ptr<Operation>> &getAppliedOperations() const {
|
const std::vector<std::unique_ptr<Operation>> &getAppliedOperations() const {
|
||||||
return m_undoStack;
|
return m_undoStack;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace hex::test {
|
|||||||
|
|
||||||
[[nodiscard]] UnlocalizedString getTypeName() const override { return "hex.test.provider.test"; }
|
[[nodiscard]] UnlocalizedString getTypeName() const override { return "hex.test.provider.test"; }
|
||||||
|
|
||||||
bool open() override { return true; }
|
OpenResult open() override { return {}; }
|
||||||
void close() override { }
|
void close() override { }
|
||||||
|
|
||||||
nlohmann::json storeSettings(nlohmann::json) const override { return {}; }
|
nlohmann::json storeSettings(nlohmann::json) const override { return {}; }
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ namespace ImGuiExt {
|
|||||||
bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0));
|
bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0));
|
||||||
bool DimmedIconToggle(const char *icon, bool *v);
|
bool DimmedIconToggle(const char *icon, bool *v);
|
||||||
bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v);
|
bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v);
|
||||||
|
bool DimmedArrowButton(const char *id, ImGuiDir dir, ImVec2 size = ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight()));
|
||||||
|
|
||||||
void TextOverlay(const char *text, ImVec2 pos, float maxWidth = -1);
|
void TextOverlay(const char *text, ImVec2 pos, float maxWidth = -1);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <hex/api/tutorial_manager.hpp>
|
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ namespace hex {
|
|||||||
* @brief Draws the view
|
* @brief Draws the view
|
||||||
* @note Do not override this method. Override drawContent() instead
|
* @note Do not override this method. Override drawContent() instead
|
||||||
*/
|
*/
|
||||||
virtual void draw() = 0;
|
virtual void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws the content of the view
|
* @brief Draws the content of the view
|
||||||
@@ -126,6 +125,7 @@ namespace hex {
|
|||||||
class Window;
|
class Window;
|
||||||
class Special;
|
class Special;
|
||||||
class Floating;
|
class Floating;
|
||||||
|
class Scrolling;
|
||||||
class Modal;
|
class Modal;
|
||||||
class FullScreen;
|
class FullScreen;
|
||||||
|
|
||||||
@@ -147,23 +147,24 @@ namespace hex {
|
|||||||
class View::Window : public View {
|
class View::Window : public View {
|
||||||
public:
|
public:
|
||||||
explicit Window(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
|
explicit Window(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
|
||||||
|
[[nodiscard]] ImGuiWindow *getFocusedSubWindow() const { return m_focusedSubWindow; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws help text for the view
|
* @brief Draws help text for the view
|
||||||
*/
|
*/
|
||||||
virtual void drawHelpText() = 0;
|
virtual void drawHelpText() = 0;
|
||||||
|
|
||||||
void draw() final {
|
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) override;
|
||||||
if (this->shouldDraw()) {
|
|
||||||
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
|
[[nodiscard]] virtual bool allowScroll() const {
|
||||||
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
|
return false;
|
||||||
if (ImGui::Begin(title.c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
|
|
||||||
TutorialManager::setLastItemInteractiveHelpPopup([this]{ this->drawHelpText(); });
|
|
||||||
this->drawContent();
|
|
||||||
}
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleFocusRestoration();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImGuiWindow *m_focusedSubWindow{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,12 +175,7 @@ namespace hex {
|
|||||||
public:
|
public:
|
||||||
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
|
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
|
||||||
|
|
||||||
void draw() final {
|
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||||
if (this->shouldDraw()) {
|
|
||||||
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
|
|
||||||
this->drawContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,10 +185,25 @@ namespace hex {
|
|||||||
public:
|
public:
|
||||||
explicit Floating(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {}
|
explicit Floating(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {}
|
||||||
|
|
||||||
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoDocking; }
|
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||||
|
|
||||||
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
|
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A view that draws all its content at once without any scrolling being done by the window itself
|
||||||
|
*/
|
||||||
|
class View::Scrolling : public View::Window {
|
||||||
|
public:
|
||||||
|
explicit Scrolling(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {}
|
||||||
|
|
||||||
|
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||||
|
|
||||||
|
[[nodiscard]] bool allowScroll() const final {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A view that draws a modal window. The window will always be drawn on top and will block input to other windows
|
* @brief A view that draws a modal window. The window will always be drawn on top and will block input to other windows
|
||||||
*/
|
*/
|
||||||
@@ -200,24 +211,7 @@ namespace hex {
|
|||||||
public:
|
public:
|
||||||
explicit Modal(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
|
explicit Modal(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
|
||||||
|
|
||||||
void draw() final {
|
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||||
if (this->shouldDraw()) {
|
|
||||||
if (this->getWindowOpenState())
|
|
||||||
ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str());
|
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
|
||||||
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
|
|
||||||
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
|
|
||||||
if (ImGui::BeginPopupModal(title.c_str(), this->hasCloseButton() ? &this->getWindowOpenState() : nullptr, ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
|
|
||||||
this->drawContent();
|
|
||||||
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
|
|
||||||
this->getWindowOpenState() = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] virtual bool hasCloseButton() const { return true; }
|
[[nodiscard]] virtual bool hasCloseButton() const { return true; }
|
||||||
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
|
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
|
||||||
@@ -227,10 +221,7 @@ namespace hex {
|
|||||||
public:
|
public:
|
||||||
explicit FullScreen() : View("FullScreen", "") {}
|
explicit FullScreen() : View("FullScreen", "") {}
|
||||||
|
|
||||||
void draw() final {
|
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||||
this->drawContent();
|
|
||||||
this->drawAlwaysVisibleContent();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,8 +264,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.empty())
|
if (json.empty()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
#if defined(OS_WEB)
|
#if defined(OS_WEB)
|
||||||
auto data = json.dump();
|
auto data = json.dump();
|
||||||
|
|||||||
@@ -594,6 +594,12 @@ namespace hex {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Spacer::draw(const std::string& name) {
|
||||||
|
std::ignore = name;
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,22 +630,22 @@ namespace hex {
|
|||||||
void add(Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) {
|
void add(Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) {
|
||||||
log::debug("Registered new command palette command: {}", command);
|
log::debug("Registered new command palette command: {}", command);
|
||||||
|
|
||||||
impl::s_entries->push_back(impl::Entry { type, command, unlocalizedDescription, displayCallback, executeCallback });
|
impl::s_entries->push_back(impl::Entry { .type=type, .command=command, .unlocalizedDescription=unlocalizedDescription, .displayCallback=displayCallback, .executeCallback=executeCallback });
|
||||||
}
|
}
|
||||||
|
|
||||||
void addHandler(Type type, const std::string &command, const impl::QueryCallback &queryCallback, const impl::DisplayCallback &displayCallback) {
|
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);
|
log::debug("Registered new command palette command handler: {}", command);
|
||||||
|
|
||||||
impl::s_handlers->push_back(impl::Handler { type, command, queryCallback, displayCallback });
|
impl::s_handlers->push_back(impl::Handler { .type=type, .command=command, .queryCallback=queryCallback, .displayCallback=displayCallback });
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback) {
|
void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback) {
|
||||||
impl::s_displayedContent = impl::ContentDisplay { true, displayCallback };
|
impl::s_displayedContent = impl::ContentDisplay { .showSearchBox=true, .callback=displayCallback };
|
||||||
}
|
}
|
||||||
|
|
||||||
void openWithContent(const impl::ContentDisplayCallback &displayCallback) {
|
void openWithContent(const impl::ContentDisplayCallback &displayCallback) {
|
||||||
RequestOpenCommandPalette::post();
|
RequestOpenCommandPalette::post();
|
||||||
impl::s_displayedContent = impl::ContentDisplay { false, displayCallback };
|
impl::s_displayedContent = impl::ContentDisplay { .showSearchBox=false, .callback=displayCallback };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -777,12 +783,12 @@ namespace hex {
|
|||||||
|
|
||||||
void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) {
|
void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) {
|
||||||
log::debug("Registered new pattern visualizer function: {}", name);
|
log::debug("Registered new pattern visualizer function: {}", name);
|
||||||
(*impl::s_visualizers)[name] = impl::Visualizer { parameterCount, function };
|
(*impl::s_visualizers)[name] = impl::Visualizer { .parameterCount=parameterCount, .callback=function };
|
||||||
}
|
}
|
||||||
|
|
||||||
void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) {
|
void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) {
|
||||||
log::debug("Registered new inline pattern visualizer function: {}", name);
|
log::debug("Registered new inline pattern visualizer function: {}", name);
|
||||||
(*impl::s_inlineVisualizers)[name] = impl::Visualizer { parameterCount, function };
|
(*impl::s_inlineVisualizers)[name] = impl::Visualizer { .parameterCount=parameterCount, .callback=function };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -848,7 +854,7 @@ namespace hex {
|
|||||||
void add(const UnlocalizedString &unlocalizedName, const char *icon, const impl::Callback &function) {
|
void add(const UnlocalizedString &unlocalizedName, const char *icon, const impl::Callback &function) {
|
||||||
log::debug("Registered new tool: {}", unlocalizedName.get());
|
log::debug("Registered new tool: {}", unlocalizedName.get());
|
||||||
|
|
||||||
impl::s_tools->emplace_back(impl::Entry { unlocalizedName, icon, function });
|
impl::s_tools->emplace_back(impl::Entry { .unlocalizedName=unlocalizedName, .icon=icon, .function=function });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -864,6 +870,18 @@ namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace EditWidget {
|
||||||
|
std::optional<std::vector<u8>> TextInput::draw(std::string &value, std::endian endian) {
|
||||||
|
if (ImGui::InputText("##InspectorLineEditing", value,
|
||||||
|
ImGuiInputTextFlags_EnterReturnsTrue |
|
||||||
|
ImGuiInputTextFlags_AutoSelectAll)) {
|
||||||
|
return getBytes(value, endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
|
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
|
||||||
log::debug("Registered new data inspector format: {}", unlocalizedName.get());
|
log::debug("Registered new data inspector format: {}", unlocalizedName.get());
|
||||||
|
|
||||||
@@ -991,7 +1009,7 @@ namespace hex {
|
|||||||
coloredIcon.color = ImGuiCustomCol_ToolbarGray;
|
coloredIcon.color = ImGuiCustomCol_ToolbarGray;
|
||||||
|
|
||||||
impl::s_menuItems->insert({
|
impl::s_menuItems->insert({
|
||||||
priority, impl::MenuItem { unlocalizedMainMenuNames, coloredIcon, shortcut, view, function, enabledCallback, selectedCallback, -1 }
|
priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=coloredIcon, .shortcut=shortcut, .view=view, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=selectedCallback, .toolbarIndex=-1 }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (shortcut != Shortcut::None) {
|
if (shortcut != Shortcut::None) {
|
||||||
@@ -1015,14 +1033,23 @@ namespace hex {
|
|||||||
|
|
||||||
unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue);
|
unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue);
|
||||||
impl::s_menuItems->insert({
|
impl::s_menuItems->insert({
|
||||||
priority, impl::MenuItem { unlocalizedMainMenuNames, icon, showOnWelcomeScreen ? Shortcut({ ShowOnWelcomeScreen }) : Shortcut::None, view, function, enabledCallback, []{ return false; }, -1 }
|
priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=icon, .shortcut=showOnWelcomeScreen ? Shortcut({ ShowOnWelcomeScreen }) : Shortcut::None, .view=view, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, View *view) {
|
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, View *view) {
|
||||||
unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue);
|
unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue);
|
||||||
impl::s_menuItems->insert({
|
impl::s_menuItems->insert({
|
||||||
priority, impl::MenuItem { unlocalizedMainMenuNames, "", Shortcut::None, view, []{}, []{ return true; }, []{ return false; }, -1 }
|
priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon="", .shortcut=Shortcut::None, .view=view, .callback=[]{}, .enabledCallback=[]{ return true; }, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void addTaskBarMenuItem(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) {
|
||||||
|
log::debug("Added new taskbar menu item to menu {} ", unlocalizedMainMenuNames[0].get());
|
||||||
|
|
||||||
|
unlocalizedMainMenuNames.insert(unlocalizedMainMenuNames.begin(), impl::TaskBarMenuValue);
|
||||||
|
impl::s_menuItems->insert({
|
||||||
|
priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon="", .shortcut=Shortcut::None, .view=nullptr, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1038,13 +1065,13 @@ namespace hex {
|
|||||||
impl::s_toolbarItems->push_back(function);
|
impl::s_toolbarItems->push_back(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addMenuItemToToolbar(const UnlocalizedString& unlocalizedName, ImGuiCustomCol color) {
|
void addMenuItemToToolbar(const std::vector<UnlocalizedString>& unlocalizedNames, ImGuiCustomCol color) {
|
||||||
const auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) {
|
const auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) {
|
||||||
return a.second.toolbarIndex < b.second.toolbarIndex;
|
return a.second.toolbarIndex < b.second.toolbarIndex;
|
||||||
})->second.toolbarIndex;
|
})->second.toolbarIndex;
|
||||||
|
|
||||||
for (auto &[priority, menuItem] : *impl::s_menuItems) {
|
for (auto &[priority, menuItem] : *impl::s_menuItems) {
|
||||||
if (menuItem.unlocalizedNames.back() == unlocalizedName) {
|
if (menuItem.unlocalizedNames == unlocalizedNames) {
|
||||||
menuItem.toolbarIndex = maxIndex + 1;
|
menuItem.toolbarIndex = maxIndex + 1;
|
||||||
menuItem.icon.color = color;
|
menuItem.icon.color = color;
|
||||||
updateToolbarItems();
|
updateToolbarItems();
|
||||||
@@ -1096,18 +1123,18 @@ namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ContentRegistry::Provider {
|
|
||||||
|
|
||||||
namespace impl {
|
namespace ContentRegistry::Provider::impl {
|
||||||
|
|
||||||
void add(const std::string &typeName, ProviderCreationFunction creationFunction) {
|
void add(const std::string &typeName, ProviderCreationFunction creationFunction) {
|
||||||
(void)RequestCreateProvider::subscribe([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, prv::Provider **provider) {
|
(void)RequestCreateProvider::subscribe([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, std::shared_ptr<prv::Provider> *provider) {
|
||||||
if (name != expectedName) return;
|
if (name != expectedName) return;
|
||||||
|
|
||||||
auto newProvider = creationFunction();
|
auto newProvider = creationFunction();
|
||||||
|
|
||||||
if (provider != nullptr) {
|
if (provider != nullptr) {
|
||||||
*provider = newProvider.get();
|
*provider = newProvider;
|
||||||
ImHexApi::Provider::add(std::move(newProvider), skipLoadInterface, selectProvider);
|
ImHexApi::Provider::add(std::move(newProvider), skipLoadInterface, selectProvider);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1127,7 +1154,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ContentRegistry::DataFormatter {
|
namespace ContentRegistry::DataFormatter {
|
||||||
|
|
||||||
@@ -1283,47 +1310,42 @@ namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ContentRegistry::Diffing {
|
|
||||||
|
|
||||||
namespace impl {
|
namespace ContentRegistry::Diffing::impl {
|
||||||
|
|
||||||
static AutoReset<std::vector<std::unique_ptr<Algorithm>>> s_algorithms;
|
static AutoReset<std::vector<std::unique_ptr<Algorithm>>> s_algorithms;
|
||||||
const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms() {
|
const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms() {
|
||||||
return *s_algorithms;
|
return *s_algorithms;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addAlgorithm(std::unique_ptr<Algorithm> &&hash) {
|
|
||||||
s_algorithms->emplace_back(std::move(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void addAlgorithm(std::unique_ptr<Algorithm> &&hash) {
|
||||||
|
s_algorithms->emplace_back(std::move(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ContentRegistry::Hashes {
|
|
||||||
|
|
||||||
namespace impl {
|
namespace ContentRegistry::Hashes::impl {
|
||||||
|
|
||||||
static AutoReset<std::vector<std::unique_ptr<Hash>>> s_hashes;
|
static AutoReset<std::vector<std::unique_ptr<Hash>>> s_hashes;
|
||||||
const std::vector<std::unique_ptr<Hash>>& getHashes() {
|
const std::vector<std::unique_ptr<Hash>>& getHashes() {
|
||||||
return *s_hashes;
|
return *s_hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(std::unique_ptr<Hash> &&hash) {
|
|
||||||
s_hashes->emplace_back(std::move(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void add(std::unique_ptr<Hash> &&hash) {
|
||||||
|
s_hashes->emplace_back(std::move(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace ContentRegistry::BackgroundServices {
|
namespace ContentRegistry::BackgroundServices {
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
|
||||||
class Service {
|
class Service {
|
||||||
public:
|
public:
|
||||||
Service(const UnlocalizedString &unlocalizedName, std::jthread thread) : m_unlocalizedName(std::move(unlocalizedName)), m_thread(std::move(thread)) { }
|
Service(UnlocalizedString unlocalizedName, std::jthread thread) : m_unlocalizedName(std::move(unlocalizedName)), m_thread(std::move(thread)) { }
|
||||||
Service(const Service&) = delete;
|
Service(const Service&) = delete;
|
||||||
Service(Service &&) = default;
|
Service(Service &&) = default;
|
||||||
~Service() {
|
~Service() {
|
||||||
@@ -1395,6 +1417,40 @@ namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ContentRegistry::MCP {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
std::unique_ptr<mcp::Server>& getMcpServerInstance() {
|
||||||
|
static std::unique_ptr<mcp::Server> server;
|
||||||
|
|
||||||
|
if (server == nullptr)
|
||||||
|
server = std::make_unique<mcp::Server>();
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool s_mcpEnabled = false;
|
||||||
|
void setEnabled(bool enabled) {
|
||||||
|
s_mcpEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return impl::s_mcpEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isConnected() {
|
||||||
|
return impl::getMcpServerInstance()->isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerTool(std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json ¶ms)> function) {
|
||||||
|
impl::getMcpServerInstance()->addPrimitive("tools", capabilities, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace ContentRegistry::Experiments {
|
namespace ContentRegistry::Experiments {
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
@@ -1488,22 +1544,19 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace ContentRegistry::Disassemblers {
|
namespace ContentRegistry::Disassemblers::impl {
|
||||||
|
|
||||||
namespace impl {
|
static AutoReset<std::map<std::string, impl::CreatorFunction>> s_architectures;
|
||||||
|
|
||||||
static AutoReset<std::map<std::string, impl::CreatorFunction>> s_architectures;
|
void addArchitectureCreator(impl::CreatorFunction function) {
|
||||||
|
const auto arch = function();
|
||||||
void addArchitectureCreator(impl::CreatorFunction function) {
|
(*s_architectures)[arch->getName()] = std::move(function);
|
||||||
const auto arch = function();
|
}
|
||||||
(*s_architectures)[arch->getName()] = std::move(function);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::map<std::string, impl::CreatorFunction>& getArchitectures() {
|
|
||||||
return *s_architectures;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const std::map<std::string, impl::CreatorFunction>& getArchitectures() {
|
||||||
|
return *s_architectures;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <hex/api/event_manager.hpp>
|
#include <hex/api/event_manager.hpp>
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
@@ -35,7 +36,7 @@ namespace hex {
|
|||||||
|
|
||||||
void EventManager::unsubscribe(void *token, impl::EventId id) {
|
void EventManager::unsubscribe(void *token, impl::EventId id) {
|
||||||
auto &tokenStore = getTokenStore();
|
auto &tokenStore = getTokenStore();
|
||||||
auto iter = std::find_if(tokenStore.begin(), tokenStore.end(), [&](auto &item) {
|
auto iter = std::ranges::find_if(tokenStore, [&](auto &item) {
|
||||||
return item.first == token && item.second->first == id;
|
return item.first == token && item.second->first == id;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,10 @@
|
|||||||
|
|
||||||
#if defined(OS_WEB)
|
#if defined(OS_WEB)
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
|
#elif defined(OS_MACOS)
|
||||||
|
extern "C" {
|
||||||
|
void macosRegisterFont(const unsigned char *data, size_t size);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
@@ -257,7 +261,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setSelection(u64 address, size_t size, prv::Provider *provider) {
|
void setSelection(u64 address, size_t size, prv::Provider *provider) {
|
||||||
setSelection({ { address, size }, provider == nullptr ? Provider::get() : provider });
|
setSelection({ { .address=address, .size=size }, provider == nullptr ? Provider::get() : provider });
|
||||||
}
|
}
|
||||||
|
|
||||||
void addVirtualFile(const std::string &path, std::vector<u8> data, Region region) {
|
void addVirtualFile(const std::string &path, std::vector<u8> data, Region region) {
|
||||||
@@ -281,7 +285,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 add(u64 address, size_t size, const std::string &name, const std::string &comment, u32 color) {
|
u64 add(u64 address, size_t size, const std::string &name, const std::string &comment, u32 color) {
|
||||||
return add(Region { address, size }, name, comment, color);
|
return add(Region { .address=address, .size=size }, name, comment, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(u64 id) {
|
void remove(u64 id) {
|
||||||
@@ -294,8 +298,8 @@ namespace hex {
|
|||||||
namespace ImHexApi::Provider {
|
namespace ImHexApi::Provider {
|
||||||
|
|
||||||
static i64 s_currentProvider = -1;
|
static i64 s_currentProvider = -1;
|
||||||
static AutoReset<std::vector<std::unique_ptr<prv::Provider>>> s_providers;
|
static AutoReset<std::vector<std::shared_ptr<prv::Provider>>> s_providers;
|
||||||
static AutoReset<std::map<prv::Provider*, std::unique_ptr<prv::Provider>>> s_providersToRemove;
|
static AutoReset<std::map<prv::Provider*, std::shared_ptr<prv::Provider>>> s_providersToRemove;
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
|
||||||
@@ -382,7 +386,7 @@ namespace hex {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface, bool select) {
|
void add(std::shared_ptr<prv::Provider> &&provider, bool skipLoadInterface, bool select) {
|
||||||
std::scoped_lock lock(impl::s_providerMutex);
|
std::scoped_lock lock(impl::s_providerMutex);
|
||||||
|
|
||||||
if (TaskManager::getRunningTaskCount() > 0)
|
if (TaskManager::getRunningTaskCount() > 0)
|
||||||
@@ -391,7 +395,7 @@ namespace hex {
|
|||||||
if (skipLoadInterface)
|
if (skipLoadInterface)
|
||||||
provider->skipLoadInterface();
|
provider->skipLoadInterface();
|
||||||
|
|
||||||
EventProviderCreated::post(provider.get());
|
EventProviderCreated::post(provider);
|
||||||
s_providers->emplace_back(std::move(provider));
|
s_providers->emplace_back(std::move(provider));
|
||||||
|
|
||||||
if (select || s_providers->size() == 1)
|
if (select || s_providers->size() == 1)
|
||||||
@@ -491,13 +495,17 @@ namespace hex {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {
|
std::shared_ptr<prv::Provider> createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {
|
||||||
prv::Provider* result = nullptr;
|
std::shared_ptr<prv::Provider> result = nullptr;
|
||||||
RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result);
|
RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openProvider(std::shared_ptr<prv::Provider> provider) {
|
||||||
|
RequestOpenProvider::post(provider);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ImHexApi::System {
|
namespace ImHexApi::System {
|
||||||
@@ -530,6 +538,11 @@ namespace hex {
|
|||||||
s_mainWindowHandle = window;
|
s_mainWindowHandle = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool s_mainWindowFocused = false;
|
||||||
|
void setMainWindowFocusState(bool focused) {
|
||||||
|
s_mainWindowFocused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static float s_globalScale = 1.0;
|
static float s_globalScale = 1.0;
|
||||||
void setGlobalScale(float scale) {
|
void setGlobalScale(float scale) {
|
||||||
@@ -667,13 +680,15 @@ namespace hex {
|
|||||||
if (!sessionType.has_value() || sessionType == "x11")
|
if (!sessionType.has_value() || sessionType == "x11")
|
||||||
return 1.0F;
|
return 1.0F;
|
||||||
else {
|
else {
|
||||||
float xScale = 0, yScale = 0;
|
int windowW, windowH;
|
||||||
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xScale, &yScale);
|
int displayW, displayH;
|
||||||
|
glfwGetWindowSize(getMainWindowHandle(), &windowW, &windowH);
|
||||||
|
glfwGetFramebufferSize(getMainWindowHandle(), &displayW, &displayH);
|
||||||
|
|
||||||
return std::midpoint(xScale, yScale);
|
return (windowW > 0) ? float(displayW) / windowW : 1.0f;
|
||||||
}
|
}
|
||||||
#elif defined(OS_WEB)
|
#elif defined(OS_WEB)
|
||||||
return 1.0F;
|
return emscripten_get_device_pixel_ratio();
|
||||||
#else
|
#else
|
||||||
return 1.0F;
|
return 1.0F;
|
||||||
#endif
|
#endif
|
||||||
@@ -700,6 +715,10 @@ namespace hex {
|
|||||||
return impl::s_mainWindowHandle;
|
return impl::s_mainWindowHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isMainWindowFocused() {
|
||||||
|
return impl::s_mainWindowFocused;
|
||||||
|
}
|
||||||
|
|
||||||
bool isBorderlessWindowModeEnabled() {
|
bool isBorderlessWindowModeEnabled() {
|
||||||
return impl::s_borderlessWindowMode;
|
return impl::s_borderlessWindowMode;
|
||||||
}
|
}
|
||||||
@@ -896,7 +915,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { { name, version } };
|
return { { .name=name, .version=version } };
|
||||||
}
|
}
|
||||||
|
|
||||||
const SemanticVersion& getImHexVersion() {
|
const SemanticVersion& getImHexVersion() {
|
||||||
@@ -1201,6 +1220,10 @@ namespace hex {
|
|||||||
offset,
|
offset,
|
||||||
fontSizeMultiplier
|
fontSizeMultiplier
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#if defined(OS_MACOS)
|
||||||
|
macosRegisterFont(data.data(), data.size_bytes());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerFont(const Font& font) {
|
void registerFont(const Font& font) {
|
||||||
@@ -1212,7 +1235,7 @@ namespace hex {
|
|||||||
|
|
||||||
if (it == impl::s_fontDefinitions->end()) {
|
if (it == impl::s_fontDefinitions->end()) {
|
||||||
const auto defaultFont = ImGui::GetDefaultFont();
|
const auto defaultFont = ImGui::GetDefaultFont();
|
||||||
return { defaultFont, defaultFont, defaultFont };
|
return { .regular=defaultFont, .bold=defaultFont, .italic=defaultFont };
|
||||||
} else
|
} else
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LayoutManager::lockLayout(bool locked) {
|
void LayoutManager::lockLayout(bool locked) {
|
||||||
log::info("Layout {}", locked ? "locked" : "unlocked");
|
log::debug("Layout {}", locked ? "locked" : "unlocked");
|
||||||
s_layoutLocked = locked;
|
s_layoutLocked = locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ namespace hex {
|
|||||||
definition.fallbackLanguageId = item["fallback"].get<std::string>();
|
definition.fallbackLanguageId = item["fallback"].get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.contains("hidden") && item["hidden"].get<bool>() == true) {
|
if (item.contains("hidden") && item["hidden"].get<bool>()) {
|
||||||
definition.hidden = true;
|
definition.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto path = item["path"].get<std::string>();
|
const auto path = item["path"].get<std::string>();
|
||||||
|
|
||||||
definition.languageFilePaths.emplace_back(PathEntry{ path, callback });
|
definition.languageFilePaths.emplace_back(PathEntry{ .path=path, .callback=callback });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +104,21 @@ namespace hex {
|
|||||||
|
|
||||||
for (const auto &entry : json.items()) {
|
for (const auto &entry : json.items()) {
|
||||||
auto value = entry.value().get<std::string>();
|
auto value = entry.value().get<std::string>();
|
||||||
|
|
||||||
|
// Skip empty values
|
||||||
if (value.empty())
|
if (value.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Handle references to files
|
||||||
|
if (value.starts_with("#@")) {
|
||||||
|
try {
|
||||||
|
value = path.callback(value.substr(2));
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
log::error("Failed to load localization file reference '{}': {}", entry.key(), e.what());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
localizations.try_emplace(LangConst::hash(entry.key()), std::move(value));
|
localizations.try_emplace(LangConst::hash(entry.key()), std::move(value));
|
||||||
}
|
}
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
@@ -143,7 +155,7 @@ namespace hex {
|
|||||||
static AutoReset<std::unordered_map<std::size_t, std::string>> loadedLocalization;
|
static AutoReset<std::unordered_map<std::size_t, std::string>> loadedLocalization;
|
||||||
static std::mutex mutex;
|
static std::mutex mutex;
|
||||||
|
|
||||||
std::lock_guard lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
if (*currentLanguageId != languageId) {
|
if (*currentLanguageId != languageId) {
|
||||||
currentLanguageId = languageId;
|
currentLanguageId = languageId;
|
||||||
loadedLocalization->clear();
|
loadedLocalization->clear();
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Plugin::Plugin(const std::string &name, const hex::PluginFunctions &functions) :
|
Plugin::Plugin(const std::string &name, const hex::PluginFunctions &functions) :
|
||||||
m_handle(0), m_path(name), m_addedManually(true), m_functions(functions) { }
|
m_path(name), m_addedManually(true), m_functions(functions) { }
|
||||||
|
|
||||||
|
|
||||||
Plugin::Plugin(Plugin &&other) noexcept {
|
Plugin::Plugin(Plugin &&other) noexcept {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::fs::path ProjectFile::getPath() {
|
std::fs::path ProjectFile::getPath() {
|
||||||
return s_currProjectPath;
|
return *s_currProjectPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectFile::setPath(const std::fs::path &path) {
|
void ProjectFile::setPath(const std::fs::path &path) {
|
||||||
|
|||||||
@@ -281,25 +281,25 @@ namespace hex {
|
|||||||
|
|
||||||
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
||||||
log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get());
|
log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get());
|
||||||
auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { shortcut, unlocalizedName, callback, enabledCallback } });
|
auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { .shortcut=shortcut, .unlocalizedName=unlocalizedName, .callback=callback, .enabledCallback=enabledCallback } });
|
||||||
if (!inserted) log::error("Failed to add shortcut!");
|
if (!inserted) log::error("Failed to add shortcut!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
||||||
log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.get());
|
log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.get());
|
||||||
auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { shortcut, { unlocalizedName }, callback, enabledCallback } });
|
auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { .shortcut=shortcut, .unlocalizedName={ unlocalizedName }, .callback=callback, .enabledCallback=enabledCallback } });
|
||||||
if (!inserted) log::error("Failed to add shortcut!");
|
if (!inserted) log::error("Failed to add shortcut!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
||||||
log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get());
|
log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get());
|
||||||
auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { shortcut, unlocalizedName, callback, enabledCallback } });
|
auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { .shortcut=shortcut, .unlocalizedName=unlocalizedName, .callback=callback, .enabledCallback=enabledCallback } });
|
||||||
if (!inserted) log::error("Failed to add shortcut!");
|
if (!inserted) log::error("Failed to add shortcut!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {
|
||||||
log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.get());
|
log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.get());
|
||||||
auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { shortcut, { unlocalizedName }, callback, enabledCallback } });
|
auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { .shortcut=shortcut, .unlocalizedName={ unlocalizedName }, .callback=callback, .enabledCallback=enabledCallback } });
|
||||||
if (!inserted) log::error("Failed to add shortcut!");
|
if (!inserted) log::error("Failed to add shortcut!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,12 +329,13 @@ namespace hex {
|
|||||||
if (ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId))
|
if (ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const bool currentlyTyping = ImGui::GetIO().WantTextInput;
|
auto it = shortcuts.end();
|
||||||
|
if (ImGui::GetIO().WantTextInput) {
|
||||||
auto it = shortcuts.find(shortcut + AllowWhileTyping);
|
it = shortcuts.find(shortcut + AllowWhileTyping);
|
||||||
if (!currentlyTyping && it == shortcuts.end()) {
|
} else {
|
||||||
|
it = shortcuts.find(shortcut);
|
||||||
if (it == shortcuts.end())
|
if (it == shortcuts.end())
|
||||||
it = shortcuts.find(shortcut);
|
it = shortcuts.find(shortcut + AllowWhileTyping);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it != shortcuts.end()) {
|
if (it != shortcuts.end()) {
|
||||||
@@ -366,7 +367,17 @@ namespace hex {
|
|||||||
if (keyCode != 0)
|
if (keyCode != 0)
|
||||||
s_prevShortcut = Shortcut(pressedShortcut.getKeys());
|
s_prevShortcut = Shortcut(pressedShortcut.getKeys());
|
||||||
|
|
||||||
runShortcut(pressedShortcut, currentView);
|
std::set<const View*> processedViews;
|
||||||
|
while (true) {
|
||||||
|
if (runShortcut(pressedShortcut, currentView)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
processedViews.insert(currentView);
|
||||||
|
currentView = currentView->getMenuItemInheritView();
|
||||||
|
if (currentView == nullptr || processedViews.contains(currentView))
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {
|
void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include <jthread.hpp>
|
#include <jthread.hpp>
|
||||||
|
#include <hex/helpers/debugging.hpp>
|
||||||
|
#include <hex/trace/exceptions.hpp>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -52,6 +55,7 @@ namespace hex {
|
|||||||
std::list<std::function<void()>> s_deferredCalls;
|
std::list<std::function<void()>> s_deferredCalls;
|
||||||
std::unordered_map<SourceLocationWrapper, std::function<void()>> s_onceDeferredCalls;
|
std::unordered_map<SourceLocationWrapper, std::function<void()>> s_onceDeferredCalls;
|
||||||
std::list<std::function<void()>> s_tasksFinishedCallbacks;
|
std::list<std::function<void()>> s_tasksFinishedCallbacks;
|
||||||
|
std::list<std::function<void(Task&)>> s_taskCompletionCallbacks;
|
||||||
|
|
||||||
std::mutex s_queueMutex;
|
std::mutex s_queueMutex;
|
||||||
std::condition_variable s_jobCondVar;
|
std::condition_variable s_jobCondVar;
|
||||||
@@ -64,8 +68,8 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Task::Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function)
|
Task::Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function)
|
||||||
: m_unlocalizedName(unlocalizedName),
|
: m_unlocalizedName(std::move(unlocalizedName)),
|
||||||
m_maxValue(maxValue),
|
m_maxValue(maxValue),
|
||||||
m_function(std::move(function)),
|
m_function(std::move(function)),
|
||||||
m_background(background), m_blocking(blocking) { }
|
m_background(background), m_blocking(blocking) { }
|
||||||
@@ -310,6 +314,8 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
trace::enableExceptionCaptureForCurrentThread();
|
||||||
|
|
||||||
// Set the thread name to the name of the task
|
// Set the thread name to the name of the task
|
||||||
TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
|
TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
|
||||||
|
|
||||||
@@ -317,21 +323,34 @@ namespace hex {
|
|||||||
task->m_function(*task);
|
task->m_function(*task);
|
||||||
|
|
||||||
log::debug("Task '{}' finished", task->m_unlocalizedName.get());
|
log::debug("Task '{}' finished", task->m_unlocalizedName.get());
|
||||||
|
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(s_tasksFinishedMutex);
|
||||||
|
|
||||||
|
for (const auto &callback : s_taskCompletionCallbacks)
|
||||||
|
callback(*task);
|
||||||
|
}
|
||||||
} catch (const Task::TaskInterruptor &) {
|
} catch (const Task::TaskInterruptor &) {
|
||||||
// Handle the task being interrupted by user request
|
// Handle the task being interrupted by user request
|
||||||
task->interruption();
|
task->interruption();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what());
|
log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what());
|
||||||
|
|
||||||
|
dbg::printStackTrace(trace::getStackTrace());
|
||||||
|
|
||||||
// Handle the task throwing an uncaught exception
|
// Handle the task throwing an uncaught exception
|
||||||
task->exception(e.what());
|
task->exception(e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
log::error("Exception in task '{}'", task->m_unlocalizedName.get());
|
log::error("Exception in task '{}'", task->m_unlocalizedName.get());
|
||||||
|
|
||||||
|
dbg::printStackTrace(trace::getStackTrace());
|
||||||
|
|
||||||
// Handle the task throwing an uncaught exception of unknown type
|
// Handle the task throwing an uncaught exception of unknown type
|
||||||
task->exception("Unknown Exception");
|
task->exception("Unknown Exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace::disableExceptionCaptureForCurrentThread();
|
||||||
|
|
||||||
s_currentTask = nullptr;
|
s_currentTask = nullptr;
|
||||||
task->finish();
|
task->finish();
|
||||||
}
|
}
|
||||||
@@ -350,7 +369,10 @@ namespace hex {
|
|||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
|
|
||||||
// Wake up all the idle worker threads so they can exit
|
// Wake up all the idle worker threads so they can exit
|
||||||
s_jobCondVar.notify_all();
|
{
|
||||||
|
std::unique_lock lock(s_queueMutex);
|
||||||
|
s_jobCondVar.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for all worker threads to exit
|
// Wait for all worker threads to exit
|
||||||
s_workers.clear();
|
s_workers.clear();
|
||||||
@@ -361,6 +383,7 @@ namespace hex {
|
|||||||
s_deferredCalls.clear();
|
s_deferredCalls.clear();
|
||||||
s_onceDeferredCalls.clear();
|
s_onceDeferredCalls.clear();
|
||||||
s_tasksFinishedCallbacks.clear();
|
s_tasksFinishedCallbacks.clear();
|
||||||
|
s_taskCompletionCallbacks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task&)> function) {
|
TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task&)> function) {
|
||||||
@@ -569,5 +592,13 @@ namespace hex {
|
|||||||
return s_mainThreadId == std::this_thread::get_id();
|
return s_mainThreadId == std::this_thread::get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TaskManager::addTaskCompletionCallback(const std::function<void(Task &)> &function) {
|
||||||
|
std::scoped_lock lock(s_tasksFinishedMutex);
|
||||||
|
|
||||||
|
for (const auto &task : s_tasks) {
|
||||||
|
task->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
s_taskCompletionCallbacks.push_back(function);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace hex {
|
|||||||
void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) {
|
void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) {
|
||||||
std::unique_lock lock(s_themeMutex);
|
std::unique_lock lock(s_themeMutex);
|
||||||
|
|
||||||
(*s_themeHandlers)[name] = { colorMap, getFunction, setFunction };
|
(*s_themeHandlers)[name] = { .colorMap=colorMap, .getFunction=getFunction, .setFunction=setFunction };
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) {
|
void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) {
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace hex {
|
|||||||
ImGuiID s_activeHelpId;
|
ImGuiID s_activeHelpId;
|
||||||
bool s_helpHoverActive = false;
|
bool s_helpHoverActive = false;
|
||||||
|
|
||||||
|
AutoReset<std::function<std::function<void()>(const std::string &)>> s_renderer;
|
||||||
|
|
||||||
|
|
||||||
class IDStack {
|
class IDStack {
|
||||||
public:
|
public:
|
||||||
@@ -55,7 +57,7 @@ namespace hex {
|
|||||||
|
|
||||||
void add(const void *pointer) {
|
void add(const void *pointer) {
|
||||||
const ImGuiID seed = idStack.back();
|
const ImGuiID seed = idStack.back();
|
||||||
const ImGuiID id = ImHashData(&pointer, sizeof(pointer), seed);
|
const ImGuiID id = ImHashData((const void*) &pointer, sizeof(pointer), seed);
|
||||||
|
|
||||||
idStack.push_back(id);
|
idStack.push_back(id);
|
||||||
}
|
}
|
||||||
@@ -94,38 +96,47 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TutorialManager::init() {
|
void TutorialManager::init() {
|
||||||
EventImGuiElementRendered::subscribe([](ImGuiID id, const std::array<float, 4> bb){
|
if (*s_renderer == nullptr) {
|
||||||
const auto boundingBox = ImRect(bb[0], bb[1], bb[2], bb[3]);
|
*s_renderer = [](const std::string &message) {
|
||||||
|
return [message] {
|
||||||
|
ImGui::PushTextWrapPos(300_scaled);
|
||||||
|
ImGui::TextUnformatted(message.c_str());
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::NewLine();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ImGui::IsRectVisible(boundingBox.Min, boundingBox.Max))
|
void TutorialManager::postElementRendered(ImGuiID id, const ImRect &boundingBox) {
|
||||||
return;
|
if (!ImGui::IsRectVisible(boundingBox.Min, boundingBox.Max))
|
||||||
|
return;
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto element = hex::s_highlights->find(id);
|
const auto element = hex::s_highlights->find(id);
|
||||||
if (element != hex::s_highlights->end()) {
|
if (element != hex::s_highlights->end()) {
|
||||||
hex::s_highlightDisplays->emplace_back(boundingBox, element->second);
|
hex::s_highlightDisplays->emplace_back(boundingBox, element->second);
|
||||||
|
|
||||||
const auto window = ImGui::GetCurrentWindow();
|
const auto window = ImGui::GetCurrentWindow();
|
||||||
if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr)
|
if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr)
|
||||||
window->DockNode->TabBar->NextSelectedTabId = window->TabId;
|
window->DockNode->TabBar->NextSelectedTabId = window->TabId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto element = s_interactiveHelpItems->find(id);
|
||||||
|
if (element != s_interactiveHelpItems->end()) {
|
||||||
|
(*s_interactiveHelpDisplays)[id] = boundingBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
}
|
||||||
const auto element = s_interactiveHelpItems->find(id);
|
|
||||||
if (element != s_interactiveHelpItems->end()) {
|
|
||||||
(*s_interactiveHelpDisplays)[id] = boundingBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (id != 0 && boundingBox.Contains(ImGui::GetMousePos())) {
|
||||||
|
if ((s_hoveredRect.GetArea() == 0 || boundingBox.GetArea() < s_hoveredRect.GetArea()) && s_interactiveHelpItems->contains(id)) {
|
||||||
|
s_hoveredRect = boundingBox;
|
||||||
|
s_hoveredId = id;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (id != 0 && boundingBox.Contains(ImGui::GetMousePos())) {
|
|
||||||
if ((s_hoveredRect.GetArea() == 0 || boundingBox.GetArea() < s_hoveredRect.GetArea()) && s_interactiveHelpItems->contains(id)) {
|
|
||||||
s_hoveredRect = boundingBox;
|
|
||||||
s_hoveredId = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::map<std::string, TutorialManager::Tutorial>& TutorialManager::getTutorials() {
|
const std::map<std::string, TutorialManager::Tutorial>& TutorialManager::getTutorials() {
|
||||||
@@ -204,6 +215,10 @@ namespace hex {
|
|||||||
s_currentTutorial->second.start();
|
s_currentTutorial->second.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TutorialManager::stopCurrentTutorial() {
|
||||||
|
s_currentTutorial = s_tutorials->end();
|
||||||
|
}
|
||||||
|
|
||||||
void TutorialManager::drawHighlights() {
|
void TutorialManager::drawHighlights() {
|
||||||
if (s_helpHoverActive) {
|
if (s_helpHoverActive) {
|
||||||
const auto &drawList = ImGui::GetForegroundDrawList(ImGui::GetMainViewport());
|
const auto &drawList = ImGui::GetForegroundDrawList(ImGui::GetMainViewport());
|
||||||
@@ -303,10 +318,10 @@ namespace hex {
|
|||||||
|
|
||||||
if (!message.has_value()) {
|
if (!message.has_value()) {
|
||||||
message = Tutorial::Step::Message {
|
message = Tutorial::Step::Message {
|
||||||
Position::None,
|
.position=Position::None,
|
||||||
"",
|
.unlocalizedTitle="",
|
||||||
"",
|
.unlocalizedMessage="",
|
||||||
false
|
.allowSkip=false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,34 +348,39 @@ namespace hex {
|
|||||||
|
|
||||||
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
|
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
|
||||||
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
|
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
|
||||||
if (ImGui::Begin("##TutorialMessage", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing)) {
|
ImGui::SetNextWindowSize(ImVec2(300_scaled, 0));
|
||||||
|
|
||||||
|
bool open = true;
|
||||||
|
if (ImGui::Begin(message->unlocalizedTitle.empty() ? "##TutorialMessage" : Lang(message->unlocalizedTitle), &open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoFocusOnAppearing)) {
|
||||||
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
|
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
|
||||||
|
|
||||||
if (!message->unlocalizedTitle.empty())
|
auto &step = s_currentTutorial->second.m_currentStep;
|
||||||
ImGuiExt::Header(Lang(message->unlocalizedTitle), true);
|
|
||||||
|
|
||||||
if (!message->unlocalizedMessage.empty()) {
|
if (!message->unlocalizedMessage.empty()) {
|
||||||
ImGui::PushTextWrapPos(300_scaled);
|
step->m_drawFunction();
|
||||||
ImGui::TextUnformatted(Lang(message->unlocalizedMessage));
|
ImGui::NewLine();
|
||||||
ImGui::PopTextWrapPos();
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::BeginDisabled(s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_steps.begin());
|
ImGui::BeginDisabled(step == s_currentTutorial->second.m_steps.begin());
|
||||||
if (ImGui::ArrowButton("Backwards", ImGuiDir_Left)) {
|
if (ImGuiExt::DimmedArrowButton("Backwards", ImGuiDir_Left)) {
|
||||||
s_currentTutorial->second.m_currentStep->advance(-1);
|
s_currentTutorial->second.m_currentStep->advance(-1);
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(!message->allowSkip && s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_latestStep);
|
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.x);
|
||||||
if (ImGui::ArrowButton("Forwards", ImGuiDir_Right)) {
|
ImGui::BeginDisabled(!message->allowSkip && step == s_currentTutorial->second.m_latestStep);
|
||||||
s_currentTutorial->second.m_currentStep->advance(1);
|
if (ImGuiExt::DimmedArrowButton("Forwards", ImGuiDir_Right)) {
|
||||||
|
step->advance(1);
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
if (!open) {
|
||||||
|
stopCurrentTutorial();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TutorialManager::drawTutorial() {
|
void TutorialManager::drawTutorial() {
|
||||||
@@ -387,6 +407,10 @@ namespace hex {
|
|||||||
s_highlightDisplays->clear();
|
s_highlightDisplays->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TutorialManager::setRenderer(std::function<DrawFunction(const std::string &)> renderer) {
|
||||||
|
s_renderer = std::move(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
|
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
|
||||||
auto &newStep = m_steps.emplace_back(this);
|
auto &newStep = m_steps.emplace_back(this);
|
||||||
m_currentStep = m_steps.end();
|
m_currentStep = m_steps.end();
|
||||||
@@ -402,6 +426,9 @@ namespace hex {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_currentStep->addHighlights();
|
m_currentStep->addHighlights();
|
||||||
|
|
||||||
|
if (m_currentStep->m_message.has_value())
|
||||||
|
m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_currentStep->m_message->unlocalizedMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TutorialManager::Tutorial::Step::addHighlights() const {
|
void TutorialManager::Tutorial::Step::addHighlights() const {
|
||||||
@@ -426,8 +453,12 @@ namespace hex {
|
|||||||
std::advance(m_parent->m_latestStep, steps);
|
std::advance(m_parent->m_latestStep, steps);
|
||||||
std::advance(m_parent->m_currentStep, steps);
|
std::advance(m_parent->m_currentStep, steps);
|
||||||
|
|
||||||
if (m_parent->m_currentStep != m_parent->m_steps.end())
|
if (m_parent->m_currentStep != m_parent->m_steps.end()) {
|
||||||
m_parent->m_currentStep->addHighlights();
|
m_parent->m_currentStep->addHighlights();
|
||||||
|
|
||||||
|
if (m_message.has_value())
|
||||||
|
m_parent->m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_parent->m_currentStep->m_message->unlocalizedMessage));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
s_currentTutorial = s_tutorials->end();
|
s_currentTutorial = s_tutorials->end();
|
||||||
}
|
}
|
||||||
@@ -450,10 +481,10 @@ namespace hex {
|
|||||||
|
|
||||||
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position) {
|
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position) {
|
||||||
m_message = Message {
|
m_message = Message {
|
||||||
position,
|
.position=position,
|
||||||
unlocalizedTitle,
|
.unlocalizedTitle=unlocalizedTitle,
|
||||||
unlocalizedMessage,
|
.unlocalizedMessage=unlocalizedMessage,
|
||||||
false
|
.allowSkip=false
|
||||||
};
|
};
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@@ -464,10 +495,10 @@ namespace hex {
|
|||||||
m_message->allowSkip = true;
|
m_message->allowSkip = true;
|
||||||
} else {
|
} else {
|
||||||
m_message = Message {
|
m_message = Message {
|
||||||
Position::Bottom | Position::Right,
|
.position=Position::Bottom | Position::Right,
|
||||||
"",
|
.unlocalizedTitle="",
|
||||||
"",
|
.unlocalizedMessage="",
|
||||||
true
|
.allowSkip=true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ namespace hex {
|
|||||||
|
|
||||||
void WorkspaceManager::process() {
|
void WorkspaceManager::process() {
|
||||||
if (s_previousWorkspace != s_currentWorkspace) {
|
if (s_previousWorkspace != s_currentWorkspace) {
|
||||||
log::info("Updating workspace");
|
log::debug("Updating workspace");
|
||||||
if (s_previousWorkspace != s_workspaces->end()) {
|
if (s_previousWorkspace != s_workspaces->end()) {
|
||||||
auto newWorkspace = s_currentWorkspace;
|
auto newWorkspace = s_currentWorkspace;
|
||||||
s_currentWorkspace = s_previousWorkspace;
|
s_currentWorkspace = s_previousWorkspace;
|
||||||
|
|||||||
@@ -151,8 +151,8 @@ namespace hex::dp {
|
|||||||
m_overlay->getData() = data;
|
m_overlay->getData() = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void Node::throwNodeError(const std::string &message) {
|
[[noreturn]] void Node::throwNodeError(const std::string &msg) {
|
||||||
throw NodeError { this, message };
|
throw NodeError(this, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::setAttributes(std::vector<Attribute> attributes) {
|
void Node::setAttributes(std::vector<Attribute> attributes) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace hex {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void skipWhitespace(std::string_view &string) {
|
void skipWhitespace(std::string_view &string) {
|
||||||
while (string.length() > 0) {
|
while (!string.empty()) {
|
||||||
if (!std::isspace(string.front()))
|
if (!std::isspace(string.front()))
|
||||||
break;
|
break;
|
||||||
string = string.substr(1);
|
string = string.substr(1);
|
||||||
@@ -89,15 +89,15 @@ namespace hex {
|
|||||||
return { };
|
return { };
|
||||||
|
|
||||||
bool inString = false;
|
bool inString = false;
|
||||||
while (string.length() > 0) {
|
while (!string.empty()) {
|
||||||
BinaryPattern::Pattern pattern = { 0, 0 };
|
BinaryPattern::Pattern pattern = { .mask=0, .value=0 };
|
||||||
|
|
||||||
if (string.starts_with("\"")) {
|
if (string.starts_with("\"")) {
|
||||||
inString = !inString;
|
inString = !inString;
|
||||||
string = string.substr(1);
|
string = string.substr(1);
|
||||||
continue;
|
continue;
|
||||||
} else if (inString) {
|
} else if (inString) {
|
||||||
pattern = { 0xFF, u8(string.front()) };
|
pattern = { .mask=0xFF, .value=u8(string.front()) };
|
||||||
string = string.substr(1);
|
string = string.substr(1);
|
||||||
} else if (string.starts_with("u") || string.starts_with("s")) {
|
} else if (string.starts_with("u") || string.starts_with("s")) {
|
||||||
auto newPatterns = parseValueExpression(string);
|
auto newPatterns = parseValueExpression(string);
|
||||||
@@ -106,7 +106,7 @@ namespace hex {
|
|||||||
std::ranges::move(newPatterns, std::back_inserter(result));
|
std::ranges::move(newPatterns, std::back_inserter(result));
|
||||||
continue;
|
continue;
|
||||||
} else if (string.starts_with("??")) {
|
} else if (string.starts_with("??")) {
|
||||||
pattern = { 0x00, 0x00 };
|
pattern = { .mask=0x00, .value=0x00 };
|
||||||
string = string.substr(2);
|
string = string.substr(2);
|
||||||
} else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) {
|
} else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) {
|
||||||
const auto hex = string.substr(0, 2);
|
const auto hex = string.substr(0, 2);
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <functional>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
@@ -82,7 +81,7 @@ namespace hex::crypt {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr Crc(u64 polynomial, u64 init, u64 xorOut, bool reflectInput, bool reflectOutput)
|
constexpr Crc(u64 polynomial, u64 init, u64 xorOut, bool reflectInput, bool reflectOutput)
|
||||||
: m_value(0x00), m_init(init & ((0b10ull << (NumBits - 1)) - 1)), m_xorOut(xorOut & ((0b10ull << (NumBits - 1)) - 1)),
|
: m_init(init & ((0b10ull << (NumBits - 1)) - 1)), m_xorOut(xorOut & ((0b10ull << (NumBits - 1)) - 1)),
|
||||||
m_reflectInput(reflectInput), m_reflectOutput(reflectOutput),
|
m_reflectInput(reflectInput), m_reflectOutput(reflectOutput),
|
||||||
m_table([polynomial] {
|
m_table([polynomial] {
|
||||||
auto reflectedPoly = reflect(polynomial & ((0b10ull << (NumBits - 1)) - 1), NumBits);
|
auto reflectedPoly = reflect(polynomial & ((0b10ull << (NumBits - 1)) - 1), NumBits);
|
||||||
@@ -129,7 +128,7 @@ namespace hex::crypt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u64 m_value;
|
u64 m_value = 0x00;
|
||||||
|
|
||||||
u64 m_init;
|
u64 m_init;
|
||||||
u64 m_xorOut;
|
u64 m_xorOut;
|
||||||
@@ -144,7 +143,7 @@ namespace hex::crypt {
|
|||||||
using Crc = Crc<NumBits>;
|
using Crc = Crc<NumBits>;
|
||||||
Crc crc(polynomial, init, xorout, reflectIn, reflectOut);
|
Crc crc(polynomial, init, xorout, reflectIn, reflectOut);
|
||||||
|
|
||||||
processDataByChunks(data, offset, size, std::bind(&Crc::processBytes, &crc, _1, _2));
|
processDataByChunks(data, offset, size, [&crc](auto && data, auto && size) { crc.processBytes(data, size); });
|
||||||
|
|
||||||
return crc.checksum();
|
return crc.checksum();
|
||||||
}
|
}
|
||||||
@@ -170,7 +169,7 @@ namespace hex::crypt {
|
|||||||
|
|
||||||
mbedtls_md5_starts(&ctx);
|
mbedtls_md5_starts(&ctx);
|
||||||
|
|
||||||
processDataByChunks(data, offset, size, std::bind(mbedtls_md5_update, &ctx, _1, _2));
|
processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_md5_update(&ctx, data, size); });
|
||||||
|
|
||||||
mbedtls_md5_finish(&ctx, result.data());
|
mbedtls_md5_finish(&ctx, result.data());
|
||||||
|
|
||||||
@@ -202,7 +201,7 @@ namespace hex::crypt {
|
|||||||
|
|
||||||
mbedtls_sha1_starts(&ctx);
|
mbedtls_sha1_starts(&ctx);
|
||||||
|
|
||||||
processDataByChunks(data, offset, size, std::bind(mbedtls_sha1_update, &ctx, _1, _2));
|
processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha1_update(&ctx, data, size); });
|
||||||
|
|
||||||
mbedtls_sha1_finish(&ctx, result.data());
|
mbedtls_sha1_finish(&ctx, result.data());
|
||||||
|
|
||||||
@@ -234,7 +233,7 @@ namespace hex::crypt {
|
|||||||
|
|
||||||
mbedtls_sha256_starts(&ctx, true);
|
mbedtls_sha256_starts(&ctx, true);
|
||||||
|
|
||||||
processDataByChunks(data, offset, size, std::bind(mbedtls_sha256_update, &ctx, _1, _2));
|
processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha256_update(&ctx, data, size); });
|
||||||
|
|
||||||
mbedtls_sha256_finish(&ctx, result.data());
|
mbedtls_sha256_finish(&ctx, result.data());
|
||||||
|
|
||||||
@@ -266,7 +265,7 @@ namespace hex::crypt {
|
|||||||
|
|
||||||
mbedtls_sha256_starts(&ctx, false);
|
mbedtls_sha256_starts(&ctx, false);
|
||||||
|
|
||||||
processDataByChunks(data, offset, size, std::bind(mbedtls_sha256_update, &ctx, _1, _2));
|
processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha256_update(&ctx, data, size); });
|
||||||
|
|
||||||
mbedtls_sha256_finish(&ctx, result.data());
|
mbedtls_sha256_finish(&ctx, result.data());
|
||||||
|
|
||||||
@@ -298,7 +297,7 @@ namespace hex::crypt {
|
|||||||
|
|
||||||
mbedtls_sha512_starts(&ctx, true);
|
mbedtls_sha512_starts(&ctx, true);
|
||||||
|
|
||||||
processDataByChunks(data, offset, size, std::bind(mbedtls_sha512_update, &ctx, _1, _2));
|
processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha512_update(&ctx, data, size); });
|
||||||
|
|
||||||
mbedtls_sha512_finish(&ctx, result.data());
|
mbedtls_sha512_finish(&ctx, result.data());
|
||||||
|
|
||||||
@@ -330,7 +329,7 @@ namespace hex::crypt {
|
|||||||
|
|
||||||
mbedtls_sha512_starts(&ctx, false);
|
mbedtls_sha512_starts(&ctx, false);
|
||||||
|
|
||||||
processDataByChunks(data, offset, size, std::bind(mbedtls_sha512_update, &ctx, _1, _2));
|
processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha512_update(&ctx, data, size); });
|
||||||
|
|
||||||
mbedtls_sha512_finish(&ctx, result.data());
|
mbedtls_sha512_finish(&ctx, result.data());
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include <hex/helpers/debugging.hpp>
|
#include <hex/helpers/debugging.hpp>
|
||||||
|
#include <hex/helpers/logger.hpp>
|
||||||
|
#include <hex/trace/stacktrace.hpp>
|
||||||
|
|
||||||
namespace hex::dbg {
|
namespace hex::dbg {
|
||||||
|
|
||||||
@@ -21,4 +23,23 @@ namespace hex::dbg {
|
|||||||
s_debugMode = enabled;
|
s_debugMode = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString) {
|
||||||
|
log::error("Assertion failed: {} at {}:{} => {}", exprString, file, line, function);
|
||||||
|
|
||||||
|
const auto stackTrace = trace::getStackTrace();
|
||||||
|
dbg::printStackTrace(stackTrace);
|
||||||
|
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printStackTrace(const trace::StackTraceResult &stackTrace) {
|
||||||
|
log::fatal("Printing stacktrace using implementation '{}'", stackTrace.implementationName);
|
||||||
|
for (const auto &stackFrame : stackTrace.stackFrames) {
|
||||||
|
if (stackFrame.line == 0)
|
||||||
|
log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function);
|
||||||
|
else
|
||||||
|
log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <hex/helpers/default_paths.hpp>
|
#include <hex/helpers/default_paths.hpp>
|
||||||
|
|
||||||
#include <hex/api/imhex_api/system.hpp>
|
#include <hex/api/imhex_api/system.hpp>
|
||||||
#include <hex/api/project_file_manager.hpp>
|
#include <hex/api/project_file_manager.hpp>
|
||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <algorithm>
|
#include <set>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -17,6 +18,13 @@ namespace hex::paths {
|
|||||||
|
|
||||||
std::vector<std::fs::path> getDataPaths(bool includeSystemFolders) {
|
std::vector<std::fs::path> getDataPaths(bool includeSystemFolders) {
|
||||||
std::vector<std::fs::path> paths;
|
std::vector<std::fs::path> paths;
|
||||||
|
std::set<std::fs::path> uniquePaths;
|
||||||
|
|
||||||
|
const auto emplaceUniquePath = [&](const std::fs::path &path) {
|
||||||
|
auto duplicate = uniquePaths.insert(path);
|
||||||
|
if (duplicate.second)
|
||||||
|
paths.emplace_back(path);
|
||||||
|
};
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
|
|
||||||
@@ -25,21 +33,22 @@ namespace hex::paths {
|
|||||||
if (!ImHexApi::System::isPortableVersion()) {
|
if (!ImHexApi::System::isPortableVersion()) {
|
||||||
PWSTR wAppDataPath = nullptr;
|
PWSTR wAppDataPath = nullptr;
|
||||||
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) {
|
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) {
|
||||||
paths.emplace_back(wAppDataPath);
|
emplaceUniquePath(wAppDataPath);
|
||||||
CoTaskMemFree(wAppDataPath);
|
CoTaskMemFree(wAppDataPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(OS_MACOS)
|
#elif defined(OS_MACOS)
|
||||||
|
|
||||||
paths.push_back(wolv::io::fs::getApplicationSupportDirectoryPath() / "imhex");
|
emplaceUniquePath(wolv::io::fs::getApplicationSupportDirectoryPath() / "imhex");
|
||||||
|
|
||||||
#elif defined(OS_LINUX) || defined(OS_WEB)
|
#elif defined(OS_LINUX) || defined(OS_WEB)
|
||||||
|
|
||||||
paths.push_back(xdg::DataHomeDir());
|
emplaceUniquePath(xdg::DataHomeDir());
|
||||||
|
|
||||||
auto dataDirs = xdg::DataDirs();
|
auto dataDirs = xdg::DataDirs();
|
||||||
std::copy(dataDirs.begin(), dataDirs.end(), std::back_inserter(paths));
|
for (const auto &path : dataDirs)
|
||||||
|
emplaceUniquePath(path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -47,18 +56,21 @@ namespace hex::paths {
|
|||||||
|
|
||||||
if (includeSystemFolders) {
|
if (includeSystemFolders) {
|
||||||
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
|
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
|
||||||
paths.push_back(executablePath->parent_path());
|
emplaceUniquePath(executablePath->parent_path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
for (auto &path : paths)
|
uniquePaths.clear();
|
||||||
|
for (auto &path : paths) {
|
||||||
path = path / "imhex";
|
path = path / "imhex";
|
||||||
|
uniquePaths.insert(path);
|
||||||
|
}
|
||||||
|
|
||||||
if (ImHexApi::System::isPortableVersion() || includeSystemFolders) {
|
if (ImHexApi::System::isPortableVersion() || includeSystemFolders) {
|
||||||
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())
|
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())
|
||||||
paths.push_back(executablePath->parent_path());
|
emplaceUniquePath(executablePath->parent_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -66,11 +78,12 @@ namespace hex::paths {
|
|||||||
|
|
||||||
// Add additional data directories to the path
|
// Add additional data directories to the path
|
||||||
auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths();
|
auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths();
|
||||||
std::ranges::copy(additionalDirs, std::back_inserter(paths));
|
for (const auto &path : additionalDirs)
|
||||||
|
emplaceUniquePath(path);
|
||||||
|
|
||||||
// Add the project file directory to the path, if one is loaded
|
// Add the project file directory to the path, if one is loaded
|
||||||
if (ProjectFile::hasPath()) {
|
if (ProjectFile::hasPath()) {
|
||||||
paths.push_back(ProjectFile::getPath().parent_path());
|
emplaceUniquePath(ProjectFile::getPath().parent_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
@@ -97,11 +110,18 @@ namespace hex::paths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::fs::path> getPluginPaths() {
|
static std::vector<std::fs::path> getPluginPaths() {
|
||||||
|
// If running from an AppImage, only allow loaded plugins from inside it
|
||||||
|
#if defined(OS_LINUX)
|
||||||
|
if(const char* appdir = std::getenv("APPDIR")) { // check for AppImage environment
|
||||||
|
return {std::string(appdir) + "/usr/lib/imhex"};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::vector<std::fs::path> paths = getDataPaths(true);
|
std::vector<std::fs::path> paths = getDataPaths(true);
|
||||||
|
|
||||||
// Add the system plugin directory to the path if one was provided at compile time
|
// Add the system plugin directory to the path if one was provided at compile time
|
||||||
#if defined(OS_LINUX) && defined(SYSTEM_PLUGINS_LOCATION)
|
#if defined(OS_LINUX) && defined(SYSTEM_PLUGINS_LOCATION)
|
||||||
paths.push_back(SYSTEM_PLUGINS_LOCATION);
|
paths.emplace_back(SYSTEM_PLUGINS_LOCATION);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <hex/helpers/utils.hpp>
|
#include <hex/helpers/utils.hpp>
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
#include <wolv/io/file.hpp>
|
#include <wolv/io/file.hpp>
|
||||||
#include <wolv/utils/string.hpp>
|
#include <wolv/utils/string.hpp>
|
||||||
|
|
||||||
@@ -65,6 +66,9 @@ namespace hex {
|
|||||||
|
|
||||||
|
|
||||||
EncodingFile &EncodingFile::operator=(const hex::EncodingFile &other) {
|
EncodingFile &EncodingFile::operator=(const hex::EncodingFile &other) {
|
||||||
|
if(this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
|
m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
|
||||||
m_tableContent = other.m_tableContent;
|
m_tableContent = other.m_tableContent;
|
||||||
m_longestSequence = other.m_longestSequence;
|
m_longestSequence = other.m_longestSequence;
|
||||||
@@ -89,9 +93,7 @@ namespace hex {
|
|||||||
|
|
||||||
|
|
||||||
std::pair<std::string_view, size_t> EncodingFile::getEncodingFor(std::span<const u8> buffer) const {
|
std::pair<std::string_view, size_t> EncodingFile::getEncodingFor(std::span<const u8> buffer) const {
|
||||||
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
|
for (const auto &[size, mapping] : std::ranges::reverse_view(*m_mapping)) {
|
||||||
const auto &[size, mapping] = *riter;
|
|
||||||
|
|
||||||
if (size > buffer.size()) continue;
|
if (size > buffer.size()) continue;
|
||||||
|
|
||||||
std::vector key(buffer.begin(), buffer.begin() + size);
|
std::vector key(buffer.begin(), buffer.begin() + size);
|
||||||
@@ -103,9 +105,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
|
u64 EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
|
||||||
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
|
for (const auto& [size, mapping] : std::ranges::reverse_view(*m_mapping)) {
|
||||||
const auto &[size, mapping] = *riter;
|
|
||||||
|
|
||||||
if (size > buffer.size()) continue;
|
if (size > buffer.size()) continue;
|
||||||
|
|
||||||
std::vector key(buffer.begin(), buffer.begin() + size);
|
std::vector key(buffer.begin(), buffer.begin() + size);
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
#include <xdg.hpp>
|
#include <xdg.hpp>
|
||||||
# if defined(OS_FREEBSD)
|
# if defined(OS_FREEBSD)
|
||||||
#include <sys/syslimits.h>
|
#include <sys/syslimits.h>
|
||||||
# else
|
|
||||||
#include <limits.h>
|
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
|
|
||||||
#include <array>
|
#include <hex/api/tutorial_manager.hpp>
|
||||||
|
|
||||||
#if !defined(IMGUI_TEST_ENGINE)
|
#if !defined(IMGUI_TEST_ENGINE)
|
||||||
|
|
||||||
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
|
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
|
||||||
std::array<float, 4> boundingBox = { bb.Min.x, bb.Min.y, bb.Max.x, bb.Max.y };
|
hex::TutorialManager::postElementRendered(id, bb);
|
||||||
hex::EventImGuiElementRendered::post(id, boundingBox);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}
|
void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
|
#include <hex/helpers/debugging.hpp>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -117,8 +118,8 @@ namespace hex::log {
|
|||||||
|
|
||||||
void addLogEntry(std::string_view project, std::string_view level, std::string message) {
|
void addLogEntry(std::string_view project, std::string_view level, std::string message) {
|
||||||
s_logEntries->emplace_back(
|
s_logEntries->emplace_back(
|
||||||
std::move(project),
|
project,
|
||||||
std::move(level),
|
level,
|
||||||
std::move(message)
|
std::move(message)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -149,14 +150,6 @@ namespace hex::log {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void assertionHandler(const char* exprString, const char* file, int line) {
|
|
||||||
log::error("Assertion failed: {} at {}:{}", exprString, file, line);
|
|
||||||
|
|
||||||
#if defined (DEBUG)
|
|
||||||
std::abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace color {
|
namespace color {
|
||||||
|
|
||||||
fmt::color debug() { return fmt::color::medium_sea_green; }
|
fmt::color debug() { return fmt::color::medium_sea_green; }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
struct KeyEquivalent {
|
struct KeyEquivalent {
|
||||||
bool valid;
|
bool valid;
|
||||||
@@ -61,7 +62,120 @@ void macosEndMainMenuBar(void) {
|
|||||||
s_constructingMenu = false;
|
s_constructingMenu = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool macosBeginMenu(const char* label, bool enabled) {
|
|
||||||
|
static NSMutableArray* g_RegisteredIconFontDescriptors = nil;
|
||||||
|
void macosRegisterFont(const unsigned char* fontBytes, size_t fontLength) {
|
||||||
|
if (!fontBytes || fontLength == 0 || fontLength > 100 * 1024 * 1024) { // Max 100MB sanity check
|
||||||
|
NSLog(@"Invalid font data: bytes=%p, length=%zu", fontBytes, fontLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize array on first use
|
||||||
|
if (!g_RegisteredIconFontDescriptors) {
|
||||||
|
g_RegisteredIconFontDescriptors = [[NSMutableArray alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create NSData - this will copy the bytes
|
||||||
|
NSData *fontData = [NSData dataWithBytes:fontBytes length:fontLength];
|
||||||
|
if (!fontData) {
|
||||||
|
NSLog(@"Failed to create NSData from font bytes");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFErrorRef error = NULL;
|
||||||
|
CFArrayRef descriptors = CTFontManagerCreateFontDescriptorsFromData((__bridge CFDataRef)fontData);
|
||||||
|
|
||||||
|
if (descriptors && CFArrayGetCount(descriptors) > 0) {
|
||||||
|
// Register all descriptors from this font file
|
||||||
|
CTFontManagerRegisterFontDescriptors(descriptors, kCTFontManagerScopeProcess, true, NULL);
|
||||||
|
if (true) {
|
||||||
|
// Store each descriptor for later use
|
||||||
|
for (CFIndex i = 0; i < CFArrayGetCount(descriptors); i++) {
|
||||||
|
CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descriptors, i);
|
||||||
|
[g_RegisteredIconFontDescriptors addObject:(__bridge id)descriptor];
|
||||||
|
|
||||||
|
// Log the font name for debugging
|
||||||
|
CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, 16.0, NULL);
|
||||||
|
if (font) {
|
||||||
|
CFRelease(font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSLog(@"Failed to register font descriptors");
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(descriptors);
|
||||||
|
} else {
|
||||||
|
NSLog(@"Failed to create font descriptors from data (length: %zu)", fontLength);
|
||||||
|
if (error) {
|
||||||
|
NSLog(@"Error: %@", (__bridge NSError *)error);
|
||||||
|
CFRelease(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSImage* imageFromIconFont(NSString* character,
|
||||||
|
CGFloat size,
|
||||||
|
NSColor* color) {
|
||||||
|
|
||||||
|
if (!character || [character length] == 0) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_RegisteredIconFontDescriptors || [g_RegisteredIconFontDescriptors count] == 0) {
|
||||||
|
NSLog(@"No icon fonts registered");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSFont *font = nil;
|
||||||
|
unichar unicode = [character characterAtIndex:0];
|
||||||
|
|
||||||
|
for (id descriptorObj in g_RegisteredIconFontDescriptors) {
|
||||||
|
CTFontDescriptorRef descriptor = (__bridge CTFontDescriptorRef)descriptorObj;
|
||||||
|
CTFontRef ctFont = CTFontCreateWithFontDescriptor(descriptor, size, NULL);
|
||||||
|
|
||||||
|
if (ctFont) {
|
||||||
|
CGGlyph glyph;
|
||||||
|
UniChar unichar = unicode;
|
||||||
|
if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
|
||||||
|
font = (__bridge NSFont *)ctFont;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CFRelease(ctFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!font) {
|
||||||
|
NSLog(@"No font found with glyph for character U+%04X (string: '%@', length: %lu)",
|
||||||
|
unicode, character, (unsigned long)[character length]);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *attributes = @{
|
||||||
|
NSFontAttributeName: font,
|
||||||
|
NSForegroundColorAttributeName: color
|
||||||
|
};
|
||||||
|
NSAttributedString *attrString = [[NSAttributedString alloc]
|
||||||
|
initWithString:character
|
||||||
|
attributes:attributes];
|
||||||
|
|
||||||
|
NSSize stringSize = [attrString size];
|
||||||
|
|
||||||
|
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(size, size)];
|
||||||
|
|
||||||
|
[image lockFocus];
|
||||||
|
|
||||||
|
NSPoint drawPoint = NSMakePoint((size - stringSize.width) / 2.0,
|
||||||
|
(size - stringSize.height) / 2.0);
|
||||||
|
[attrString drawAtPoint:drawPoint];
|
||||||
|
|
||||||
|
[image unlockFocus];
|
||||||
|
[image setTemplate:YES];
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool macosBeginMenu(const char* label, const char *icon, bool enabled) {
|
||||||
NSString* title = [NSString stringWithUTF8String:label];
|
NSString* title = [NSString stringWithUTF8String:label];
|
||||||
|
|
||||||
// Search for menu item with the given name
|
// Search for menu item with the given name
|
||||||
@@ -79,6 +193,17 @@ bool macosBeginMenu(const char* label, bool enabled) {
|
|||||||
menuItem.title = title;
|
menuItem.title = title;
|
||||||
[menuItem setSubmenu:newMenu];
|
[menuItem setSubmenu:newMenu];
|
||||||
|
|
||||||
|
// Hide menus starting with '$' (used for special menus)
|
||||||
|
if (label[0] == '$') {
|
||||||
|
[menuItem setHidden:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icon != NULL) {
|
||||||
|
NSString *iconString = [NSString stringWithUTF8String:icon];
|
||||||
|
NSImage* iconImage = imageFromIconFont(iconString, 16.0, [NSColor blackColor]);
|
||||||
|
[menuItem setImage:iconImage];
|
||||||
|
}
|
||||||
|
|
||||||
// Add the new menu to the end of the list
|
// Add the new menu to the end of the list
|
||||||
menuIndex = [s_menuStack[s_menuStackSize - 1] numberOfItems];
|
menuIndex = [s_menuStack[s_menuStackSize - 1] numberOfItems];
|
||||||
|
|
||||||
@@ -104,7 +229,7 @@ void macosEndMenu(void) {
|
|||||||
s_menuStackSize -= 1;
|
s_menuStackSize -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool macosMenuItem(const char* label, struct KeyEquivalent keyEquivalent, bool selected, bool enabled) {
|
bool macosMenuItem(const char* label, const char *icon, struct KeyEquivalent keyEquivalent, bool selected, bool enabled) {
|
||||||
NSString* title = [NSString stringWithUTF8String:label];
|
NSString* title = [NSString stringWithUTF8String:label];
|
||||||
|
|
||||||
if (s_constructingMenu) {
|
if (s_constructingMenu) {
|
||||||
@@ -113,6 +238,12 @@ bool macosMenuItem(const char* label, struct KeyEquivalent keyEquivalent, bool s
|
|||||||
menuItem.action = @selector(OnClick:);
|
menuItem.action = @selector(OnClick:);
|
||||||
menuItem.target = s_menuItemHandler;
|
menuItem.target = s_menuItemHandler;
|
||||||
|
|
||||||
|
if (icon != NULL) {
|
||||||
|
NSString *iconString = [NSString stringWithUTF8String:icon];
|
||||||
|
NSImage* iconImage = imageFromIconFont(iconString, 16.0, [NSColor blackColor]);
|
||||||
|
[menuItem setImage:iconImage];
|
||||||
|
}
|
||||||
|
|
||||||
[menuItem setTag:s_currTag];
|
[menuItem setTag:s_currTag];
|
||||||
s_currTag += 1;
|
s_currTag += 1;
|
||||||
|
|
||||||
@@ -164,8 +295,8 @@ bool macosMenuItem(const char* label, struct KeyEquivalent keyEquivalent, bool s
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool macosMenuItemSelect(const char* label, struct KeyEquivalent keyEquivalent, bool* selected, bool enabled) {
|
bool macosMenuItemSelect(const char* label, const char *icon, struct KeyEquivalent keyEquivalent, bool* selected, bool enabled) {
|
||||||
if (macosMenuItem(label, keyEquivalent, selected != NULL ? *selected : false, enabled)) {
|
if (macosMenuItem(label, icon, keyEquivalent, selected != NULL ? *selected : false, enabled)) {
|
||||||
if (selected != NULL)
|
if (selected != NULL)
|
||||||
*selected = !(*selected);
|
*selected = !(*selected);
|
||||||
|
|
||||||
@@ -180,3 +311,94 @@ void macosSeparator(void) {
|
|||||||
[s_menuStack[s_menuStackSize - 1] addItem:separator];
|
[s_menuStack[s_menuStackSize - 1] addItem:separator];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface NSObject (DockMenuAddition)
|
||||||
|
- (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSObject (DockMenuAddition)
|
||||||
|
|
||||||
|
- (NSMenu *)cloneMenu:(NSMenu *)originalMenu {
|
||||||
|
NSMenu *clonedMenu = [[NSMenu alloc] initWithTitle:[originalMenu title]];
|
||||||
|
|
||||||
|
for (NSMenuItem *item in [originalMenu itemArray]) {
|
||||||
|
NSMenuItem *clonedItem = [self cloneMenuItem:item];
|
||||||
|
[clonedMenu addItem:clonedItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
return clonedMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMenuItem *)cloneMenuItem:(NSMenuItem *)original {
|
||||||
|
if ([original isSeparatorItem]) {
|
||||||
|
return [NSMenuItem separatorItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new item with same properties
|
||||||
|
NSMenuItem *clone = [[NSMenuItem alloc] initWithTitle:[original title]
|
||||||
|
action:[original action]
|
||||||
|
keyEquivalent:[original keyEquivalent]];
|
||||||
|
|
||||||
|
// Copy other properties
|
||||||
|
[clone setTarget:[original target]];
|
||||||
|
[clone setEnabled:[original isEnabled]];
|
||||||
|
[clone setImage:[original image]];
|
||||||
|
[clone setTag:[original tag]];
|
||||||
|
[clone setRepresentedObject:[original representedObject]];
|
||||||
|
[clone setToolTip:[original toolTip]];
|
||||||
|
[clone setState:[original state]];
|
||||||
|
|
||||||
|
// Handle submenus recursively
|
||||||
|
if ([original hasSubmenu]) {
|
||||||
|
NSMenu *clonedSubmenu = [self cloneMenu:[original submenu]];
|
||||||
|
[clone setSubmenu:clonedSubmenu];
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender {
|
||||||
|
NSMenu *dockMenu = [[NSMenu alloc] init];
|
||||||
|
|
||||||
|
NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:@"$TASKBAR$"];
|
||||||
|
if (menuIndex == -1) {
|
||||||
|
return dockMenu;
|
||||||
|
}
|
||||||
|
NSMenuItem *fileMenuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex];
|
||||||
|
|
||||||
|
// Get the File submenu
|
||||||
|
NSMenu *fileSubmenu = [fileMenuItem submenu];
|
||||||
|
|
||||||
|
if (fileSubmenu) {
|
||||||
|
// Clone each item from the File submenu directly into the dock menu
|
||||||
|
for (NSMenuItem *item in [fileSubmenu itemArray]) {
|
||||||
|
NSMenuItem *clonedItem = [self cloneMenuItem:item];
|
||||||
|
[dockMenu addItem:clonedItem];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dockMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
void macosSetupDockMenu(void) {
|
||||||
|
@autoreleasepool {
|
||||||
|
// Get GLFW's delegate class
|
||||||
|
Class delegateClass = objc_getClass("GLFWApplicationDelegate");
|
||||||
|
|
||||||
|
if (delegateClass != nil) {
|
||||||
|
// Get our custom implementation
|
||||||
|
Method customMethod = class_getInstanceMethod([NSObject class],
|
||||||
|
@selector(imhexApplicationDockMenu:));
|
||||||
|
|
||||||
|
// Add the method to GLFW's delegate class
|
||||||
|
class_addMethod(delegateClass,
|
||||||
|
@selector(applicationDockMenu:),
|
||||||
|
method_getImplementation(customMethod),
|
||||||
|
method_getTypeEncoding(customMethod));
|
||||||
|
} else {
|
||||||
|
NSLog(@"ERROR: Could not find GLFWApplicationDelegate class");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
#include <cstddef>
|
||||||
|
#include "wolv/types.hpp"
|
||||||
|
#include <cmath>
|
||||||
#include <hex/helpers/opengl.hpp>
|
#include <hex/helpers/opengl.hpp>
|
||||||
#include <opengl_support.h>
|
#include <opengl_support.h>
|
||||||
|
|
||||||
#include <hex/helpers/utils.hpp>
|
|
||||||
#include <hex/helpers/logger.hpp>
|
#include <hex/helpers/logger.hpp>
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
#include <wolv/utils/guards.hpp>
|
#include <wolv/utils/guards.hpp>
|
||||||
|
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
@@ -133,17 +138,18 @@ namespace hex::gl {
|
|||||||
|
|
||||||
|
|
||||||
GLint Shader::getUniformLocation(std::string_view name) {
|
GLint Shader::getUniformLocation(std::string_view name) {
|
||||||
auto uniform = m_uniforms.find(name.data());
|
auto nameStr = std::string(name);
|
||||||
|
auto uniform = m_uniforms.find(nameStr);
|
||||||
if (uniform == m_uniforms.end()) {
|
if (uniform == m_uniforms.end()) {
|
||||||
auto location = glGetUniformLocation(m_program, name.data());
|
auto location = glGetUniformLocation(m_program, nameStr.data());
|
||||||
if (location == -1) {
|
if (location == -1) {
|
||||||
log::warn("Uniform '{}' not found in shader", name);
|
log::warn("Uniform '{}' not found in shader", name);
|
||||||
m_uniforms[name.data()] = -1;
|
m_uniforms[nameStr] = -1;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_uniforms[name.data()] = location;
|
m_uniforms[nameStr] = location;
|
||||||
uniform = m_uniforms.find(name.data());
|
uniform = m_uniforms.find(nameStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return uniform->second;
|
return uniform->second;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <hex/helpers/patches.hpp>
|
#include <hex/helpers/patches.hpp>
|
||||||
|
|
||||||
#include <hex/helpers/utils.hpp>
|
#include <hex/helpers/utils.hpp>
|
||||||
@@ -5,7 +6,6 @@
|
|||||||
#include <hex/providers/provider.hpp>
|
#include <hex/providers/provider.hpp>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
@@ -24,7 +24,7 @@ namespace hex {
|
|||||||
[[nodiscard]] bool isSavable() const override { return false; }
|
[[nodiscard]] bool isSavable() const override { return false; }
|
||||||
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
|
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
|
||||||
|
|
||||||
[[nodiscard]] bool open() override { return true; }
|
[[nodiscard]] OpenResult open() override { return {}; }
|
||||||
void close() override { }
|
void close() override { }
|
||||||
|
|
||||||
void readRaw(u64 offset, void *buffer, size_t size) override {
|
void readRaw(u64 offset, void *buffer, size_t size) override {
|
||||||
@@ -83,7 +83,7 @@ namespace hex {
|
|||||||
|
|
||||||
[[nodiscard]] UnlocalizedString getTypeName() const override { return ""; }
|
[[nodiscard]] UnlocalizedString getTypeName() const override { return ""; }
|
||||||
|
|
||||||
const std::map<u64, u8>& getPatches() const {
|
[[nodiscard]] const std::map<u64, u8>& getPatches() const {
|
||||||
return m_patches;
|
return m_patches;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
@@ -92,7 +92,7 @@ namespace hex {
|
|||||||
|
|
||||||
|
|
||||||
void pushStringBack(std::vector<u8> &buffer, const std::string &string) {
|
void pushStringBack(std::vector<u8> &buffer, const std::string &string) {
|
||||||
std::copy(string.begin(), string.end(), std::back_inserter(buffer));
|
std::ranges::copy(string, std::back_inserter(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include <microtar.h>
|
#include <microtar.h>
|
||||||
|
|
||||||
|
#include "wolv/utils/string.hpp"
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
using namespace hex::literals;
|
using namespace hex::literals;
|
||||||
@@ -22,13 +24,13 @@ namespace hex {
|
|||||||
|
|
||||||
m_ctx = std::make_unique<mtar_t>();
|
m_ctx = std::make_unique<mtar_t>();
|
||||||
|
|
||||||
auto shortPath = wolv::io::fs::toShortPath(path);
|
auto shortPath = wolv::io::fs::toNormalizedPathString(wolv::io::fs::toShortPath(path));
|
||||||
if (mode == Tar::Mode::Read)
|
if (mode == Tar::Mode::Read)
|
||||||
tarError = mtar_open(m_ctx.get(), shortPath.string().c_str(), "r");
|
tarError = mtar_open(m_ctx.get(), shortPath.c_str(), "r");
|
||||||
else if (mode == Tar::Mode::Write)
|
else if (mode == Tar::Mode::Write)
|
||||||
tarError = mtar_open(m_ctx.get(), shortPath.string().c_str(), "a");
|
tarError = mtar_open(m_ctx.get(), shortPath.c_str(), "a");
|
||||||
else if (mode == Tar::Mode::Create)
|
else if (mode == Tar::Mode::Create)
|
||||||
tarError = mtar_open(m_ctx.get(), shortPath.string().c_str(), "w");
|
tarError = mtar_open(m_ctx.get(), shortPath.c_str(), "w");
|
||||||
else
|
else
|
||||||
tarError = MTAR_EFAILURE;
|
tarError = MTAR_EFAILURE;
|
||||||
|
|
||||||
@@ -187,4 +189,4 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
UDPServer::UDPServer(u16 port, Callback callback)
|
UDPServer::UDPServer(u16 port, Callback callback)
|
||||||
: m_port(port), m_callback(std::move(callback)), m_running(false), m_socketFd(-1) {
|
: m_port(port), m_callback(std::move(callback)), m_running(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
UDPServer::~UDPServer() {
|
UDPServer::~UDPServer() {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <hex/helpers/utils.hpp>
|
#include <hex/helpers/utils.hpp>
|
||||||
|
|
||||||
@@ -29,10 +30,12 @@
|
|||||||
#elif defined(OS_LINUX)
|
#elif defined(OS_LINUX)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <spawn.h>
|
||||||
#include <hex/helpers/utils_linux.hpp>
|
#include <hex/helpers/utils_linux.hpp>
|
||||||
#elif defined(OS_MACOS)
|
#elif defined(OS_MACOS)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <spawn.h>
|
||||||
#include <hex/helpers/utils_macos.hpp>
|
#include <hex/helpers/utils_macos.hpp>
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#elif defined(OS_WEB)
|
#elif defined(OS_WEB)
|
||||||
@@ -98,7 +101,7 @@ namespace hex {
|
|||||||
string = wolv::util::replaceStrings(string, ",", "");
|
string = wolv::util::replaceStrings(string, ",", "");
|
||||||
|
|
||||||
// Check for non-hex characters
|
// Check for non-hex characters
|
||||||
bool isValidHexString = std::find_if(string.begin(), string.end(), [](char c) {
|
bool isValidHexString = std::ranges::find_if(string, [](char c) {
|
||||||
return !std::isxdigit(c) && !std::isspace(c);
|
return !std::isxdigit(c) && !std::isspace(c);
|
||||||
}) == string.end();
|
}) == string.end();
|
||||||
|
|
||||||
@@ -309,6 +312,86 @@ namespace hex {
|
|||||||
return ::system(command.c_str());
|
return ::system(command.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> executeCommandWithOutput(const std::string &command) {
|
||||||
|
std::array<char, 256> buffer = {};
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
#if defined(OS_WINDOWS)
|
||||||
|
FILE* pipe = _popen(command.c_str(), "r");
|
||||||
|
#else
|
||||||
|
FILE* pipe = popen(command.c_str(), "r");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!pipe) {
|
||||||
|
hex::log::error("Failed to open pipe for command: {}", command);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
|
||||||
|
result += buffer.data();
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
hex::log::error("Exception while reading command output: {}", e.what());
|
||||||
|
|
||||||
|
#if defined(OS_WINDOWS)
|
||||||
|
_pclose(pipe);
|
||||||
|
#else
|
||||||
|
pclose(pipe);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(OS_WINDOWS)
|
||||||
|
int exitCode = _pclose(pipe);
|
||||||
|
#else
|
||||||
|
int exitCode = pclose(pipe);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (exitCode != 0) {
|
||||||
|
hex::log::debug("Command exited with code {}: {}", exitCode, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeCommandDetach(const std::string &command) {
|
||||||
|
#if defined(OS_WINDOWS)
|
||||||
|
STARTUPINFOA si = { };
|
||||||
|
PROCESS_INFORMATION pi = { };
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
|
||||||
|
DWORD flags = CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW;
|
||||||
|
std::string cmdCopy = command;
|
||||||
|
|
||||||
|
BOOL result = ::CreateProcessA(
|
||||||
|
nullptr,
|
||||||
|
cmdCopy.data(),
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
false,
|
||||||
|
flags,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
&si,
|
||||||
|
&pi
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
::CloseHandle(pi.hProcess);
|
||||||
|
::CloseHandle(pi.hThread);
|
||||||
|
}
|
||||||
|
#elif defined(OS_MACOS) || defined(OS_LINUX)
|
||||||
|
pid_t pid;
|
||||||
|
const char* argv[] = { "sh", "-c", command.c_str(), nullptr };
|
||||||
|
|
||||||
|
::posix_spawnp(&pid, "sh", nullptr, nullptr, const_cast<char* const*>(argv), nullptr);
|
||||||
|
#elif defined(OS_WEB)
|
||||||
|
std::ignore = command;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void openWebpage(std::string url) {
|
void openWebpage(std::string url) {
|
||||||
if (!url.contains("://"))
|
if (!url.contains("://"))
|
||||||
url = "https://" + url;
|
url = "https://" + url;
|
||||||
@@ -463,7 +546,7 @@ namespace hex {
|
|||||||
if (ch <= 0x7F) {
|
if (ch <= 0x7F) {
|
||||||
unicode = ch;
|
unicode = ch;
|
||||||
unicodeSize = 0;
|
unicodeSize = 0;
|
||||||
} else if (ch <= 0xBF) {
|
} else if (ch <= 0xBF) { //NOLINT(bugprone-branch-clone)
|
||||||
return { };
|
return { };
|
||||||
} else if (ch <= 0xDF) {
|
} else if (ch <= 0xDF) {
|
||||||
unicode = ch&0x1F;
|
unicode = ch&0x1F;
|
||||||
@@ -524,7 +607,7 @@ namespace hex {
|
|||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
if (wch < 0xD800 || wch > 0xDFFF) {
|
if (wch < 0xD800 || wch > 0xDFFF) {
|
||||||
unicode = static_cast<u32>(wch);
|
unicode = static_cast<u32>(wch); // NOLINT(cert-str34-c)
|
||||||
} else if (wch >= 0xD800 && wch <= 0xDBFF) {
|
} else if (wch >= 0xD800 && wch <= 0xDBFF) {
|
||||||
if (index == utf16.size())
|
if (index == utf16.size())
|
||||||
return "";
|
return "";
|
||||||
@@ -785,7 +868,7 @@ namespace hex {
|
|||||||
input.imbue(std::locale(std::setlocale(LC_ALL, nullptr)));
|
input.imbue(std::locale(std::setlocale(LC_ALL, nullptr)));
|
||||||
|
|
||||||
tm time = {};
|
tm time = {};
|
||||||
input >> std::get_time(&time, format.data());
|
input >> std::get_time(&time, std::string(format).data());
|
||||||
if (input.fail()) {
|
if (input.fail()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -824,10 +907,10 @@ namespace hex {
|
|||||||
|
|
||||||
if (lang.has_value() && !lang->empty() && *lang != "C" && *lang != "C.UTF-8") {
|
if (lang.has_value() && !lang->empty() && *lang != "C" && *lang != "C.UTF-8") {
|
||||||
auto parts = wolv::util::splitString(*lang, ".");
|
auto parts = wolv::util::splitString(*lang, ".");
|
||||||
if (parts.size() > 0)
|
if (!parts.empty())
|
||||||
return parts[0];
|
return parts[0];
|
||||||
else
|
else
|
||||||
return *lang;
|
return lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -905,6 +988,12 @@ namespace hex {
|
|||||||
nid.dwInfoFlags = NIIF_INFO;
|
nid.dwInfoFlags = NIIF_INFO;
|
||||||
|
|
||||||
Shell_NotifyIcon(NIM_ADD, &nid);
|
Shell_NotifyIcon(NIM_ADD, &nid);
|
||||||
|
|
||||||
|
Sleep(100);
|
||||||
|
|
||||||
|
Shell_NotifyIcon(NIM_DELETE, &nid);
|
||||||
|
CloseWindow(hwnd);
|
||||||
|
DestroyWindow(hwnd);
|
||||||
#elif defined(OS_MACOS)
|
#elif defined(OS_MACOS)
|
||||||
toastMessageMacos(title.c_str(), message.c_str());
|
toastMessageMacos(title.c_str(), message.c_str());
|
||||||
#elif defined(OS_LINUX)
|
#elif defined(OS_LINUX)
|
||||||
@@ -913,17 +1002,21 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
#elif defined(OS_WEB)
|
#elif defined(OS_WEB)
|
||||||
EM_ASM({
|
EM_ASM({
|
||||||
const t = UTF8ToString($0);
|
try {
|
||||||
const m = UTF8ToString($1);
|
const t = UTF8ToString($0);
|
||||||
|
const m = UTF8ToString($1);
|
||||||
|
|
||||||
if (Notification.permission === "granted") {
|
if (Notification.permission === "granted") {
|
||||||
new Notification(t, { body: m });
|
new Notification(t, { body: m });
|
||||||
} else if (Notification.permission !== "denied") {
|
} else if (Notification.permission !== "denied") {
|
||||||
Notification.requestPermission().then(function(p) {
|
Notification.requestPermission().then(function(p) {
|
||||||
if (p === "granted") {
|
if (p === "granted") {
|
||||||
new Notification(t, { body: m });
|
new Notification(t, { body: m });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
}
|
}
|
||||||
}, title.c_str(), message.c_str());
|
}, title.c_str(), message.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ namespace hex {
|
|||||||
|
|
||||||
void executeCmd(const std::vector<std::string> &argsVector) {
|
void executeCmd(const std::vector<std::string> &argsVector) {
|
||||||
std::vector<char*> cArgsVector;
|
std::vector<char*> cArgsVector;
|
||||||
for (const auto &str : argsVector) {
|
cArgsVector.reserve(argsVector.size());
|
||||||
|
for (const auto &str : argsVector) {
|
||||||
cArgsVector.push_back(const_cast<char*>(str.c_str()));
|
cArgsVector.push_back(const_cast<char*>(str.c_str()));
|
||||||
}
|
}
|
||||||
cArgsVector.push_back(nullptr);
|
cArgsVector.push_back(nullptr);
|
||||||
|
|
||||||
if (fork() == 0) {
|
if (fork() == 0) {
|
||||||
execvp(cArgsVector[0], &cArgsVector[0]);
|
execvp(cArgsVector[0], cArgsVector.data());
|
||||||
log::error("execvp() failed: {}", strerror(errno));
|
log::error("execvp() failed: {}", strerror(errno));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#if defined(OS_MACOS)
|
#if defined(OS_MACOS)
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
|
||||||
#include <CoreFoundation/CFBundle.h>
|
#include <CoreFoundation/CFBundle.h>
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
#include <Foundation/NSUserDefaults.h>
|
#include <Foundation/NSUserDefaults.h>
|
||||||
@@ -70,6 +73,49 @@
|
|||||||
cocoaWindow.titlebarAppearsTransparent = YES;
|
cocoaWindow.titlebarAppearsTransparent = YES;
|
||||||
cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
|
cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||||
|
|
||||||
|
// Setup liquid glass background effect
|
||||||
|
{
|
||||||
|
NSView* glfwContentView = [cocoaWindow contentView];
|
||||||
|
|
||||||
|
NSOpenGLContext* context = [NSOpenGLContext currentContext];
|
||||||
|
if (!context) {
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
context = [NSOpenGLContext currentContext];
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint opaque = 0;
|
||||||
|
[context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
|
||||||
|
[context update];
|
||||||
|
|
||||||
|
NSView* containerView = [[NSView alloc] initWithFrame:[glfwContentView frame]];
|
||||||
|
containerView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
|
[containerView setWantsLayer:YES];
|
||||||
|
|
||||||
|
Class glassEffectClass = NSClassFromString(@"NSGlassEffectView");
|
||||||
|
NSView* effectView = nil;
|
||||||
|
if (glassEffectClass) {
|
||||||
|
// Use the new liquid glass effect
|
||||||
|
effectView = [[glassEffectClass alloc] initWithFrame:[containerView bounds]];
|
||||||
|
} else {
|
||||||
|
// Fall back to NSVisualEffectView for older systems
|
||||||
|
NSVisualEffectView* visualEffectView = [[NSVisualEffectView alloc] initWithFrame:[containerView bounds]];
|
||||||
|
visualEffectView.material = NSVisualEffectMaterialHUDWindow;
|
||||||
|
visualEffectView.blendingMode = NSVisualEffectBlendingModeBehindWindow;
|
||||||
|
visualEffectView.state = NSVisualEffectStateActive;
|
||||||
|
effectView = visualEffectView;
|
||||||
|
}
|
||||||
|
|
||||||
|
effectView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
|
|
||||||
|
[containerView addSubview:effectView];
|
||||||
|
|
||||||
|
[glfwContentView removeFromSuperview];
|
||||||
|
glfwContentView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
|
[containerView addSubview:glfwContentView];
|
||||||
|
|
||||||
|
[cocoaWindow setContentView:containerView];
|
||||||
|
}
|
||||||
|
|
||||||
[cocoaWindow setOpaque:NO];
|
[cocoaWindow setOpaque:NO];
|
||||||
[cocoaWindow setHasShadow:YES];
|
[cocoaWindow setHasShadow:YES];
|
||||||
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
|
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
|
||||||
@@ -370,6 +416,27 @@
|
|||||||
return [bundlePath.pathExtension.lowercaseString isEqualToString:@"app"];
|
return [bundlePath.pathExtension.lowercaseString isEqualToString:@"app"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface NotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NotificationDelegate
|
||||||
|
|
||||||
|
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||||
|
willPresentNotification:(UNNotification *)notification
|
||||||
|
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
|
||||||
|
if (@available(macOS 11.0, *)) {
|
||||||
|
completionHandler(UNNotificationPresentationOptionBanner |
|
||||||
|
UNNotificationPresentationOptionSound |
|
||||||
|
UNNotificationPresentationOptionList);
|
||||||
|
} else {
|
||||||
|
// For macOS 10.15 and earlier
|
||||||
|
completionHandler(UNNotificationPresentationOptionAlert |
|
||||||
|
UNNotificationPresentationOptionSound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
void toastMessageMacos(const char *title, const char *message) {
|
void toastMessageMacos(const char *title, const char *message) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
// Only show notification if we're inside a bundle
|
// Only show notification if we're inside a bundle
|
||||||
@@ -407,5 +474,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
48
lib/libimhex/source/mcp/client.cpp
Normal file
48
lib/libimhex/source/mcp/client.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include <hex/mcp/client.hpp>
|
||||||
|
#include <hex/mcp/server.hpp>
|
||||||
|
|
||||||
|
#include <hex.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <hex/api/imhex_api/system.hpp>
|
||||||
|
#include <hex/helpers/logger.hpp>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <wolv/literals.hpp>
|
||||||
|
#include <wolv/net/socket_client.hpp>
|
||||||
|
|
||||||
|
namespace hex::mcp {
|
||||||
|
|
||||||
|
using namespace wolv::literals;
|
||||||
|
|
||||||
|
int Client::run(std::istream &input, std::ostream &output) {
|
||||||
|
wolv::net::SocketClient client(wolv::net::SocketClient::Type::TCP, true);
|
||||||
|
client.connect("127.0.0.1", Server::McpInternalPort);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::string request;
|
||||||
|
std::getline(input, request);
|
||||||
|
|
||||||
|
if (ImHexApi::System::isMainInstance()) {
|
||||||
|
JsonRpc response(request);
|
||||||
|
response.setError(JsonRpc::ErrorCode::InternalError, "No other instance of ImHex is running. Make sure that you have ImHex open already.");
|
||||||
|
output << response.execute([](auto, auto){ return nlohmann::json::object(); }).value_or("") << '\n';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.writeString(request);
|
||||||
|
auto response = client.readBytesUntil(0x00);
|
||||||
|
if (!response.empty() && response.front() != 0x00)
|
||||||
|
output << std::string(response.begin(), response.end()) << std::endl;
|
||||||
|
|
||||||
|
if (!client.isConnected())
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
243
lib/libimhex/source/mcp/server.cpp
Normal file
243
lib/libimhex/source/mcp/server.cpp
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
#include <hex/mcp/server.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <hex/helpers/logger.hpp>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <utility>
|
||||||
|
#include <hex/api/task_manager.hpp>
|
||||||
|
#include <wolv/net/socket_client.hpp>
|
||||||
|
#include <hex/api/imhex_api/system.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace hex::mcp {
|
||||||
|
|
||||||
|
std::optional<std::string> JsonRpc::execute(const Callback &callback) {
|
||||||
|
try {
|
||||||
|
auto requestJson = nlohmann::json::parse(m_request);
|
||||||
|
|
||||||
|
if (requestJson.is_array()) {
|
||||||
|
return handleBatchedMessages(requestJson, callback).transform([](const auto &response) { return response.dump(); });
|
||||||
|
} else {
|
||||||
|
return handleMessage(requestJson, callback).transform([](const auto &response) { return response.dump(); });
|
||||||
|
}
|
||||||
|
} catch (const nlohmann::json::exception &) {
|
||||||
|
return createErrorMessage(ErrorCode::ParseError, "Parse error").dump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonRpc::setError(ErrorCode code, std::string message) {
|
||||||
|
m_error = Error{ code, std::move(message) };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<nlohmann::json> JsonRpc::handleMessage(const nlohmann::json &request, const Callback &callback) {
|
||||||
|
try {
|
||||||
|
// Validate JSON-RPC request
|
||||||
|
if (!request.contains("jsonrpc") || request["jsonrpc"] != "2.0" ||
|
||||||
|
!request.contains("method") || !request["method"].is_string()) {
|
||||||
|
m_id = request.contains("id") ? std::optional(request["id"].get<int>()) : std::nullopt;
|
||||||
|
|
||||||
|
return createErrorMessage(ErrorCode::InvalidRequest, "Invalid Request");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_id = request.contains("id") ? std::optional(request["id"].get<int>()) : std::nullopt;
|
||||||
|
|
||||||
|
// Return a user-specified error if set
|
||||||
|
if (m_error.has_value()) {
|
||||||
|
return createErrorMessage(m_error->code, m_error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the method
|
||||||
|
auto result = callback(request["method"].get<std::string>(), request.value("params", nlohmann::json::object()));
|
||||||
|
|
||||||
|
if (!m_id.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return createResponseMessage(result.is_null() ? nlohmann::json::object() : result);
|
||||||
|
} catch (const MethodNotFoundException &) {
|
||||||
|
return createErrorMessage(ErrorCode::MethodNotFound, "Method not found");
|
||||||
|
} catch (const InvalidParametersException &) {
|
||||||
|
return createErrorMessage(ErrorCode::InvalidParams, "Invalid params");
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
return createErrorMessage(ErrorCode::InternalError, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<nlohmann::json> JsonRpc::handleBatchedMessages(const nlohmann::json &request, const Callback &callback) {
|
||||||
|
if (!request.is_array()) {
|
||||||
|
return createErrorMessage(ErrorCode::InvalidRequest, "Invalid Request");
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json responses = nlohmann::json::array();
|
||||||
|
for (const auto &message : request) {
|
||||||
|
auto response = handleMessage(message, callback);
|
||||||
|
if (response.has_value())
|
||||||
|
responses.push_back(*response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responses.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json JsonRpc::createDefaultMessage() {
|
||||||
|
nlohmann::json message;
|
||||||
|
message["jsonrpc"] = "2.0";
|
||||||
|
if (m_id.has_value())
|
||||||
|
message["id"] = m_id.value();
|
||||||
|
else
|
||||||
|
message["id"] = nullptr;
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json JsonRpc::createErrorMessage(ErrorCode code, const std::string &message) {
|
||||||
|
auto json = createDefaultMessage();
|
||||||
|
json["error"] = {
|
||||||
|
{ "code", int(code) },
|
||||||
|
{ "message", message }
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json JsonRpc::createResponseMessage(const nlohmann::json &result) {
|
||||||
|
auto json = createDefaultMessage();
|
||||||
|
json["result"] = result;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
Server::Server() : m_server(McpInternalPort, 1024, 1, true) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Server::~Server() {
|
||||||
|
this->shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::listen() {
|
||||||
|
m_server.accept([this](auto, const std::vector<u8> &data) -> std::vector<u8> {
|
||||||
|
std::string request(data.begin(), data.end());
|
||||||
|
|
||||||
|
TaskManager::setCurrentThreadName("MCP Server");
|
||||||
|
log::debug("MCP ----> {}", request);
|
||||||
|
|
||||||
|
JsonRpc rpc(request);
|
||||||
|
auto response = rpc.execute([this](const std::string &method, const nlohmann::json ¶ms) -> nlohmann::json {
|
||||||
|
if (method == "initialize") {
|
||||||
|
return handleInitialize(params);
|
||||||
|
} else if (method.starts_with("notifications/")) {
|
||||||
|
handleNotifications(method.substr(14), params);
|
||||||
|
return {};
|
||||||
|
} else if (method.ends_with("/list")) {
|
||||||
|
auto primitiveName = method.substr(0, method.size() - 5);
|
||||||
|
if (m_primitives.contains(primitiveName)) {
|
||||||
|
nlohmann::json capabilitiesList = nlohmann::json::array();
|
||||||
|
for (const auto &[name, primitive] : m_primitives[primitiveName]) {
|
||||||
|
capabilitiesList.push_back(primitive.capabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json result;
|
||||||
|
result[primitiveName] = capabilitiesList;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else if (method.ends_with("/call")) {
|
||||||
|
auto primitive = method.substr(0, method.size() - 5);
|
||||||
|
if (auto primitiveIt = m_primitives.find(primitive); primitiveIt != m_primitives.end()) {
|
||||||
|
auto name = params.value("name", "");
|
||||||
|
if (auto functionIt = primitiveIt->second.find(name); functionIt != primitiveIt->second.end()) {
|
||||||
|
auto result = functionIt->second.function(params.value("arguments", nlohmann::json::object()));
|
||||||
|
|
||||||
|
return result.is_null() ? nlohmann::json::object() : result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw JsonRpc::MethodNotFoundException();
|
||||||
|
});
|
||||||
|
|
||||||
|
log::debug("MCP <---- {}", response.value_or("<Nothing>"));
|
||||||
|
|
||||||
|
if (response.has_value()) {
|
||||||
|
response->push_back(0x00);
|
||||||
|
return { response->begin(), response->end() };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return std::vector<u8>{ 0x00 };
|
||||||
|
}, [this](auto) {
|
||||||
|
log::info("MCP client disconnected");
|
||||||
|
m_connected = false;
|
||||||
|
m_clientInfo = {};
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::shutdown() {
|
||||||
|
m_server.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::disconnect() {
|
||||||
|
m_server.disconnectClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::addPrimitive(std::string type, std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json ¶ms)> function) {
|
||||||
|
auto json = nlohmann::json::parse(capabilities);
|
||||||
|
auto name = json["name"].get<std::string>();
|
||||||
|
|
||||||
|
m_primitives[type][name] = {
|
||||||
|
.capabilities=json,
|
||||||
|
.function=function
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nlohmann::json Server::handleInitialize(const nlohmann::json ¶ms) {
|
||||||
|
constexpr static auto ServerName = "ImHex";
|
||||||
|
constexpr static auto ProtocolVersion = "2025-06-18";
|
||||||
|
|
||||||
|
m_clientInfo = {};
|
||||||
|
|
||||||
|
if (params.contains("protocolVersion")) {
|
||||||
|
auto clientProtocolVersion = params["protocolVersion"].get<std::string>();
|
||||||
|
m_clientInfo.protocolVersion = clientProtocolVersion;
|
||||||
|
} else {
|
||||||
|
throw JsonRpc::InvalidParametersException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.contains("clientInfo")) {
|
||||||
|
const auto &clientInfo = params["clientInfo"];
|
||||||
|
m_clientInfo.name = clientInfo.value("name", "???");
|
||||||
|
m_clientInfo.version = clientInfo.value("version", "???");
|
||||||
|
|
||||||
|
log::info("MCP client connected: {} v{} (protocol {})", m_clientInfo.name, m_clientInfo.version, m_clientInfo.protocolVersion);
|
||||||
|
} else {
|
||||||
|
log::info("MCP client connected: Unknown client info");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
{ "protocolVersion", ProtocolVersion },
|
||||||
|
{
|
||||||
|
"capabilities",
|
||||||
|
{
|
||||||
|
{ "tools", nlohmann::json::object() },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"serverInfo", {
|
||||||
|
{ "name", ServerName },
|
||||||
|
{ "version", ImHexApi::System::getImHexVersion().get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::handleNotifications(const std::string &method, [[maybe_unused]] const nlohmann::json ¶ms) {
|
||||||
|
if (method == "initialized") {
|
||||||
|
m_connected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::isConnected() {
|
||||||
|
return m_connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ namespace hex::prv {
|
|||||||
clearCache();
|
clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CachedProvider::open() {
|
Provider::OpenResult CachedProvider::open() {
|
||||||
clearCache();
|
clearCache();
|
||||||
return true;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CachedProvider::close() {
|
void CachedProvider::close() {
|
||||||
@@ -49,7 +49,7 @@ namespace hex::prv {
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(m_cacheMutex);
|
std::unique_lock lock(m_cacheMutex);
|
||||||
m_cache[cacheSlot] = Block{blockIndex, std::move(blockData), false};
|
m_cache[cacheSlot] = Block{.index=blockIndex, .data=std::move(blockData), .dirty=false};
|
||||||
std::copy_n(m_cache[cacheSlot]->data.begin() + blockOffset, toRead, out);
|
std::copy_n(m_cache[cacheSlot]->data.begin() + blockOffset, toRead, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ namespace hex::prv {
|
|||||||
if (!slot || slot->index != blockIndex) {
|
if (!slot || slot->index != blockIndex) {
|
||||||
std::vector<uint8_t> blockData(m_cacheBlockSize);
|
std::vector<uint8_t> blockData(m_cacheBlockSize);
|
||||||
readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize);
|
readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize);
|
||||||
slot = Block { blockIndex, std::move(blockData), false };
|
slot = Block { .index=blockIndex, .data=std::move(blockData), .dirty=false };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy_n(in, toWrite, slot->data.begin() + blockOffset);
|
std::copy_n(in, toWrite, slot->data.begin() + blockOffset);
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
namespace hex::prv {
|
namespace hex::prv {
|
||||||
|
|
||||||
bool MemoryProvider::open() {
|
Provider::OpenResult MemoryProvider::open() {
|
||||||
if (m_data.empty()) {
|
if (m_data.empty()) {
|
||||||
m_data.resize(1);
|
m_data.resize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
void MemoryProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
||||||
|
|||||||
@@ -7,12 +7,14 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <hex/api/content_registry/settings.hpp>
|
||||||
|
|
||||||
#include <hex/helpers/magic.hpp>
|
#include <hex/helpers/magic.hpp>
|
||||||
#include <wolv/io/file.hpp>
|
#include <wolv/io/file.hpp>
|
||||||
#include <wolv/literals.hpp>
|
#include <wolv/literals.hpp>
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <wolv/utils/string.hpp>
|
||||||
|
|
||||||
namespace hex::prv {
|
namespace hex::prv {
|
||||||
|
|
||||||
@@ -24,6 +26,28 @@ namespace hex::prv {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IProviderDataBackupable::IProviderDataBackupable(Provider* provider) : m_provider(provider) {
|
||||||
|
m_shouldCreateBackups = ContentRegistry::Settings::read<bool>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.enable", true);
|
||||||
|
m_maxSize = ContentRegistry::Settings::read<u32>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.max_size", 1_MiB);
|
||||||
|
m_backupExtension = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.extension", ".bak");
|
||||||
|
}
|
||||||
|
|
||||||
|
void IProviderDataBackupable::createBackupIfNeeded(const std::fs::path &inputFilePath) {
|
||||||
|
if (!m_shouldCreateBackups || m_backupCreated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_provider->getActualSize() > m_maxSize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::fs::path backupFilePath = wolv::util::toUTF8String(inputFilePath) + m_backupExtension;
|
||||||
|
if (wolv::io::fs::copyFile(inputFilePath, backupFilePath, std::fs::copy_options::overwrite_existing)) {
|
||||||
|
if (wolv::io::fs::exists(backupFilePath)) {
|
||||||
|
m_backupCreated = true;
|
||||||
|
log::info("Created backup of provider data at '{}'", backupFilePath.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {
|
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {
|
||||||
|
|
||||||
@@ -33,7 +57,7 @@ namespace hex::prv {
|
|||||||
m_overlays.clear();
|
m_overlays.clear();
|
||||||
|
|
||||||
if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value() && selection->provider == this)
|
if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value() && selection->provider == this)
|
||||||
EventRegionSelected::post(ImHexApi::HexEditor::ProviderRegion { { 0x00, 0x00 }, nullptr });
|
EventRegionSelected::post(ImHexApi::HexEditor::ProviderRegion { { .address=0x00, .size=0x00 }, nullptr });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Provider::read(u64 offset, void *buffer, size_t size, bool overlays) {
|
void Provider::read(u64 offset, void *buffer, size_t size, bool overlays) {
|
||||||
@@ -109,7 +133,6 @@ namespace hex::prv {
|
|||||||
this->resizeRaw(newSize);
|
this->resizeRaw(newSize);
|
||||||
|
|
||||||
std::vector<u8> buffer(0x1000, 0x00);
|
std::vector<u8> buffer(0x1000, 0x00);
|
||||||
const std::vector<u8> zeroBuffer(0x1000, 0x00);
|
|
||||||
|
|
||||||
auto position = oldSize;
|
auto position = oldSize;
|
||||||
while (position > offset) {
|
while (position > offset) {
|
||||||
@@ -118,9 +141,18 @@ namespace hex::prv {
|
|||||||
position -= readSize;
|
position -= readSize;
|
||||||
|
|
||||||
this->readRaw(position, buffer.data(), readSize);
|
this->readRaw(position, buffer.data(), readSize);
|
||||||
this->writeRaw(position, zeroBuffer.data(), newSize - oldSize);
|
|
||||||
this->writeRaw(position + size, buffer.data(), readSize);
|
this->writeRaw(position + size, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr static std::array<u8, 0x1000> ZeroBuffer = {};
|
||||||
|
auto zeroPosition = offset;
|
||||||
|
auto remainingZeros = size;
|
||||||
|
while (remainingZeros > 0) {
|
||||||
|
const auto writeSize = std::min<size_t>(remainingZeros, ZeroBuffer.size());
|
||||||
|
this->writeRaw(zeroPosition, ZeroBuffer.data(), writeSize);
|
||||||
|
zeroPosition += writeSize;
|
||||||
|
remainingZeros -= writeSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Provider::removeRaw(u64 offset, u64 size) {
|
void Provider::removeRaw(u64 offset, u64 size) {
|
||||||
@@ -265,19 +297,19 @@ namespace hex::prv {
|
|||||||
u64 absoluteAddress = address - this->getBaseAddress();
|
u64 absoluteAddress = address - this->getBaseAddress();
|
||||||
|
|
||||||
if (absoluteAddress < this->getActualSize())
|
if (absoluteAddress < this->getActualSize())
|
||||||
return { Region { this->getBaseAddress() + absoluteAddress, this->getActualSize() - absoluteAddress }, true };
|
return { Region { .address=this->getBaseAddress() + absoluteAddress, .size=this->getActualSize() - absoluteAddress }, true };
|
||||||
|
|
||||||
|
|
||||||
bool insideValidRegion = false;
|
bool insideValidRegion = false;
|
||||||
|
|
||||||
std::optional<u64> nextRegionAddress;
|
std::optional<u64> nextRegionAddress;
|
||||||
for (const auto &overlay : m_overlays) {
|
for (const auto &overlay : m_overlays) {
|
||||||
Region overlayRegion = { overlay->getAddress(), overlay->getSize() };
|
Region overlayRegion = { .address=overlay->getAddress(), .size=overlay->getSize() };
|
||||||
if (!nextRegionAddress.has_value() || overlay->getAddress() < nextRegionAddress) {
|
if (!nextRegionAddress.has_value() || overlay->getAddress() < nextRegionAddress) {
|
||||||
nextRegionAddress = overlayRegion.getStartAddress();
|
nextRegionAddress = overlayRegion.getStartAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Region { address, 1 }.overlaps(overlayRegion)) {
|
if (Region { .address=address, .size=1 }.overlaps(overlayRegion)) {
|
||||||
insideValidRegion = true;
|
insideValidRegion = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,7 +317,7 @@ namespace hex::prv {
|
|||||||
if (!nextRegionAddress.has_value())
|
if (!nextRegionAddress.has_value())
|
||||||
return { Region::Invalid(), false };
|
return { Region::Invalid(), false };
|
||||||
else
|
else
|
||||||
return { Region { address, *nextRegionAddress - address }, insideValidRegion };
|
return { Region { .address=address, .size=*nextRegionAddress - address }, insideValidRegion };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user