mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
Compare commits
290 Commits
releases/v
...
nightly
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
---
|
||||
Checks: '-*,
|
||||
mpi-*,
|
||||
bugprone-*,
|
||||
-bugprone-signal-handler,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-redundant-branch-condition,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-shared-ptr-array-mismatch,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-signed-char-misuse,
|
||||
-bugprone-unhandled-exception-at-new,
|
||||
-bugprone-infinite-loop,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
cert-err52-cpp,
|
||||
cert-err60-cpp,
|
||||
cert-err34-c,
|
||||
cert-str34-c,
|
||||
cert-dcl21-cpp,
|
||||
cert-msc50-cpp,
|
||||
cert-msc51-cpp,
|
||||
cert-dcl58-cpp,
|
||||
cert-flp30-c,
|
||||
cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
cppcoreguidelines-pro-type-member-init,
|
||||
cppcoreguidelines-slicing,
|
||||
cppcoreguidelines-interfaces-global-init,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
cppcoreguidelines-narrowing-conversions,
|
||||
google-default-arguments,
|
||||
google-runtime-operator,
|
||||
google-explicit-constructor,
|
||||
hicpp-multiway-paths-covered,
|
||||
hicpp-exception-baseclass,
|
||||
misc-*,
|
||||
-misc-definitions-in-headers,
|
||||
-misc-unused-parameters,
|
||||
-misc-unused-alias-decls,
|
||||
-misc-use-anonymous-namespace,
|
||||
-misc-misleading-identifier,
|
||||
-misc-confusable-identifiers,
|
||||
-misc-misleading-bidirectional,
|
||||
-misc-static-assert,
|
||||
-misc-no-recursion,
|
||||
-misc-const-correctness,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
openmp-use-default-none,
|
||||
performance-*,
|
||||
-performance-no-int-to-ptr,
|
||||
portability-*,
|
||||
-portability-restrict-system-includes,
|
||||
readability-*,
|
||||
-readability-redundant-preprocessor,
|
||||
-readability-named-parameter,
|
||||
-readability-function-size,
|
||||
-readability-use-anyofallof,
|
||||
-readability-identifier-length,
|
||||
-readability-magic-numbers,
|
||||
-readability-braces-around-statements,
|
||||
-readability-suspicious-call-argument,
|
||||
-readability-isolate-declaration,
|
||||
-readability-else-after-return,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-identifier-naming,
|
||||
*-include-cleaner,
|
||||
-readability-qualified-auto'
|
||||
# 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.
|
||||
# Or at least an empty comment # to show they were put here explicitely,
|
||||
# and not as part of the historical CLion-generated rules
|
||||
# Note: `- -X` means disable X
|
||||
# CLI usage: go to the build directory and run: `run-clang-tidy -allow-no-checks -source-filter ".*/lib/.*" -fix -j`
|
||||
|
||||
Checks:
|
||||
- -*
|
||||
- mpi-*
|
||||
- bugprone-*
|
||||
- -bugprone-signal-handler
|
||||
- -bugprone-narrowing-conversions
|
||||
- -bugprone-redundant-branch-condition
|
||||
- -bugprone-exception-escape
|
||||
- -bugprone-shared-ptr-array-mismatch
|
||||
- -bugprone-implicit-widening-of-multiplication-result
|
||||
- -bugprone-signed-char-misuse
|
||||
- -bugprone-unhandled-exception-at-new
|
||||
- -bugprone-infinite-loop
|
||||
- -bugprone-easily-swappable-parameters
|
||||
- -bugprone-float-loop-counter #
|
||||
- -bugprone-unchecked-string-to-number-conversion # Unfortunately no alternative
|
||||
- -bugprone-branch-clone # Mostly warns about one-line duplicates
|
||||
- cert-err52-cpp
|
||||
- cert-err60-cpp
|
||||
- cert-str34-c
|
||||
- cert-dcl21-cpp
|
||||
- cert-msc50-cpp
|
||||
- cert-msc51-cpp
|
||||
- cert-dcl58-cpp
|
||||
- cppcoreguidelines-avoid-const-or-ref-data-members
|
||||
- cppcoreguidelines-pro-type-member-init # We want to use default member initializers
|
||||
- cppcoreguidelines-slicing
|
||||
- cppcoreguidelines-interfaces-global-init
|
||||
- -cppcoreguidelines-pro-type-static-cast-downcast # dynamic_cast has a runtime overhead
|
||||
- -cppcoreguidelines-narrowing-conversions #
|
||||
- google-runtime-operator
|
||||
- google-explicit-constructor
|
||||
- -google-default-arguments # Provider and ViewProvider read() is a good example of why this is useful
|
||||
- hicpp-multiway-paths-covered
|
||||
- hicpp-exception-baseclass
|
||||
- misc-*
|
||||
- -misc-definitions-in-headers
|
||||
- -misc-unused-parameters
|
||||
- -misc-unused-alias-decls
|
||||
- -misc-use-anonymous-namespace
|
||||
- -misc-misleading-identifier
|
||||
- -misc-confusable-identifiers
|
||||
- -misc-misleading-bidirectional
|
||||
- -misc-static-assert
|
||||
- -misc-no-recursion
|
||||
- -misc-const-correctness
|
||||
- -misc-use-internal-linkage # False positives if header where function is defined is not included
|
||||
- -misc-include-cleaner # Allow indirect includes
|
||||
- -misc-non-private-member-variables-in-classes #
|
||||
- modernize-*
|
||||
- -modernize-use-trailing-return-type
|
||||
- -modernize-use-std-print # We want to use fmt::print instead
|
||||
- -modernize-use-integer-sign-comparison # Too much occurrences to change
|
||||
- openmp-use-default-none
|
||||
- performance-*
|
||||
- -performance-no-int-to-ptr
|
||||
- portability-*
|
||||
- -portability-restrict-system-includes
|
||||
- readability-*
|
||||
- -readability-redundant-preprocessor
|
||||
- -readability-named-parameter
|
||||
- -readability-function-size
|
||||
- -readability-use-anyofallof
|
||||
- -readability-identifier-length
|
||||
- -readability-magic-numbers
|
||||
- -readability-braces-around-statements
|
||||
- -readability-suspicious-call-argument
|
||||
- -readability-isolate-declaration
|
||||
- -readability-else-after-return
|
||||
- -readability-redundant-access-specifiers
|
||||
- -readability-function-cognitive-complexity
|
||||
- -readability-identifier-naming
|
||||
- -readability-qualified-auto
|
||||
- -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
|
||||
|
||||
patreon: werwolv
|
||||
custom: https://werwolv.net/donate
|
||||
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
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -49,7 +53,7 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@main
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
id: cache-ccache
|
||||
with:
|
||||
key: ${{ runner.os }}-mingw-ccache-${{ github.run_id }}
|
||||
@@ -95,7 +99,6 @@ jobs:
|
||||
-DIMHEX_GENERATE_PDBS=ON \
|
||||
-DIMHEX_REPLACE_DWARF_WITH_PDB=ON \
|
||||
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \
|
||||
-DCPACK_WIX_VERSION="4" \
|
||||
-DCPACK_WIX_ROOT="$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools" \
|
||||
..
|
||||
|
||||
@@ -106,8 +109,8 @@ jobs:
|
||||
|
||||
- name: 🕯️ Install WiX Toolkit
|
||||
run: |
|
||||
"C:/Program Files/dotnet/dotnet.exe" tool install --global wix
|
||||
"$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools/wix" extension add -g WixToolset.UI.wixext
|
||||
"C:/Program Files/dotnet/dotnet.exe" tool install --global wix@6.0.2
|
||||
"$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools/wix" extension add --global WixToolset.UI.wixext/6.0.2
|
||||
|
||||
- name: 🪲 Create PDBs for MSI
|
||||
run: |
|
||||
@@ -166,6 +169,7 @@ jobs:
|
||||
|
||||
- name: ⬆️ Upload Windows Installer
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload-installer
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Windows Installer ${{ matrix.architecture_name }}
|
||||
@@ -233,7 +237,7 @@ jobs:
|
||||
arch: ${{ matrix.vs_arch }}
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@main
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
id: cache-ccache
|
||||
with:
|
||||
key: ${{ runner.os }}-msvc-${{ matrix.vs_arch }}-ccache-${{ github.run_id }}
|
||||
@@ -242,7 +246,7 @@ jobs:
|
||||
|
||||
- name: 📦 Install vcpkg
|
||||
uses: friendlyanon/setup-vcpkg@v1
|
||||
with: { committish: ef7dbf94b9198bc58f45951adcf1f041fcbc5ea0 }
|
||||
with: { committish: 66c0373dc7fca549e5803087b9487edfe3aca0a1 }
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
@@ -279,7 +283,6 @@ jobs:
|
||||
-DIMHEX_COMMIT_HASH_LONG="$env:GITHUB_SHA" `
|
||||
-DIMHEX_COMMIT_BRANCH="$($env:GITHUB_REF -replace '.*/', '')" `
|
||||
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" `
|
||||
-DCPACK_WIX_VERSION="4" `
|
||||
-DCPACK_WIX_ROOT="$($env:USERPROFILE -replace '\\','/')/.dotnet/tools" `
|
||||
.
|
||||
|
||||
@@ -290,8 +293,8 @@ jobs:
|
||||
|
||||
- name: 🕯️ Install WiX Toolkit
|
||||
run: |
|
||||
& "C:/Program Files/dotnet/dotnet.exe" tool install --global wix
|
||||
& "$($env:USERPROFILE -replace '\\','/')/.dotnet/tools/wix" extension add -g WixToolset.UI.wixext
|
||||
& "C:/Program Files/dotnet/dotnet.exe" tool install --global wix@6.0.2
|
||||
& "$($env:USERPROFILE -replace '\\','/')/.dotnet/tools/wix" extension add --global WixToolset.UI.wixext/6.0.2
|
||||
|
||||
- name: 📦 Bundle MSI
|
||||
run: |
|
||||
@@ -386,18 +389,7 @@ jobs:
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
strategy:
|
||||
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 }}
|
||||
name: 🍎 macOS 10.15 x86_64
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
@@ -412,76 +404,55 @@ jobs:
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}${{ matrix.file_suffix }}-ccache-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}${{ matrix.file_suffix }}-ccache
|
||||
key: ${{ runner.os }}-ccache-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-ccache
|
||||
max-size: 1G
|
||||
|
||||
- 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
|
||||
env:
|
||||
# Make brew not display useless errors
|
||||
HOMEBREW_TESTS: 1
|
||||
run: |
|
||||
brew reinstall python --quiet || true
|
||||
brew link --overwrite --quiet python 2>/dev/null || true
|
||||
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
|
||||
brew install llvm@21 automake
|
||||
sudo -E port install mbedtls3 nlohmann-json ccache freetype libmagic pkgconfig curl glfw ninja zlib xz bzip2 zstd libssh2 md4c
|
||||
|
||||
- name: ⬇️ Install .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
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
|
||||
- name: 🛠️ Configure CMake
|
||||
run: |
|
||||
set -x
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=$(brew --prefix llvm)/bin/clang \
|
||||
CXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
OBJC=$(brew --prefix llvm)/bin/clang \
|
||||
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \
|
||||
CC=$(brew --prefix llvm@21)/bin/clang \
|
||||
CXX=$(brew --prefix llvm@21)/bin/clang++ \
|
||||
OBJC=$(brew --prefix llvm@21)/bin/clang \
|
||||
OBJCXX=$(brew --prefix llvm@21)/bin/clang++ \
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
||||
-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_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
@@ -499,7 +470,6 @@ jobs:
|
||||
run: |
|
||||
set -x
|
||||
cd build/install
|
||||
mv imhex.app ImHex.app
|
||||
codesign --remove-signature ImHex.app
|
||||
codesign --force --deep --sign - ImHex.app
|
||||
|
||||
@@ -528,7 +498,7 @@ jobs:
|
||||
break;
|
||||
fi
|
||||
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
|
||||
uses: actions/attest-build-provenance@v2
|
||||
@@ -541,12 +511,12 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: macOS DMG ${{ matrix.name_suffix }} x86_64
|
||||
name: macOS DMG x86_64
|
||||
path: ./*.dmg
|
||||
|
||||
macos-arm64:
|
||||
runs-on: ubuntu-24.04
|
||||
name: 🍎 macOS 15 arm64
|
||||
name: 🍎 macOS 11 arm64
|
||||
|
||||
outputs:
|
||||
IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }}
|
||||
@@ -594,7 +564,7 @@ jobs:
|
||||
|
||||
macos-arm64-package:
|
||||
runs-on: macos-15-intel
|
||||
name: 🍎 macOS 15 arm64 Packaging
|
||||
name: 🍎 macOS 11 arm64 Packaging
|
||||
needs: macos-arm64
|
||||
|
||||
env:
|
||||
@@ -620,7 +590,6 @@ jobs:
|
||||
run: |
|
||||
set -x
|
||||
cd out
|
||||
mv imhex.app ImHex.app
|
||||
codesign --remove-signature ImHex.app
|
||||
codesign --force --deep --entitlements Entitlements.plist --sign - ImHex.app
|
||||
|
||||
@@ -671,14 +640,21 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- release_num: "24.04"
|
||||
- release_num: "25.04"
|
||||
- name: "Ubuntu"
|
||||
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
|
||||
|
||||
container:
|
||||
image: "ubuntu:${{ matrix.release_num }}"
|
||||
image: "${{ matrix.image }}"
|
||||
options: --privileged
|
||||
|
||||
permissions:
|
||||
@@ -697,8 +673,8 @@ jobs:
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: Ubuntu-${{ matrix.release_num }}-ccache-${{ github.run_id }}
|
||||
restore-keys: Ubuntu-${{ matrix.release_num }}-ccache
|
||||
key: ${{ matrix.image }}-ccache-${{ github.run_id }}
|
||||
restore-keys: ${{ matrix.image }}-ccache
|
||||
max-size: 1G
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
@@ -743,7 +719,7 @@ jobs:
|
||||
run: |
|
||||
cp -r build/DEBIAN 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
|
||||
uses: actions/attest-build-provenance@v2
|
||||
@@ -756,7 +732,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Ubuntu ${{ matrix.release_num }} DEB x86_64
|
||||
name: ${{ matrix.name }} ${{ matrix.release_num }} DEB x86_64
|
||||
path: '*.deb'
|
||||
|
||||
# AppImage build
|
||||
@@ -831,7 +807,7 @@ jobs:
|
||||
|
||||
# ArchLinux build
|
||||
archlinux-build:
|
||||
name: 🐧 ArchLinux
|
||||
name: 🐧 ArchLinux x86_64
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
container:
|
||||
@@ -946,23 +922,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Fedora
|
||||
release_num: rawhide
|
||||
mock_config: fedora-rawhide
|
||||
- name: Fedora
|
||||
release_num: 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
|
||||
|
||||
container:
|
||||
@@ -1225,10 +1189,6 @@ jobs:
|
||||
webassembly-build:
|
||||
runs-on: ubuntu-24.04
|
||||
name: 🌍 Web
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
actions: write
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -1248,17 +1208,20 @@ jobs:
|
||||
cache-source: cache
|
||||
cache-target: /cache
|
||||
|
||||
- name: 🛠️ Build using docker
|
||||
- name: 🔨 Copy necessary files
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: ⬇️ Download Release
|
||||
- name: ⬇️ Download Release artifact
|
||||
if: ${{ github.event.repository.fork == false }}
|
||||
uses: robinraju/release-downloader@v1
|
||||
with:
|
||||
latest: true
|
||||
fileName: 'imhex-*-Web.zip'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh --repo $GITHUB_REPOSITORY release download --pattern "imhex-*-Web.zip"
|
||||
|
||||
- name: 🔨 Fix permissions
|
||||
if: ${{ github.event.repository.fork == false }}
|
||||
@@ -1271,10 +1234,6 @@ jobs:
|
||||
with:
|
||||
path: out/
|
||||
|
||||
- name: 🔨 Copy necessary files
|
||||
run: |
|
||||
cp dist/web/serve.py out/nightly/start_imhex_web.py
|
||||
|
||||
- name: ⬆️ Upload package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -1293,7 +1252,7 @@ jobs:
|
||||
|
||||
webassembly-deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
name: ImHex Web
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
permissions:
|
||||
pages: write
|
||||
@@ -1306,11 +1265,64 @@ jobs:
|
||||
needs: webassembly-build
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🌍 Deploy WebAssembly Build to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
- 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:
|
||||
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: |
|
||||
project_version=`cat ImHex/VERSION`
|
||||
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
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
workflow: build.yml
|
||||
branch: ${{ github.event.release.target_commitish }}
|
||||
branch: master
|
||||
workflow_conclusion: success
|
||||
skip_unpack: true
|
||||
|
||||
@@ -125,4 +126,19 @@ jobs:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||
with:
|
||||
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:
|
||||
types: [published]
|
||||
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:
|
||||
release-update-repos:
|
||||
@@ -25,7 +31,7 @@ jobs:
|
||||
project_version=`cat ImHex/VERSION`
|
||||
tag_version="${{github.event.release.tag_name}}"
|
||||
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"
|
||||
exit 1
|
||||
fi
|
||||
@@ -41,6 +47,7 @@ jobs:
|
||||
tag: ImHex-v${{ env.IMHEX_VERSION }}
|
||||
repo: PatternLanguage
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
skipIfReleaseExists: true
|
||||
|
||||
- name: 🎫 Create ImHex-Patterns release
|
||||
uses: ncipollo/release-action@v1
|
||||
@@ -51,6 +58,7 @@ jobs:
|
||||
tag: ImHex-v${{ env.IMHEX_VERSION }}
|
||||
repo: ImHex-Patterns
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
skipIfReleaseExists: true
|
||||
|
||||
- name: 🎫 Create imhex-download-sdk release
|
||||
uses: ncipollo/release-action@v1
|
||||
@@ -61,11 +69,13 @@ jobs:
|
||||
tag: v${{ env.IMHEX_VERSION }}
|
||||
repo: imhex-download-sdk
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
skipIfReleaseExists: true
|
||||
|
||||
release-upload-artifacts:
|
||||
runs-on: ubuntu-24.04
|
||||
name: Release Upload Artifacts
|
||||
|
||||
outputs:
|
||||
IMHEX_VERSION: ${{ steps.verify_version.outputs.IMHEX_VERSION }}
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -74,17 +84,19 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Verify version and set version variable
|
||||
id: verify_version
|
||||
run: |
|
||||
set -x
|
||||
project_version=`cat ImHex/VERSION`
|
||||
tag_version="${{github.event.release.tag_name}}"
|
||||
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"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV
|
||||
echo "IMHEX_VERSION=$project_version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 🗜️ Create tarball from sources with dependencies
|
||||
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
|
||||
@@ -97,6 +109,7 @@ jobs:
|
||||
branch: ${{ github.event.release.target_commitish }}
|
||||
workflow_conclusion: success
|
||||
skip_unpack: true
|
||||
commit: ${{ github.event.inputs.commit_hash }}
|
||||
|
||||
- name: 🗜️ Unzip files when needed
|
||||
run: |
|
||||
@@ -115,25 +128,87 @@ jobs:
|
||||
|
||||
- name: 🟩 Rename artifacts when needed
|
||||
run: |
|
||||
mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip
|
||||
mv "Windows Portable arm64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip
|
||||
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
|
||||
mv "ImHex Web.zip" imhex-${{ env.IMHEX_VERSION }}-Web.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 || true
|
||||
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 || 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
|
||||
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
|
||||
with:
|
||||
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
|
||||
run: |
|
||||
set -x
|
||||
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
|
||||
|
||||
- name: ⬆️ Publish AUR package
|
||||
@@ -147,9 +222,9 @@ jobs:
|
||||
pkgname: imhex-bin
|
||||
pkgbuild: ./PKGBUILD
|
||||
commit_username: iTrooz
|
||||
commit_email: itrooz@protonmail.com
|
||||
commit_email: hey@itrooz.fr
|
||||
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
|
||||
|
||||
release-update-winget:
|
||||
@@ -161,6 +236,7 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
|
||||
- name: ⬆️ Update winget manifest
|
||||
shell: pwsh
|
||||
env:
|
||||
@@ -193,7 +269,7 @@ jobs:
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||
with:
|
||||
snap: imhex-${{ env.IMHEX_VERSION }}-x86_64.snap
|
||||
snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-x86_64.snap
|
||||
release: stable
|
||||
|
||||
- name: ⬆️ Publish arm64 Snap package
|
||||
@@ -202,5 +278,5 @@ jobs:
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||
with:
|
||||
snap: imhex-${{ env.IMHEX_VERSION }}-arm64.snap
|
||||
snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-arm64.snap
|
||||
release: stable
|
||||
@@ -1,33 +1,39 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
# 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_OFFLINE_BUILD "Enable offline build" 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_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_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
|
||||
option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON )
|
||||
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" 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_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
|
||||
option(IMHEX_GENERATE_PACKAGE "Specify if a native package should be built. (Windows and MacOS only)" OFF)
|
||||
option(IMHEX_GENERATE_PACKAGE "Specify if a cpack package should be built. (Windows 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_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_COMPRESS_DEBUG_INFO "Compress debug information" ON )
|
||||
option(IMHEX_ENABLE_CXX_MODULES "Enable C++20 Module compilation. Testing only!" OFF)
|
||||
option(IMHEX_ENABLE_CPPCHECK "Enable cppcheck static analysis" OFF)
|
||||
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(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
|
||||
## 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">
|
||||
<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>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -175,15 +175,11 @@ macro(detectOS)
|
||||
endif()
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(IMHEX_PLUGINS_IN_SHARE)
|
||||
set(PLUGINS_INSTALL_LOCATION "share/imhex/plugins")
|
||||
else()
|
||||
set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins")
|
||||
set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins")
|
||||
|
||||
# Add System plugin location for plugins to be loaded from
|
||||
# IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release
|
||||
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex")
|
||||
endif()
|
||||
# Add System plugin location for plugins to be loaded from
|
||||
# IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release
|
||||
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex")
|
||||
|
||||
else ()
|
||||
message(FATAL_ERROR "Unknown / unsupported system!")
|
||||
@@ -205,11 +201,18 @@ macro(configurePackingResources)
|
||||
set(CPACK_GENERATOR "WIX")
|
||||
set(CPACK_PACKAGE_NAME "ImHex")
|
||||
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_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_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_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_property(INSTALL "$<TARGET_FILE_NAME:main>"
|
||||
PROPERTY CPACK_START_MENU_SHORTCUTS "ImHex"
|
||||
@@ -218,9 +221,9 @@ macro(configurePackingResources)
|
||||
endif()
|
||||
elseif (APPLE OR ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns")
|
||||
set(BUNDLE_NAME "imhex.app")
|
||||
set(BUNDLE_NAME "ImHex.app")
|
||||
|
||||
if (IMHEX_GENERATE_PACKAGE)
|
||||
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||
set(APPLICATION_TYPE MACOSX_BUNDLE)
|
||||
set_source_files_properties(${IMHEX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
set(MACOSX_BUNDLE_ICON_FILE "AppIcon.icns")
|
||||
@@ -236,9 +239,9 @@ macro(configurePackingResources)
|
||||
string(TIMESTAMP CURR_YEAR "%Y")
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 - ${CURR_YEAR} WerWolv. All rights reserved." )
|
||||
if ("${CMAKE_GENERATOR}" STREQUAL "Xcode")
|
||||
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${BUNDLE_NAME}")
|
||||
set(IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${BUNDLE_NAME}")
|
||||
else ()
|
||||
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}")
|
||||
set(IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}")
|
||||
endif()
|
||||
|
||||
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")
|
||||
|
||||
if (APPLE)
|
||||
if (IMHEX_GENERATE_PACKAGE)
|
||||
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION})
|
||||
endif ()
|
||||
else ()
|
||||
@@ -312,7 +315,7 @@ macro(createPackage)
|
||||
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}\"!")
|
||||
endif()
|
||||
|
||||
@@ -348,24 +351,21 @@ macro(createPackage)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
if (IMHEX_GENERATE_PACKAGE)
|
||||
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||
set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS} "${IMHEX_SYSTEM_LIBRARY_PATH}")
|
||||
include(PostprocessBundle)
|
||||
|
||||
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_BUNDLE_NAME "${MACOSX_BUNDLE_BUNDLE_NAME}")
|
||||
|
||||
# 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: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")
|
||||
|
||||
install(FILES ${IMHEX_ICON} DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources")
|
||||
install(TARGETS main BUNDLE DESTINATION ".")
|
||||
install(TARGETS updater DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
|
||||
install(
|
||||
@@ -431,7 +431,7 @@ macro(createPackage)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (IMHEX_GENERATE_PACKAGE)
|
||||
if (IMHEX_MACOS_CREATE_BUNDLE)
|
||||
set(CPACK_BUNDLE_NAME "ImHex")
|
||||
|
||||
include(CPack)
|
||||
@@ -560,6 +560,9 @@ function(detectBadClone)
|
||||
|
||||
file (GLOB EXTERNAL_DIRS "lib/external/*" "lib/third_party/*")
|
||||
foreach (EXTERNAL_DIR ${EXTERNAL_DIRS})
|
||||
if(NOT IS_DIRECTORY "${EXTERNAL_DIR}")
|
||||
continue()
|
||||
endif()
|
||||
file(GLOB_RECURSE RESULT "${EXTERNAL_DIR}/*")
|
||||
list(LENGTH RESULT ENTRY_COUNT)
|
||||
if(ENTRY_COUNT LESS_EQUAL 1)
|
||||
@@ -587,7 +590,9 @@ endfunction()
|
||||
macro(detectBundledPlugins)
|
||||
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})
|
||||
if (EXISTS "${PLUGIN_DIR}/CMakeLists.txt")
|
||||
get_filename_component(PLUGIN_NAME ${PLUGIN_DIR} NAME)
|
||||
@@ -596,8 +601,6 @@ macro(detectBundledPlugins)
|
||||
endif ()
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
set(PLUGINS ${IMHEX_INCLUDE_PLUGINS})
|
||||
endif()
|
||||
|
||||
foreach(PLUGIN_NAME ${PLUGINS})
|
||||
@@ -608,9 +611,13 @@ macro(detectBundledPlugins)
|
||||
message(FATAL_ERROR "No bundled plugins enabled")
|
||||
endif()
|
||||
|
||||
if (NOT ("builtin" IN_LIST PLUGINS))
|
||||
message(FATAL_ERROR "The 'builtin' plugin is required for ImHex to work!")
|
||||
endif ()
|
||||
set(REQUIRED_PLUGINS builtin fonts ui)
|
||||
foreach(PLUGIN ${REQUIRED_PLUGINS})
|
||||
list(FIND PLUGINS ${PLUGIN} PLUGIN_INDEX)
|
||||
if (PLUGIN_INDEX EQUAL -1)
|
||||
message(FATAL_ERROR "Required plugin '${PLUGIN}' is not enabled!")
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
macro(setVariableInParent variable value)
|
||||
|
||||
@@ -2,11 +2,7 @@ find_path(LIBMAGIC_INCLUDE_DIR magic.h)
|
||||
|
||||
find_library(LIBMAGIC_LIBRARY NAMES magic)
|
||||
|
||||
if (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
||||
set(LIBMAGIC_FOUND TRUE)
|
||||
endif (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
||||
|
||||
find_package_handle_standard_args("libmagic" DEFAULT_MSG
|
||||
find_package_handle_standard_args(Magic DEFAULT_MSG
|
||||
LIBMAGIC_LIBRARY
|
||||
LIBMAGIC_INCLUDE_DIR
|
||||
)
|
||||
@@ -14,5 +10,5 @@ find_package_handle_standard_args("libmagic" DEFAULT_MSG
|
||||
mark_as_advanced(
|
||||
LIBMAGIC_INCLUDE_DIR
|
||||
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}}"
|
||||
allow_unauthenticated: true
|
||||
sources:
|
||||
- sourceline: 'deb [arch=amd64] http://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=amd64] https://us.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse'
|
||||
- sourceline: 'deb [arch=arm64] https://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe multiverse'
|
||||
include:
|
||||
- libgdk-pixbuf2.0-0
|
||||
- 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 GIT_COMMIT_HASH
|
||||
ARG GIT_BRANCH
|
||||
ARG ARCHITECTURE_PACKAGE
|
||||
ARG ARCHITECTURE_FILE_NAME
|
||||
ARG ARCHITECTURE_APPIMAGE_BUILDER
|
||||
ARG ARCHITECTURE_PACKAGE=x86_64
|
||||
ARG ARCHITECTURE_FILE_NAME=amd64
|
||||
ARG ARCHITECTURE_APPIMAGE_BUILDER=x86_64
|
||||
WORKDIR /build
|
||||
|
||||
# Ubuntu sh doesnt support string substitution
|
||||
@@ -42,16 +42,18 @@ RUN <<EOF
|
||||
# Prepare ImHex build
|
||||
set -xe
|
||||
|
||||
CC=gcc-14 CXX=g++-14 cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
|
||||
-DIMHEX_ENABLE_LTO=${LTO} \
|
||||
-DIMHEX_PLUGINS_IN_SHARE=ON \
|
||||
CC=gcc-14 CXX=g++-14 cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
|
||||
-DIMHEX_ENABLE_LTO=${LTO} \
|
||||
-DIMHEX_BUNDLE_PLUGIN_SDK=OFF \
|
||||
`# To prevent using a libdir with an architecture-specific name` \
|
||||
-DCMAKE_INSTALL_LIBDIR="lib" \
|
||||
/imhex
|
||||
EOF
|
||||
|
||||
|
||||
2
dist/DEBIAN/control.in
vendored
2
dist/DEBIAN/control.in
vendored
@@ -4,7 +4,7 @@ Section: editors
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
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>
|
||||
Description: ImHex Hex Editor
|
||||
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>
|
||||
</configuration>
|
||||
<configuration default="false" name="CMake Debug" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory">
|
||||
<method v="2" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</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.
|
||||
|
||||
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:
|
||||
```sh
|
||||
cd ImHex
|
||||
@@ -20,3 +20,5 @@ cmake -G "Ninja" \
|
||||
..
|
||||
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_YARA=ON
|
||||
- -DIMHEX_OFFLINE_BUILD=ON
|
||||
- -DIMHEX_BUNDLE_PLUGIN_SDK=OFF
|
||||
- -DCMAKE_INSTALL_LIBDIR=lib
|
||||
- -DCMAKE_INSTALL_RPATH='$ORIGIN/../lib:$ORIGIN/../lib64'
|
||||
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: iTrooz <itrooz@protonmail.com>
|
||||
From: iTrooz <hey@itrooz.fr>
|
||||
Date: Mon, 29 Aug 2022 17:29:38 +0200
|
||||
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
|
||||
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
|
||||
|
||||
@@ -132,6 +132,7 @@ if [ "$CUSTOM_GLFW" ]; then
|
||||
mkdir build
|
||||
cd build
|
||||
CC=o64-clang CXX=o64-clang++ cmake -G "Ninja" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
@@ -150,9 +151,7 @@ EOF
|
||||
# Build ImHex
|
||||
## Copy ImHex
|
||||
COPY --from=imhex / /mnt/ImHex
|
||||
## Patch ImHex with hacks
|
||||
# COPY toolchain.cmake.2 /osxcross/target/toolchain.cmake
|
||||
# Configure ImHex build
|
||||
## Configure ImHex build
|
||||
RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps \
|
||||
cd /mnt/ImHex && \
|
||||
# compilers
|
||||
@@ -170,6 +169,7 @@ RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/
|
||||
-DIMHEX_STRICT_WARNINGS=OFF \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=/mnt/ImHex/build/install \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||
-B build
|
||||
## Build ImHex
|
||||
RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps <<EOF
|
||||
@@ -184,4 +184,4 @@ EOF
|
||||
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /mnt/ImHex/build/install/imhex.app imhex.app
|
||||
COPY --from=build /mnt/ImHex/build/install/ImHex.app ImHex.app
|
||||
|
||||
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_CXX_COMPILER_LAUNCHER=${CCACHE}
|
||||
- -DIMHEX_PATTERNS_PULL_MASTER=ON
|
||||
- -DIMHEX_BUNDLE_PLUGIN_SDK=OFF
|
||||
cmake-generator: Ninja
|
||||
build-packages:
|
||||
- cmake
|
||||
|
||||
2
dist/vcpkg.json
vendored
2
dist/vcpkg.json
vendored
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vcpkg",
|
||||
"version": "1.0.0",
|
||||
"builtin-baseline": "7e21420f775f72ae938bdeb5e6068f722088f06a",
|
||||
"builtin-baseline": "66c0373dc7fca549e5803087b9487edfe3aca0a1",
|
||||
"dependencies": [
|
||||
"libmagic",
|
||||
"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
|
||||
# See https://github.com/moby/moby/issues/41715#issuecomment-733976493
|
||||
ARG UNIQUEKEY 1
|
||||
ARG UNIQUEKEY=1
|
||||
|
||||
RUN apt update
|
||||
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
|
||||
set -xe
|
||||
|
||||
git clone https://github.com/microsoft/vcpkg /vcpkg
|
||||
git -C /vcpkg pull
|
||||
git clone --depth 1 https://github.com/microsoft/vcpkg /vcpkg
|
||||
/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
|
||||
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
|
||||
set -xe
|
||||
|
||||
@@ -50,6 +50,7 @@ ENV CCACHE_DIR=/cache/ccache
|
||||
|
||||
RUN mkdir /build
|
||||
WORKDIR /build
|
||||
ARG BUILD_TYPE=Release
|
||||
RUN --mount=type=cache,target=/cache \
|
||||
--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 \
|
||||
-DLIBROMFS_COMPRESS_RESOURCES=OFF \
|
||||
-DIMHEX_ENABLE_PLUGIN_TESTS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_BUILD_TYPE=${BUILD_TYPE}
|
||||
|
||||
ninja -j $JOBS
|
||||
|
||||
@@ -106,3 +107,4 @@ COPY --from=build [ \
|
||||
|
||||
FROM nginx
|
||||
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
|
||||
version: '3'
|
||||
services:
|
||||
imhex_web:
|
||||
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__':
|
||||
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]}")
|
||||
httpd.serve_forever()
|
||||
|
||||
16
dist/web/source/index.html
vendored
16
dist/web/source/index.html
vendored
@@ -15,17 +15,17 @@
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<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:image" content="https://imhex.werwolv.net/assets/splash_wasm.png">
|
||||
<meta property="og:image" content="splash_wasm.png">
|
||||
|
||||
<!-- Twitter -->
|
||||
<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:description"
|
||||
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">
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
"email": "hey@werwolv.net",
|
||||
"founder": "WerWolv",
|
||||
"slogan": "A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.",
|
||||
"url": "https://imhex.werwolv.net",
|
||||
"logo": "https://imhex.werwolv.net/assets/logos/logo.svg"
|
||||
"url": "https://web.imhex.werwolv.net",
|
||||
"logo": "https://web.imhex.werwolv.net/icon.svg"
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -96,7 +96,9 @@
|
||||
</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 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;
|
||||
top: 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);
|
||||
|
||||
let root = document.querySelector(':root');
|
||||
root.style.setProperty("--progress", `${percent}%`)
|
||||
document.getElementById("progress-bar-content").innerHTML = `${percent}% [${mibNow} MiB / ${mibTotal} MiB]`;
|
||||
if (root != null) {
|
||||
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) {
|
||||
@@ -100,81 +106,17 @@ var notWorkingTimer = setTimeout(() => {
|
||||
}, 5000);
|
||||
|
||||
var Module = {
|
||||
preRun: [],
|
||||
preRun: () => {
|
||||
ENV.IMHEX_SKIP_SPLASH_SCREEN = "1";
|
||||
},
|
||||
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() {
|
||||
// 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"
|
||||
|
||||
clearTimeout(notWorkingTimer);
|
||||
@@ -257,16 +199,6 @@ if (urlParams.has("lang")) {
|
||||
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
|
||||
document.addEventListener('keydown', e => {
|
||||
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/banner.cpp
|
||||
|
||||
source/mcp/client.cpp
|
||||
source/mcp/server.cpp
|
||||
|
||||
source/subcommands/subcommands.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -145,62 +145,17 @@ EXPORT_MODULE namespace hex {
|
||||
* @brief Returns the icon of the achievement
|
||||
* @return Icon of the achievement
|
||||
*/
|
||||
[[nodiscard]] const ImGuiExt::Texture &getIcon() const {
|
||||
if (m_iconData.empty())
|
||||
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;
|
||||
[[nodiscard]] const char* getIcon() const {
|
||||
return m_icon.c_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the icon of the achievement
|
||||
* @param data Icon data
|
||||
* @param icon Icon glyph
|
||||
* @return Reference to the achievement
|
||||
*/
|
||||
Achievement& setIcon(std::span<const std::byte> data) {
|
||||
m_iconData.reserve(data.size());
|
||||
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));
|
||||
Achievement& setIcon(std::string icon) {
|
||||
m_icon = std::move(icon);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -284,8 +239,7 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
std::function<void(Achievement &)> m_clickCallback;
|
||||
|
||||
std::vector<u8> m_iconData;
|
||||
mutable ImGuiExt::Texture m_icon;
|
||||
std::string m_icon;
|
||||
|
||||
u32 m_progress = 0;
|
||||
u32 m_maxProgress = 1;
|
||||
|
||||
@@ -16,7 +16,7 @@ EXPORT_MODULE namespace hex {
|
||||
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 <string>
|
||||
|
||||
#include <hex/mcp/server.hpp>
|
||||
|
||||
EXPORT_MODULE namespace hex {
|
||||
|
||||
/* 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 <string>
|
||||
#include <vector>
|
||||
#include <bit>
|
||||
|
||||
EXPORT_MODULE namespace hex {
|
||||
|
||||
@@ -22,8 +23,10 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct DoNotUseThisByItselfTag {};
|
||||
|
||||
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)>;
|
||||
|
||||
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
|
||||
* @param unlocalizedName The unlocalized name of the entry
|
||||
|
||||
@@ -19,7 +19,7 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
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);
|
||||
|
||||
struct Entry {
|
||||
|
||||
@@ -52,7 +52,7 @@ EXPORT_MODULE namespace hex {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Interface& setTooltip(const std::string &tooltip) {
|
||||
Interface& setTooltip(const UnlocalizedString &tooltip) {
|
||||
m_tooltip = tooltip;
|
||||
|
||||
return *this;
|
||||
@@ -239,6 +239,14 @@ EXPORT_MODULE namespace hex {
|
||||
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 {
|
||||
@@ -290,8 +298,8 @@ EXPORT_MODULE namespace hex {
|
||||
public:
|
||||
SettingsValue(nlohmann::json value) : m_value(std::move(value)) {}
|
||||
|
||||
template<typename T>
|
||||
T get(std::common_type_t<T> defaultValue) const {
|
||||
template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))
|
||||
[[nodiscard]] T get(T defaultValue) const {
|
||||
try {
|
||||
auto result = m_value;
|
||||
if (result.is_number() && std::same_as<T, bool>)
|
||||
@@ -308,8 +316,8 @@ EXPORT_MODULE namespace hex {
|
||||
nlohmann::json m_value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &defaultValue) {
|
||||
template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))
|
||||
[[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T defaultValue) {
|
||||
auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);
|
||||
|
||||
try {
|
||||
@@ -326,8 +334,8 @@ EXPORT_MODULE namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &value) {
|
||||
template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))
|
||||
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T value) {
|
||||
impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;
|
||||
impl::runOnChangeHandlers(unlocalizedCategory, unlocalizedName, value);
|
||||
|
||||
@@ -336,10 +344,75 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
using OnChangeCallback = std::function<void(const SettingsValue &)>;
|
||||
u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback);
|
||||
void removeOnChangeHandler(u64 id);
|
||||
|
||||
using OnSaveCallback = std::function<void()>;
|
||||
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 SubMenuValue = "$SUBMENU$";
|
||||
constexpr static auto TaskBarMenuValue = "$TASKBAR$";
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -220,10 +234,10 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace hex {
|
||||
* This event is responsible for (optionally) initializing the provider and calling EventProviderOpened
|
||||
* (although the event can also be called manually without problem)
|
||||
*/
|
||||
EVENT_DEF(EventProviderCreated, prv::Provider *);
|
||||
EVENT_DEF(EventProviderCreated, std::shared_ptr<prv::Provider>);
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -86,7 +86,7 @@ EXPORT_MODULE namespace hex {
|
||||
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
||||
* @param select Whether to select the provider after adding it
|
||||
*/
|
||||
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
|
||||
@@ -111,12 +111,18 @@ EXPORT_MODULE namespace hex {
|
||||
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
||||
* @param select Whether to select the provider after adding it
|
||||
*/
|
||||
prv::Provider* createProvider(
|
||||
std::shared_ptr<prv::Provider> createProvider(
|
||||
const UnlocalizedString &unlocalizedName,
|
||||
bool skipLoadInterface = false,
|
||||
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 setMainDockSpaceId(ImGuiID id);
|
||||
void setMainWindowHandle(GLFWwindow *window);
|
||||
void setMainWindowFocusState(bool focused);
|
||||
|
||||
void setGlobalScale(float scale);
|
||||
void setNativeScale(float scale);
|
||||
@@ -161,6 +162,12 @@ EXPORT_MODULE namespace hex {
|
||||
*/
|
||||
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
|
||||
* @return Whether borderless window mode is enabled
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <condition_variable>
|
||||
#include <source_location>
|
||||
#include <thread>
|
||||
#include <hex/trace/exceptions.hpp>
|
||||
|
||||
EXPORT_MODULE namespace hex {
|
||||
|
||||
@@ -22,7 +23,7 @@ EXPORT_MODULE namespace hex {
|
||||
class Task {
|
||||
public:
|
||||
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(Task &&other) noexcept;
|
||||
@@ -94,7 +95,16 @@ EXPORT_MODULE namespace hex {
|
||||
std::atomic_flag m_hadException;
|
||||
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 TaskManager;
|
||||
@@ -242,6 +252,8 @@ EXPORT_MODULE namespace hex {
|
||||
static const std::list<std::shared_ptr<Task>>& getRunningTasks();
|
||||
static void runDeferredCalls();
|
||||
|
||||
static void addTaskCompletionCallback(const std::function<void(Task&)>& function);
|
||||
|
||||
private:
|
||||
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>
|
||||
|
||||
struct ImRect;
|
||||
|
||||
EXPORT_MODULE namespace hex {
|
||||
|
||||
class TutorialManager {
|
||||
@@ -22,6 +24,8 @@ EXPORT_MODULE namespace hex {
|
||||
Right = 8
|
||||
};
|
||||
|
||||
using DrawFunction = std::function<void()>;
|
||||
|
||||
struct Tutorial {
|
||||
Tutorial() = delete;
|
||||
Tutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) :
|
||||
@@ -101,6 +105,7 @@ EXPORT_MODULE namespace hex {
|
||||
std::vector<Highlight> m_highlights;
|
||||
std::optional<Message> m_message;
|
||||
std::function<void()> m_onAppear, m_onComplete;
|
||||
DrawFunction m_drawFunction;
|
||||
};
|
||||
|
||||
Step& addStep();
|
||||
@@ -146,6 +151,7 @@ EXPORT_MODULE namespace hex {
|
||||
* @param unlocalizedName Name of tutorial to start
|
||||
*/
|
||||
static void startTutorial(const UnlocalizedString &unlocalizedName);
|
||||
static void stopCurrentTutorial();
|
||||
|
||||
static void startHelpHover();
|
||||
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 setRenderer(std::function<DrawFunction(const std::string &)> renderer);
|
||||
|
||||
static void postElementRendered(ImGuiID id, const ImRect &boundingBox);
|
||||
|
||||
private:
|
||||
TutorialManager() = delete;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
@@ -49,9 +50,15 @@ namespace hex::dp {
|
||||
virtual void store(nlohmann::json &j) const { std::ignore = j; }
|
||||
virtual void load(const nlohmann::json &j) { std::ignore = j; }
|
||||
|
||||
struct NodeError {
|
||||
struct NodeError: public std::exception {
|
||||
Node *node;
|
||||
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() {
|
||||
@@ -102,7 +109,7 @@ namespace hex::dp {
|
||||
void unmarkInputProcessed(u32 index);
|
||||
|
||||
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 setAttributes(std::vector<Attribute> attributes);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/imhex_api/system.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -9,6 +10,9 @@ namespace hex {
|
||||
class AutoResetBase {
|
||||
public:
|
||||
virtual ~AutoResetBase() = default;
|
||||
|
||||
private:
|
||||
friend void ImHexApi::System::impl::cleanup();
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
|
||||
@@ -19,16 +23,20 @@ namespace hex {
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
AutoReset() {
|
||||
ImHexApi::System::impl::addAutoResetObject(this);
|
||||
AutoReset() noexcept {
|
||||
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_valid = true;
|
||||
}
|
||||
|
||||
AutoReset(T &&value) noexcept : AutoReset() {
|
||||
explicit(false) AutoReset(T &&value) noexcept : AutoReset() {
|
||||
m_value = std::move(value);
|
||||
m_valid = true;
|
||||
}
|
||||
@@ -61,29 +69,27 @@ namespace hex {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
T& operator=(const T &value) {
|
||||
AutoReset& operator=(const T &value) {
|
||||
m_value = value;
|
||||
m_valid = true;
|
||||
return m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator=(T &&value) noexcept {
|
||||
AutoReset& operator=(T &&value) noexcept {
|
||||
m_value = std::move(value);
|
||||
m_valid = true;
|
||||
return m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
[[nodiscard]] bool isValid() const {
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
private:
|
||||
friend void ImHexApi::System::impl::cleanup();
|
||||
|
||||
void reset() override {
|
||||
if constexpr (requires { m_value.reset(); }) {
|
||||
if constexpr (requires(T t) { t.reset(); }) {
|
||||
m_value.reset();
|
||||
} else if constexpr (requires { m_value.clear(); }) {
|
||||
} else if constexpr (requires(T t) { t.clear(); }) {
|
||||
m_value.clear();
|
||||
} else if constexpr (std::is_pointer_v<T>) {
|
||||
m_value = nullptr; // cppcheck-suppress nullPointer
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
static_assert(false, "Debug variables are only intended for use during development.");
|
||||
#endif
|
||||
|
||||
namespace hex::trace {
|
||||
struct StackTraceResult;
|
||||
}
|
||||
|
||||
namespace hex::dbg {
|
||||
|
||||
namespace impl {
|
||||
@@ -47,4 +51,6 @@ namespace hex::dbg {
|
||||
bool debugModeEnabled();
|
||||
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);
|
||||
void endMenu();
|
||||
|
||||
bool beginTaskBarMenu();
|
||||
void endTaskBarMenu();
|
||||
|
||||
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);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <numbers>
|
||||
#include <array>
|
||||
|
||||
#include <opengl_support.h>
|
||||
#include "imgui.h"
|
||||
@@ -935,7 +936,7 @@ namespace hex::gl {
|
||||
void attachTexture(const Texture &texture) const;
|
||||
|
||||
private:
|
||||
GLuint m_frameBuffer, m_renderBuffer;
|
||||
GLuint m_frameBuffer = 0, m_renderBuffer = 0;
|
||||
};
|
||||
|
||||
class AxesVectors {
|
||||
|
||||
@@ -98,6 +98,8 @@ namespace hex {
|
||||
|
||||
void startProgram(const std::vector<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);
|
||||
|
||||
extern "C" void registerFont(const char *fontName, const char *fontPath);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
void macosInstallEventListener();
|
||||
|
||||
void toastMessageMacos(const char *title, const char *message);
|
||||
void macosSetupDockMenu(void);
|
||||
}
|
||||
|
||||
#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() override;
|
||||
|
||||
bool open() override;
|
||||
OpenResult open() override;
|
||||
void close() 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 isSavableAsRecent() const override { return false; }
|
||||
|
||||
[[nodiscard]] bool open() override;
|
||||
[[nodiscard]] OpenResult open() override;
|
||||
void close() 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;
|
||||
};
|
||||
|
||||
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
|
||||
*/
|
||||
@@ -79,6 +94,65 @@ namespace hex::prv {
|
||||
public:
|
||||
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();
|
||||
virtual ~Provider();
|
||||
Provider(const Provider&) = delete;
|
||||
@@ -94,7 +168,7 @@ namespace hex::prv {
|
||||
* @note This is not related to the EventProviderOpened event
|
||||
* @return true if the provider was opened successfully, else false
|
||||
*/
|
||||
[[nodiscard]] virtual bool open() = 0;
|
||||
[[nodiscard]] virtual OpenResult open() = 0;
|
||||
|
||||
/**
|
||||
* @brief Closes this provider
|
||||
@@ -262,9 +336,6 @@ namespace hex::prv {
|
||||
void skipLoadInterface() { m_skipLoadInterface = true; }
|
||||
[[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>
|
||||
bool addUndoableOperation(auto && ... args) {
|
||||
return m_undoRedoStack.add<T>(std::forward<decltype(args)...>(args)...);
|
||||
@@ -296,8 +367,6 @@ namespace hex::prv {
|
||||
*/
|
||||
bool m_skipLoadInterface = false;
|
||||
|
||||
std::string m_errorMessage = "Unspecified error";
|
||||
|
||||
u64 m_pageSize = MaxPageSize;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::prv {
|
||||
@@ -40,6 +41,8 @@ namespace hex::prv::undo {
|
||||
|
||||
bool add(std::unique_ptr<Operation> &&operation);
|
||||
|
||||
static std::recursive_mutex& getMutex();
|
||||
|
||||
const std::vector<std::unique_ptr<Operation>> &getAppliedOperations() const {
|
||||
return m_undoStack;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace hex::test {
|
||||
|
||||
[[nodiscard]] UnlocalizedString getTypeName() const override { return "hex.test.provider.test"; }
|
||||
|
||||
bool open() override { return true; }
|
||||
OpenResult open() override { return {}; }
|
||||
void close() override { }
|
||||
|
||||
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 DimmedIconToggle(const char *icon, 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);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <hex/api/tutorial_manager.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -27,7 +26,7 @@ namespace hex {
|
||||
* @brief Draws the view
|
||||
* @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
|
||||
@@ -126,6 +125,7 @@ namespace hex {
|
||||
class Window;
|
||||
class Special;
|
||||
class Floating;
|
||||
class Scrolling;
|
||||
class Modal;
|
||||
class FullScreen;
|
||||
|
||||
@@ -147,23 +147,24 @@ namespace hex {
|
||||
class View::Window : public View {
|
||||
public:
|
||||
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
|
||||
*/
|
||||
virtual void drawHelpText() = 0;
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
|
||||
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
|
||||
if (ImGui::Begin(title.c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
|
||||
TutorialManager::setLastItemInteractiveHelpPopup([this]{ this->drawHelpText(); });
|
||||
this->drawContent();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) override;
|
||||
|
||||
[[nodiscard]] virtual bool allowScroll() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void handleFocusRestoration();
|
||||
|
||||
private:
|
||||
ImGuiWindow *m_focusedSubWindow{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -174,12 +175,7 @@ namespace hex {
|
||||
public:
|
||||
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
|
||||
this->drawContent();
|
||||
}
|
||||
}
|
||||
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -189,10 +185,25 @@ namespace hex {
|
||||
public:
|
||||
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; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
@@ -200,24 +211,7 @@ namespace hex {
|
||||
public:
|
||||
explicit Modal(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
|
||||
|
||||
void draw() 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;
|
||||
}
|
||||
}
|
||||
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||
|
||||
[[nodiscard]] virtual bool hasCloseButton() const { return true; }
|
||||
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
|
||||
@@ -227,10 +221,7 @@ namespace hex {
|
||||
public:
|
||||
explicit FullScreen() : View("FullScreen", "") {}
|
||||
|
||||
void draw() final {
|
||||
this->drawContent();
|
||||
this->drawAlwaysVisibleContent();
|
||||
}
|
||||
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -264,8 +264,7 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
if (json.empty())
|
||||
return;
|
||||
if (json.empty()) return;
|
||||
|
||||
#if defined(OS_WEB)
|
||||
auto data = json.dump();
|
||||
|
||||
@@ -594,6 +594,12 @@ namespace hex {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
impl::s_displayedContent = impl::ContentDisplay { true, displayCallback };
|
||||
impl::s_displayedContent = impl::ContentDisplay { .showSearchBox=true, .callback=displayCallback };
|
||||
}
|
||||
|
||||
void openWithContent(const impl::ContentDisplayCallback &displayCallback) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
log::debug("Registered new data inspector format: {}", unlocalizedName.get());
|
||||
|
||||
@@ -991,7 +1009,7 @@ namespace hex {
|
||||
coloredIcon.color = ImGuiCustomCol_ToolbarGray;
|
||||
|
||||
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) {
|
||||
@@ -1015,14 +1033,23 @@ namespace hex {
|
||||
|
||||
unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue);
|
||||
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) {
|
||||
unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue);
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
return a.second.toolbarIndex < b.second.toolbarIndex;
|
||||
})->second.toolbarIndex;
|
||||
|
||||
for (auto &[priority, menuItem] : *impl::s_menuItems) {
|
||||
if (menuItem.unlocalizedNames.back() == unlocalizedName) {
|
||||
if (menuItem.unlocalizedNames == unlocalizedNames) {
|
||||
menuItem.toolbarIndex = maxIndex + 1;
|
||||
menuItem.icon.color = color;
|
||||
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)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;
|
||||
|
||||
auto newProvider = creationFunction();
|
||||
|
||||
if (provider != nullptr) {
|
||||
*provider = newProvider.get();
|
||||
*provider = newProvider;
|
||||
ImHexApi::Provider::add(std::move(newProvider), skipLoadInterface, selectProvider);
|
||||
}
|
||||
});
|
||||
@@ -1127,7 +1154,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms() {
|
||||
return *s_algorithms;
|
||||
}
|
||||
|
||||
void addAlgorithm(std::unique_ptr<Algorithm> &&hash) {
|
||||
s_algorithms->emplace_back(std::move(hash));
|
||||
}
|
||||
static AutoReset<std::vector<std::unique_ptr<Algorithm>>> s_algorithms;
|
||||
const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms() {
|
||||
return *s_algorithms;
|
||||
}
|
||||
|
||||
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;
|
||||
const std::vector<std::unique_ptr<Hash>>& getHashes() {
|
||||
return *s_hashes;
|
||||
}
|
||||
|
||||
void add(std::unique_ptr<Hash> &&hash) {
|
||||
s_hashes->emplace_back(std::move(hash));
|
||||
}
|
||||
static AutoReset<std::vector<std::unique_ptr<Hash>>> s_hashes;
|
||||
const std::vector<std::unique_ptr<Hash>>& getHashes() {
|
||||
return *s_hashes;
|
||||
}
|
||||
|
||||
void add(std::unique_ptr<Hash> &&hash) {
|
||||
s_hashes->emplace_back(std::move(hash));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace ContentRegistry::BackgroundServices {
|
||||
|
||||
namespace impl {
|
||||
|
||||
class Service {
|
||||
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(Service &&) = default;
|
||||
~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 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();
|
||||
(*s_architectures)[arch->getName()] = std::move(function);
|
||||
}
|
||||
|
||||
const std::map<std::string, impl::CreatorFunction>& getArchitectures() {
|
||||
return *s_architectures;
|
||||
}
|
||||
void addArchitectureCreator(impl::CreatorFunction function) {
|
||||
const auto arch = function();
|
||||
(*s_architectures)[arch->getName()] = std::move(function);
|
||||
}
|
||||
|
||||
const std::map<std::string, impl::CreatorFunction>& getArchitectures() {
|
||||
return *s_architectures;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <hex/api/event_manager.hpp>
|
||||
|
||||
namespace hex {
|
||||
@@ -35,7 +36,7 @@ namespace hex {
|
||||
|
||||
void EventManager::unsubscribe(void *token, impl::EventId id) {
|
||||
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;
|
||||
});
|
||||
|
||||
|
||||
@@ -47,6 +47,10 @@
|
||||
|
||||
#if defined(OS_WEB)
|
||||
#include <emscripten.h>
|
||||
#elif defined(OS_MACOS)
|
||||
extern "C" {
|
||||
void macosRegisterFont(const unsigned char *data, size_t size);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace hex {
|
||||
@@ -257,7 +261,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -281,7 +285,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -294,8 +298,8 @@ namespace hex {
|
||||
namespace ImHexApi::Provider {
|
||||
|
||||
static i64 s_currentProvider = -1;
|
||||
static AutoReset<std::vector<std::unique_ptr<prv::Provider>>> s_providers;
|
||||
static AutoReset<std::map<prv::Provider*, std::unique_ptr<prv::Provider>>> s_providersToRemove;
|
||||
static AutoReset<std::vector<std::shared_ptr<prv::Provider>>> s_providers;
|
||||
static AutoReset<std::map<prv::Provider*, std::shared_ptr<prv::Provider>>> s_providersToRemove;
|
||||
|
||||
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);
|
||||
|
||||
if (TaskManager::getRunningTaskCount() > 0)
|
||||
@@ -391,7 +395,7 @@ namespace hex {
|
||||
if (skipLoadInterface)
|
||||
provider->skipLoadInterface();
|
||||
|
||||
EventProviderCreated::post(provider.get());
|
||||
EventProviderCreated::post(provider);
|
||||
s_providers->emplace_back(std::move(provider));
|
||||
|
||||
if (select || s_providers->size() == 1)
|
||||
@@ -491,13 +495,17 @@ namespace hex {
|
||||
});
|
||||
}
|
||||
|
||||
prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {
|
||||
prv::Provider* result = nullptr;
|
||||
std::shared_ptr<prv::Provider> createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {
|
||||
std::shared_ptr<prv::Provider> result = nullptr;
|
||||
RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void openProvider(std::shared_ptr<prv::Provider> provider) {
|
||||
RequestOpenProvider::post(provider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ImHexApi::System {
|
||||
@@ -530,6 +538,11 @@ namespace hex {
|
||||
s_mainWindowHandle = window;
|
||||
}
|
||||
|
||||
static bool s_mainWindowFocused = false;
|
||||
void setMainWindowFocusState(bool focused) {
|
||||
s_mainWindowFocused = focused;
|
||||
}
|
||||
|
||||
|
||||
static float s_globalScale = 1.0;
|
||||
void setGlobalScale(float scale) {
|
||||
@@ -667,13 +680,15 @@ namespace hex {
|
||||
if (!sessionType.has_value() || sessionType == "x11")
|
||||
return 1.0F;
|
||||
else {
|
||||
float xScale = 0, yScale = 0;
|
||||
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xScale, &yScale);
|
||||
int windowW, windowH;
|
||||
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)
|
||||
return 1.0F;
|
||||
return emscripten_get_device_pixel_ratio();
|
||||
#else
|
||||
return 1.0F;
|
||||
#endif
|
||||
@@ -700,6 +715,10 @@ namespace hex {
|
||||
return impl::s_mainWindowHandle;
|
||||
}
|
||||
|
||||
bool isMainWindowFocused() {
|
||||
return impl::s_mainWindowFocused;
|
||||
}
|
||||
|
||||
bool isBorderlessWindowModeEnabled() {
|
||||
return impl::s_borderlessWindowMode;
|
||||
}
|
||||
@@ -896,7 +915,7 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
return { { name, version } };
|
||||
return { { .name=name, .version=version } };
|
||||
}
|
||||
|
||||
const SemanticVersion& getImHexVersion() {
|
||||
@@ -1201,6 +1220,10 @@ namespace hex {
|
||||
offset,
|
||||
fontSizeMultiplier
|
||||
);
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
macosRegisterFont(data.data(), data.size_bytes());
|
||||
#endif
|
||||
}
|
||||
|
||||
void registerFont(const Font& font) {
|
||||
@@ -1212,7 +1235,7 @@ namespace hex {
|
||||
|
||||
if (it == impl::s_fontDefinitions->end()) {
|
||||
const auto defaultFont = ImGui::GetDefaultFont();
|
||||
return { defaultFont, defaultFont, defaultFont };
|
||||
return { .regular=defaultFont, .bold=defaultFont, .italic=defaultFont };
|
||||
} else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void LayoutManager::lockLayout(bool locked) {
|
||||
log::info("Layout {}", locked ? "locked" : "unlocked");
|
||||
log::debug("Layout {}", locked ? "locked" : "unlocked");
|
||||
s_layoutLocked = locked;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,13 +50,13 @@ namespace hex {
|
||||
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;
|
||||
}
|
||||
|
||||
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()) {
|
||||
auto value = entry.value().get<std::string>();
|
||||
|
||||
// Skip empty values
|
||||
if (value.empty())
|
||||
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));
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
@@ -143,7 +155,7 @@ namespace hex {
|
||||
static AutoReset<std::unordered_map<std::size_t, std::string>> loadedLocalization;
|
||||
static std::mutex mutex;
|
||||
|
||||
std::lock_guard lock(mutex);
|
||||
std::scoped_lock lock(mutex);
|
||||
if (*currentLanguageId != languageId) {
|
||||
currentLanguageId = languageId;
|
||||
loadedLocalization->clear();
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
std::fs::path ProjectFile::getPath() {
|
||||
return s_currProjectPath;
|
||||
return *s_currProjectPath;
|
||||
}
|
||||
|
||||
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) {
|
||||
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!");
|
||||
}
|
||||
|
||||
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());
|
||||
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!");
|
||||
}
|
||||
|
||||
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());
|
||||
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!");
|
||||
}
|
||||
|
||||
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());
|
||||
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!");
|
||||
}
|
||||
|
||||
@@ -329,12 +329,13 @@ namespace hex {
|
||||
if (ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId))
|
||||
return true;
|
||||
|
||||
const bool currentlyTyping = ImGui::GetIO().WantTextInput;
|
||||
|
||||
auto it = shortcuts.find(shortcut + AllowWhileTyping);
|
||||
if (!currentlyTyping && it == shortcuts.end()) {
|
||||
auto it = shortcuts.end();
|
||||
if (ImGui::GetIO().WantTextInput) {
|
||||
it = shortcuts.find(shortcut + AllowWhileTyping);
|
||||
} else {
|
||||
it = shortcuts.find(shortcut);
|
||||
if (it == shortcuts.end())
|
||||
it = shortcuts.find(shortcut);
|
||||
it = shortcuts.find(shortcut + AllowWhileTyping);
|
||||
}
|
||||
|
||||
if (it != shortcuts.end()) {
|
||||
@@ -366,7 +367,17 @@ namespace hex {
|
||||
if (keyCode != 0)
|
||||
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) {
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <ranges>
|
||||
|
||||
#include <jthread.hpp>
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
#include <hex/trace/exceptions.hpp>
|
||||
#include <utility>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
@@ -52,6 +55,7 @@ namespace hex {
|
||||
std::list<std::function<void()>> s_deferredCalls;
|
||||
std::unordered_map<SourceLocationWrapper, std::function<void()>> s_onceDeferredCalls;
|
||||
std::list<std::function<void()>> s_tasksFinishedCallbacks;
|
||||
std::list<std::function<void(Task&)>> s_taskCompletionCallbacks;
|
||||
|
||||
std::mutex s_queueMutex;
|
||||
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)
|
||||
: m_unlocalizedName(unlocalizedName),
|
||||
Task::Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function)
|
||||
: m_unlocalizedName(std::move(unlocalizedName)),
|
||||
m_maxValue(maxValue),
|
||||
m_function(std::move(function)),
|
||||
m_background(background), m_blocking(blocking) { }
|
||||
@@ -310,6 +314,8 @@ namespace hex {
|
||||
}
|
||||
|
||||
try {
|
||||
trace::enableExceptionCaptureForCurrentThread();
|
||||
|
||||
// Set the thread name to the name of the task
|
||||
TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
|
||||
|
||||
@@ -317,21 +323,34 @@ namespace hex {
|
||||
task->m_function(*task);
|
||||
|
||||
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 &) {
|
||||
// Handle the task being interrupted by user request
|
||||
task->interruption();
|
||||
} catch (const std::exception &e) {
|
||||
log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what());
|
||||
|
||||
dbg::printStackTrace(trace::getStackTrace());
|
||||
|
||||
// Handle the task throwing an uncaught exception
|
||||
task->exception(e.what());
|
||||
} catch (...) {
|
||||
log::error("Exception in task '{}'", task->m_unlocalizedName.get());
|
||||
|
||||
dbg::printStackTrace(trace::getStackTrace());
|
||||
|
||||
// Handle the task throwing an uncaught exception of unknown type
|
||||
task->exception("Unknown Exception");
|
||||
}
|
||||
|
||||
trace::disableExceptionCaptureForCurrentThread();
|
||||
|
||||
s_currentTask = nullptr;
|
||||
task->finish();
|
||||
}
|
||||
@@ -350,7 +369,10 @@ namespace hex {
|
||||
thread.request_stop();
|
||||
|
||||
// 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
|
||||
s_workers.clear();
|
||||
@@ -361,6 +383,7 @@ namespace hex {
|
||||
s_deferredCalls.clear();
|
||||
s_onceDeferredCalls.clear();
|
||||
s_tasksFinishedCallbacks.clear();
|
||||
s_taskCompletionCallbacks.clear();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace hex {
|
||||
ImGuiID s_activeHelpId;
|
||||
bool s_helpHoverActive = false;
|
||||
|
||||
AutoReset<std::function<std::function<void()>(const std::string &)>> s_renderer;
|
||||
|
||||
|
||||
class IDStack {
|
||||
public:
|
||||
@@ -55,7 +57,7 @@ namespace hex {
|
||||
|
||||
void add(const void *pointer) {
|
||||
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);
|
||||
}
|
||||
@@ -94,38 +96,47 @@ namespace hex {
|
||||
}
|
||||
|
||||
void TutorialManager::init() {
|
||||
EventImGuiElementRendered::subscribe([](ImGuiID id, const std::array<float, 4> bb){
|
||||
const auto boundingBox = ImRect(bb[0], bb[1], bb[2], bb[3]);
|
||||
if (*s_renderer == nullptr) {
|
||||
*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))
|
||||
return;
|
||||
void TutorialManager::postElementRendered(ImGuiID id, const ImRect &boundingBox) {
|
||||
if (!ImGui::IsRectVisible(boundingBox.Min, boundingBox.Max))
|
||||
return;
|
||||
|
||||
{
|
||||
const auto element = hex::s_highlights->find(id);
|
||||
if (element != hex::s_highlights->end()) {
|
||||
hex::s_highlightDisplays->emplace_back(boundingBox, element->second);
|
||||
{
|
||||
const auto element = hex::s_highlights->find(id);
|
||||
if (element != hex::s_highlights->end()) {
|
||||
hex::s_highlightDisplays->emplace_back(boundingBox, element->second);
|
||||
|
||||
const auto window = ImGui::GetCurrentWindow();
|
||||
if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr)
|
||||
window->DockNode->TabBar->NextSelectedTabId = window->TabId;
|
||||
}
|
||||
const auto window = ImGui::GetCurrentWindow();
|
||||
if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr)
|
||||
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() {
|
||||
@@ -204,6 +215,10 @@ namespace hex {
|
||||
s_currentTutorial->second.start();
|
||||
}
|
||||
|
||||
void TutorialManager::stopCurrentTutorial() {
|
||||
s_currentTutorial = s_tutorials->end();
|
||||
}
|
||||
|
||||
void TutorialManager::drawHighlights() {
|
||||
if (s_helpHoverActive) {
|
||||
const auto &drawList = ImGui::GetForegroundDrawList(ImGui::GetMainViewport());
|
||||
@@ -303,10 +318,10 @@ namespace hex {
|
||||
|
||||
if (!message.has_value()) {
|
||||
message = Tutorial::Step::Message {
|
||||
Position::None,
|
||||
"",
|
||||
"",
|
||||
false
|
||||
.position=Position::None,
|
||||
.unlocalizedTitle="",
|
||||
.unlocalizedMessage="",
|
||||
.allowSkip=false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -333,34 +348,39 @@ namespace hex {
|
||||
|
||||
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
|
||||
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());
|
||||
|
||||
if (!message->unlocalizedTitle.empty())
|
||||
ImGuiExt::Header(Lang(message->unlocalizedTitle), true);
|
||||
|
||||
auto &step = s_currentTutorial->second.m_currentStep;
|
||||
if (!message->unlocalizedMessage.empty()) {
|
||||
ImGui::PushTextWrapPos(300_scaled);
|
||||
ImGui::TextUnformatted(Lang(message->unlocalizedMessage));
|
||||
ImGui::PopTextWrapPos();
|
||||
step->m_drawFunction();
|
||||
ImGui::NewLine();
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_steps.begin());
|
||||
if (ImGui::ArrowButton("Backwards", ImGuiDir_Left)) {
|
||||
ImGui::BeginDisabled(step == s_currentTutorial->second.m_steps.begin());
|
||||
if (ImGuiExt::DimmedArrowButton("Backwards", ImGuiDir_Left)) {
|
||||
s_currentTutorial->second.m_currentStep->advance(-1);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(!message->allowSkip && s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_latestStep);
|
||||
if (ImGui::ArrowButton("Forwards", ImGuiDir_Right)) {
|
||||
s_currentTutorial->second.m_currentStep->advance(1);
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.x);
|
||||
ImGui::BeginDisabled(!message->allowSkip && step == s_currentTutorial->second.m_latestStep);
|
||||
if (ImGuiExt::DimmedArrowButton("Forwards", ImGuiDir_Right)) {
|
||||
step->advance(1);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (!open) {
|
||||
stopCurrentTutorial();
|
||||
}
|
||||
}
|
||||
|
||||
void TutorialManager::drawTutorial() {
|
||||
@@ -387,6 +407,10 @@ namespace hex {
|
||||
s_highlightDisplays->clear();
|
||||
}
|
||||
|
||||
void TutorialManager::setRenderer(std::function<DrawFunction(const std::string &)> renderer) {
|
||||
s_renderer = std::move(renderer);
|
||||
}
|
||||
|
||||
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
|
||||
auto &newStep = m_steps.emplace_back(this);
|
||||
m_currentStep = m_steps.end();
|
||||
@@ -402,6 +426,9 @@ namespace hex {
|
||||
return;
|
||||
|
||||
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 {
|
||||
@@ -426,8 +453,12 @@ namespace hex {
|
||||
std::advance(m_parent->m_latestStep, 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();
|
||||
|
||||
if (m_message.has_value())
|
||||
m_parent->m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_parent->m_currentStep->m_message->unlocalizedMessage));
|
||||
}
|
||||
else
|
||||
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) {
|
||||
m_message = Message {
|
||||
position,
|
||||
unlocalizedTitle,
|
||||
unlocalizedMessage,
|
||||
false
|
||||
.position=position,
|
||||
.unlocalizedTitle=unlocalizedTitle,
|
||||
.unlocalizedMessage=unlocalizedMessage,
|
||||
.allowSkip=false
|
||||
};
|
||||
|
||||
return *this;
|
||||
@@ -464,10 +495,10 @@ namespace hex {
|
||||
m_message->allowSkip = true;
|
||||
} else {
|
||||
m_message = Message {
|
||||
Position::Bottom | Position::Right,
|
||||
"",
|
||||
"",
|
||||
true
|
||||
.position=Position::Bottom | Position::Right,
|
||||
.unlocalizedTitle="",
|
||||
.unlocalizedMessage="",
|
||||
.allowSkip=true
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace hex {
|
||||
|
||||
void WorkspaceManager::process() {
|
||||
if (s_previousWorkspace != s_currentWorkspace) {
|
||||
log::info("Updating workspace");
|
||||
log::debug("Updating workspace");
|
||||
if (s_previousWorkspace != s_workspaces->end()) {
|
||||
auto newWorkspace = s_currentWorkspace;
|
||||
s_currentWorkspace = s_previousWorkspace;
|
||||
|
||||
@@ -151,8 +151,8 @@ namespace hex::dp {
|
||||
m_overlay->getData() = data;
|
||||
}
|
||||
|
||||
[[noreturn]] void Node::throwNodeError(const std::string &message) {
|
||||
throw NodeError { this, message };
|
||||
[[noreturn]] void Node::throwNodeError(const std::string &msg) {
|
||||
throw NodeError(this, msg);
|
||||
}
|
||||
|
||||
void Node::setAttributes(std::vector<Attribute> attributes) {
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace hex {
|
||||
namespace {
|
||||
|
||||
void skipWhitespace(std::string_view &string) {
|
||||
while (string.length() > 0) {
|
||||
while (!string.empty()) {
|
||||
if (!std::isspace(string.front()))
|
||||
break;
|
||||
string = string.substr(1);
|
||||
@@ -89,15 +89,15 @@ namespace hex {
|
||||
return { };
|
||||
|
||||
bool inString = false;
|
||||
while (string.length() > 0) {
|
||||
BinaryPattern::Pattern pattern = { 0, 0 };
|
||||
while (!string.empty()) {
|
||||
BinaryPattern::Pattern pattern = { .mask=0, .value=0 };
|
||||
|
||||
if (string.starts_with("\"")) {
|
||||
inString = !inString;
|
||||
string = string.substr(1);
|
||||
continue;
|
||||
} else if (inString) {
|
||||
pattern = { 0xFF, u8(string.front()) };
|
||||
pattern = { .mask=0xFF, .value=u8(string.front()) };
|
||||
string = string.substr(1);
|
||||
} else if (string.starts_with("u") || string.starts_with("s")) {
|
||||
auto newPatterns = parseValueExpression(string);
|
||||
@@ -106,7 +106,7 @@ namespace hex {
|
||||
std::ranges::move(newPatterns, std::back_inserter(result));
|
||||
continue;
|
||||
} else if (string.starts_with("??")) {
|
||||
pattern = { 0x00, 0x00 };
|
||||
pattern = { .mask=0x00, .value=0x00 };
|
||||
string = string.substr(2);
|
||||
} else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) {
|
||||
const auto hex = string.substr(0, 2);
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <bit>
|
||||
@@ -82,7 +81,7 @@ namespace hex::crypt {
|
||||
|
||||
public:
|
||||
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_table([polynomial] {
|
||||
auto reflectedPoly = reflect(polynomial & ((0b10ull << (NumBits - 1)) - 1), NumBits);
|
||||
@@ -129,7 +128,7 @@ namespace hex::crypt {
|
||||
}
|
||||
|
||||
private:
|
||||
u64 m_value;
|
||||
u64 m_value = 0x00;
|
||||
|
||||
u64 m_init;
|
||||
u64 m_xorOut;
|
||||
@@ -144,7 +143,7 @@ namespace hex::crypt {
|
||||
using Crc = Crc<NumBits>;
|
||||
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();
|
||||
}
|
||||
@@ -170,7 +169,7 @@ namespace hex::crypt {
|
||||
|
||||
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());
|
||||
|
||||
@@ -202,7 +201,7 @@ namespace hex::crypt {
|
||||
|
||||
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());
|
||||
|
||||
@@ -234,7 +233,7 @@ namespace hex::crypt {
|
||||
|
||||
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());
|
||||
|
||||
@@ -266,7 +265,7 @@ namespace hex::crypt {
|
||||
|
||||
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());
|
||||
|
||||
@@ -298,7 +297,7 @@ namespace hex::crypt {
|
||||
|
||||
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());
|
||||
|
||||
@@ -330,7 +329,7 @@ namespace hex::crypt {
|
||||
|
||||
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());
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/trace/stacktrace.hpp>
|
||||
|
||||
namespace hex::dbg {
|
||||
|
||||
@@ -21,4 +23,23 @@ namespace hex::dbg {
|
||||
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/api/imhex_api/system.hpp>
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
@@ -17,6 +18,13 @@ namespace hex::paths {
|
||||
|
||||
std::vector<std::fs::path> getDataPaths(bool includeSystemFolders) {
|
||||
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)
|
||||
|
||||
@@ -25,21 +33,22 @@ namespace hex::paths {
|
||||
if (!ImHexApi::System::isPortableVersion()) {
|
||||
PWSTR wAppDataPath = nullptr;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) {
|
||||
paths.emplace_back(wAppDataPath);
|
||||
emplaceUniquePath(wAppDataPath);
|
||||
CoTaskMemFree(wAppDataPath);
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
|
||||
paths.push_back(xdg::DataHomeDir());
|
||||
emplaceUniquePath(xdg::DataHomeDir());
|
||||
|
||||
auto dataDirs = xdg::DataDirs();
|
||||
std::copy(dataDirs.begin(), dataDirs.end(), std::back_inserter(paths));
|
||||
for (const auto &path : dataDirs)
|
||||
emplaceUniquePath(path);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,18 +56,21 @@ namespace hex::paths {
|
||||
|
||||
if (includeSystemFolders) {
|
||||
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
|
||||
paths.push_back(executablePath->parent_path());
|
||||
emplaceUniquePath(executablePath->parent_path());
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for (auto &path : paths)
|
||||
uniquePaths.clear();
|
||||
for (auto &path : paths) {
|
||||
path = path / "imhex";
|
||||
uniquePaths.insert(path);
|
||||
}
|
||||
|
||||
if (ImHexApi::System::isPortableVersion() || includeSystemFolders) {
|
||||
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())
|
||||
paths.push_back(executablePath->parent_path());
|
||||
emplaceUniquePath(executablePath->parent_path());
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -66,11 +78,12 @@ namespace hex::paths {
|
||||
|
||||
// Add additional data directories to the path
|
||||
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
|
||||
if (ProjectFile::hasPath()) {
|
||||
paths.push_back(ProjectFile::getPath().parent_path());
|
||||
emplaceUniquePath(ProjectFile::getPath().parent_path());
|
||||
}
|
||||
|
||||
return paths;
|
||||
@@ -97,11 +110,18 @@ namespace hex::paths {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Add the system plugin directory to the path if one was provided at compile time
|
||||
#if defined(OS_LINUX) && defined(SYSTEM_PLUGINS_LOCATION)
|
||||
paths.push_back(SYSTEM_PLUGINS_LOCATION);
|
||||
paths.emplace_back(SYSTEM_PLUGINS_LOCATION);
|
||||
#endif
|
||||
|
||||
return paths;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <ranges>
|
||||
#include <wolv/io/file.hpp>
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
@@ -65,6 +66,9 @@ namespace hex {
|
||||
|
||||
|
||||
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_tableContent = other.m_tableContent;
|
||||
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 {
|
||||
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
|
||||
const auto &[size, mapping] = *riter;
|
||||
|
||||
for (const auto &[size, mapping] : std::ranges::reverse_view(*m_mapping)) {
|
||||
if (size > buffer.size()) continue;
|
||||
|
||||
std::vector key(buffer.begin(), buffer.begin() + size);
|
||||
@@ -103,9 +105,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
u64 EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
|
||||
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
|
||||
const auto &[size, mapping] = *riter;
|
||||
|
||||
for (const auto& [size, mapping] : std::ranges::reverse_view(*m_mapping)) {
|
||||
if (size > buffer.size()) continue;
|
||||
|
||||
std::vector key(buffer.begin(), buffer.begin() + size);
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#include <xdg.hpp>
|
||||
# if defined(OS_FREEBSD)
|
||||
#include <sys/syslimits.h>
|
||||
# else
|
||||
#include <limits.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#include <array>
|
||||
#include <hex/api/tutorial_manager.hpp>
|
||||
|
||||
#if !defined(IMGUI_TEST_ENGINE)
|
||||
|
||||
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::EventImGuiElementRendered::post(id, boundingBox);
|
||||
hex::TutorialManager::postElementRendered(id, bb);
|
||||
}
|
||||
|
||||
void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <fmt/chrono.h>
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <Windows.h>
|
||||
@@ -117,8 +118,8 @@ namespace hex::log {
|
||||
|
||||
void addLogEntry(std::string_view project, std::string_view level, std::string message) {
|
||||
s_logEntries->emplace_back(
|
||||
std::move(project),
|
||||
std::move(level),
|
||||
project,
|
||||
level,
|
||||
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 {
|
||||
|
||||
fmt::color debug() { return fmt::color::medium_sea_green; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
struct KeyEquivalent {
|
||||
bool valid;
|
||||
@@ -61,7 +62,120 @@ void macosEndMainMenuBar(void) {
|
||||
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];
|
||||
|
||||
// Search for menu item with the given name
|
||||
@@ -79,6 +193,17 @@ bool macosBeginMenu(const char* label, bool enabled) {
|
||||
menuItem.title = title;
|
||||
[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
|
||||
menuIndex = [s_menuStack[s_menuStackSize - 1] numberOfItems];
|
||||
|
||||
@@ -104,7 +229,7 @@ void macosEndMenu(void) {
|
||||
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];
|
||||
|
||||
if (s_constructingMenu) {
|
||||
@@ -113,6 +238,12 @@ bool macosMenuItem(const char* label, struct KeyEquivalent keyEquivalent, bool s
|
||||
menuItem.action = @selector(OnClick:);
|
||||
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];
|
||||
s_currTag += 1;
|
||||
|
||||
@@ -164,8 +295,8 @@ bool macosMenuItem(const char* label, struct KeyEquivalent keyEquivalent, bool s
|
||||
return false;
|
||||
}
|
||||
|
||||
bool macosMenuItemSelect(const char* label, struct KeyEquivalent keyEquivalent, bool* selected, bool enabled) {
|
||||
if (macosMenuItem(label, keyEquivalent, selected != NULL ? *selected : false, enabled)) {
|
||||
bool macosMenuItemSelect(const char* label, const char *icon, struct KeyEquivalent keyEquivalent, bool* selected, bool enabled) {
|
||||
if (macosMenuItem(label, icon, keyEquivalent, selected != NULL ? *selected : false, enabled)) {
|
||||
if (selected != NULL)
|
||||
*selected = !(*selected);
|
||||
|
||||
@@ -180,3 +311,94 @@ void macosSeparator(void) {
|
||||
[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 <opengl_support.h>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
#include <numbers>
|
||||
@@ -133,17 +138,18 @@ namespace hex::gl {
|
||||
|
||||
|
||||
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()) {
|
||||
auto location = glGetUniformLocation(m_program, name.data());
|
||||
auto location = glGetUniformLocation(m_program, nameStr.data());
|
||||
if (location == -1) {
|
||||
log::warn("Uniform '{}' not found in shader", name);
|
||||
m_uniforms[name.data()] = -1;
|
||||
m_uniforms[nameStr] = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_uniforms[name.data()] = location;
|
||||
uniform = m_uniforms.find(name.data());
|
||||
m_uniforms[nameStr] = location;
|
||||
uniform = m_uniforms.find(nameStr);
|
||||
}
|
||||
|
||||
return uniform->second;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <hex/helpers/patches.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
@@ -5,7 +6,6 @@
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
namespace hex {
|
||||
@@ -24,7 +24,7 @@ namespace hex {
|
||||
[[nodiscard]] bool isSavable() 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 readRaw(u64 offset, void *buffer, size_t size) override {
|
||||
@@ -83,7 +83,7 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] UnlocalizedString getTypeName() const override { return ""; }
|
||||
|
||||
const std::map<u64, u8>& getPatches() const {
|
||||
[[nodiscard]] const std::map<u64, u8>& getPatches() const {
|
||||
return m_patches;
|
||||
}
|
||||
private:
|
||||
@@ -92,7 +92,7 @@ namespace hex {
|
||||
|
||||
|
||||
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>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <microtar.h>
|
||||
|
||||
#include "wolv/utils/string.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
using namespace hex::literals;
|
||||
@@ -22,13 +24,13 @@ namespace hex {
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
tarError = mtar_open(m_ctx.get(), shortPath.string().c_str(), "w");
|
||||
tarError = mtar_open(m_ctx.get(), shortPath.c_str(), "w");
|
||||
else
|
||||
tarError = MTAR_EFAILURE;
|
||||
|
||||
@@ -187,4 +189,4 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace hex {
|
||||
|
||||
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() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <cwchar>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
@@ -29,10 +30,12 @@
|
||||
#elif defined(OS_LINUX)
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <spawn.h>
|
||||
#include <hex/helpers/utils_linux.hpp>
|
||||
#elif defined(OS_MACOS)
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <spawn.h>
|
||||
#include <hex/helpers/utils_macos.hpp>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#elif defined(OS_WEB)
|
||||
@@ -98,7 +101,7 @@ namespace hex {
|
||||
string = wolv::util::replaceStrings(string, ",", "");
|
||||
|
||||
// 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);
|
||||
}) == string.end();
|
||||
|
||||
@@ -309,6 +312,86 @@ namespace hex {
|
||||
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) {
|
||||
if (!url.contains("://"))
|
||||
url = "https://" + url;
|
||||
@@ -463,7 +546,7 @@ namespace hex {
|
||||
if (ch <= 0x7F) {
|
||||
unicode = ch;
|
||||
unicodeSize = 0;
|
||||
} else if (ch <= 0xBF) {
|
||||
} else if (ch <= 0xBF) { //NOLINT(bugprone-branch-clone)
|
||||
return { };
|
||||
} else if (ch <= 0xDF) {
|
||||
unicode = ch&0x1F;
|
||||
@@ -524,7 +607,7 @@ namespace hex {
|
||||
index += 1;
|
||||
|
||||
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) {
|
||||
if (index == utf16.size())
|
||||
return "";
|
||||
@@ -785,7 +868,7 @@ namespace hex {
|
||||
input.imbue(std::locale(std::setlocale(LC_ALL, nullptr)));
|
||||
|
||||
tm time = {};
|
||||
input >> std::get_time(&time, format.data());
|
||||
input >> std::get_time(&time, std::string(format).data());
|
||||
if (input.fail()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -824,10 +907,10 @@ namespace hex {
|
||||
|
||||
if (lang.has_value() && !lang->empty() && *lang != "C" && *lang != "C.UTF-8") {
|
||||
auto parts = wolv::util::splitString(*lang, ".");
|
||||
if (parts.size() > 0)
|
||||
if (!parts.empty())
|
||||
return parts[0];
|
||||
else
|
||||
return *lang;
|
||||
return lang;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
@@ -905,6 +988,12 @@ namespace hex {
|
||||
nid.dwInfoFlags = NIIF_INFO;
|
||||
|
||||
Shell_NotifyIcon(NIM_ADD, &nid);
|
||||
|
||||
Sleep(100);
|
||||
|
||||
Shell_NotifyIcon(NIM_DELETE, &nid);
|
||||
CloseWindow(hwnd);
|
||||
DestroyWindow(hwnd);
|
||||
#elif defined(OS_MACOS)
|
||||
toastMessageMacos(title.c_str(), message.c_str());
|
||||
#elif defined(OS_LINUX)
|
||||
@@ -913,17 +1002,21 @@ namespace hex {
|
||||
}
|
||||
#elif defined(OS_WEB)
|
||||
EM_ASM({
|
||||
const t = UTF8ToString($0);
|
||||
const m = UTF8ToString($1);
|
||||
try {
|
||||
const t = UTF8ToString($0);
|
||||
const m = UTF8ToString($1);
|
||||
|
||||
if (Notification.permission === "granted") {
|
||||
new Notification(t, { body: m });
|
||||
} else if (Notification.permission !== "denied") {
|
||||
Notification.requestPermission().then(function(p) {
|
||||
if (p === "granted") {
|
||||
new Notification(t, { body: m });
|
||||
}
|
||||
});
|
||||
if (Notification.permission === "granted") {
|
||||
new Notification(t, { body: m });
|
||||
} else if (Notification.permission !== "denied") {
|
||||
Notification.requestPermission().then(function(p) {
|
||||
if (p === "granted") {
|
||||
new Notification(t, { body: m });
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, title.c_str(), message.c_str());
|
||||
#endif
|
||||
|
||||
@@ -10,13 +10,14 @@ namespace hex {
|
||||
|
||||
void executeCmd(const std::vector<std::string> &argsVector) {
|
||||
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(nullptr);
|
||||
|
||||
if (fork() == 0) {
|
||||
execvp(cArgsVector[0], &cArgsVector[0]);
|
||||
execvp(cArgsVector[0], cArgsVector.data());
|
||||
log::error("execvp() failed: {}", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
#include <CoreFoundation/CFBundle.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Foundation/NSUserDefaults.h>
|
||||
@@ -70,6 +73,49 @@
|
||||
cocoaWindow.titlebarAppearsTransparent = YES;
|
||||
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 setHasShadow:YES];
|
||||
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
|
||||
@@ -370,6 +416,27 @@
|
||||
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) {
|
||||
@autoreleasepool {
|
||||
// Only show notification if we're inside a bundle
|
||||
@@ -407,5 +474,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
bool CachedProvider::open() {
|
||||
Provider::OpenResult CachedProvider::open() {
|
||||
clearCache();
|
||||
return true;
|
||||
return {};
|
||||
}
|
||||
|
||||
void CachedProvider::close() {
|
||||
@@ -49,7 +49,7 @@ namespace hex::prv {
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace hex::prv {
|
||||
if (!slot || slot->index != blockIndex) {
|
||||
std::vector<uint8_t> blockData(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);
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
bool MemoryProvider::open() {
|
||||
Provider::OpenResult MemoryProvider::open() {
|
||||
if (m_data.empty()) {
|
||||
m_data.resize(1);
|
||||
}
|
||||
|
||||
return true;
|
||||
return {};
|
||||
}
|
||||
|
||||
void MemoryProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <hex/api/content_registry/settings.hpp>
|
||||
|
||||
#include <hex/helpers/magic.hpp>
|
||||
#include <wolv/io/file.hpp>
|
||||
#include <wolv/literals.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
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++) {
|
||||
|
||||
@@ -33,7 +57,7 @@ namespace hex::prv {
|
||||
m_overlays.clear();
|
||||
|
||||
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) {
|
||||
@@ -109,7 +133,6 @@ namespace hex::prv {
|
||||
this->resizeRaw(newSize);
|
||||
|
||||
std::vector<u8> buffer(0x1000, 0x00);
|
||||
const std::vector<u8> zeroBuffer(0x1000, 0x00);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
@@ -118,9 +141,18 @@ namespace hex::prv {
|
||||
position -= readSize;
|
||||
|
||||
this->readRaw(position, buffer.data(), readSize);
|
||||
this->writeRaw(position, zeroBuffer.data(), newSize - oldSize);
|
||||
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) {
|
||||
@@ -265,19 +297,19 @@ namespace hex::prv {
|
||||
u64 absoluteAddress = address - this->getBaseAddress();
|
||||
|
||||
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;
|
||||
|
||||
std::optional<u64> nextRegionAddress;
|
||||
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) {
|
||||
nextRegionAddress = overlayRegion.getStartAddress();
|
||||
}
|
||||
|
||||
if (Region { address, 1 }.overlaps(overlayRegion)) {
|
||||
if (Region { .address=address, .size=1 }.overlaps(overlayRegion)) {
|
||||
insideValidRegion = true;
|
||||
}
|
||||
}
|
||||
@@ -285,7 +317,7 @@ namespace hex::prv {
|
||||
if (!nextRegionAddress.has_value())
|
||||
return { Region::Invalid(), false };
|
||||
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