mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 15:57:03 -05:00
Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3db8e2aec2 | ||
|
|
499b68b7ad | ||
|
|
2e3bb8e555 | ||
|
|
0c9eab70d5 | ||
|
|
0d3eaa5d86 | ||
|
|
c20634e093 | ||
|
|
5ee3b550bc | ||
|
|
831dac9b47 | ||
|
|
626c34dce8 | ||
|
|
ed67c20cba | ||
|
|
35c209c791 | ||
|
|
315109aa1f | ||
|
|
a57fa34f82 | ||
|
|
d1a2f7d6ed | ||
|
|
8382f68601 | ||
|
|
3aa1dd1e06 | ||
|
|
0571fe383c | ||
|
|
461c5eac3e | ||
|
|
e34f94bb79 | ||
|
|
071bae345e | ||
|
|
9f4625aa00 | ||
|
|
9837473810 | ||
|
|
9d3759c6cd | ||
|
|
6aa55eb056 | ||
|
|
c8b7f350ad | ||
|
|
b6b5045340 | ||
|
|
ab4f17a6f4 | ||
|
|
541f1d5550 | ||
|
|
12942a4e71 | ||
|
|
1a378381bd | ||
|
|
1354c913a4 | ||
|
|
c752fba1c4 | ||
|
|
d40a445f33 | ||
|
|
e0cae1dacb | ||
|
|
31a746f3fc | ||
|
|
b401059678 | ||
|
|
a30f49c75e | ||
|
|
e981fa53f3 | ||
|
|
4cd390ab02 | ||
|
|
716d6ae850 | ||
|
|
ceb07b7425 | ||
|
|
4885175ac6 | ||
|
|
d0f1a40f16 | ||
|
|
fc20d751bb | ||
|
|
dfc22abf35 | ||
|
|
de269e7a48 | ||
|
|
0ed885fe0f | ||
|
|
f9fc7051fc | ||
|
|
ab1f4df9d9 | ||
|
|
710771b8b1 | ||
|
|
2d982e2088 | ||
|
|
ef5fbba56b | ||
|
|
eadcc6f38c | ||
|
|
3db50a690c | ||
|
|
96aa929c31 | ||
|
|
e07fc76abf | ||
|
|
f01e227c87 | ||
|
|
cd34d567a7 | ||
|
|
bb429aae62 | ||
|
|
19f99bab0c | ||
|
|
1f433fc36d | ||
|
|
034cc0cd2f | ||
|
|
3efdc02fed | ||
|
|
501d141e13 | ||
|
|
9c1006f3ae | ||
|
|
5b0813478e | ||
|
|
ac964dc5ec | ||
|
|
11c2f240a1 | ||
|
|
8db2bdb6a7 | ||
|
|
7242eb8f4c | ||
|
|
60c6abbfcc | ||
|
|
a4c432f435 | ||
|
|
d50be26771 | ||
|
|
673027c82d | ||
|
|
e02ccd9b9b | ||
|
|
956276d1ee | ||
|
|
a936cf1ce4 | ||
|
|
5800546369 | ||
|
|
01adc8a2cd | ||
|
|
b1b33b2ae4 | ||
|
|
6506291e4e | ||
|
|
3471b314dd | ||
|
|
546d0a4922 | ||
|
|
c6989c2ef7 | ||
|
|
a5aa002752 | ||
|
|
b89490bca3 | ||
|
|
e33726f526 | ||
|
|
c238767750 | ||
|
|
116aeede2d | ||
|
|
662d80abea | ||
|
|
f6ddb3c5e7 | ||
|
|
6490e565d3 | ||
|
|
6b7ade8d61 | ||
|
|
9b77d7b5e2 | ||
|
|
1785088456 | ||
|
|
4dcd26a21f | ||
|
|
12e99a9d4c | ||
|
|
5e67a1f27b | ||
|
|
2a76e45dc5 | ||
|
|
6266883e81 | ||
|
|
aed9d15625 | ||
|
|
5551e82fea | ||
|
|
653a688fe6 | ||
|
|
dfc1dc2529 | ||
|
|
1e511acf37 | ||
|
|
141ee62af9 | ||
|
|
a5d202ffc8 | ||
|
|
f243ac7464 | ||
|
|
91ac9ca120 | ||
|
|
a56ba50cf9 | ||
|
|
fdaad55cc6 | ||
|
|
9d19214be9 | ||
|
|
038a6b9757 | ||
|
|
bad109ef8d | ||
|
|
5623e1342b | ||
|
|
6bad50c78b | ||
|
|
6929ffb865 | ||
|
|
2d7fdc0896 | ||
|
|
d1d73bcff6 | ||
|
|
bf1441223c | ||
|
|
fadca9a34a | ||
|
|
2ef3a0c157 | ||
|
|
c96a0a7bda | ||
|
|
fe6be686b7 | ||
|
|
05862ae991 | ||
|
|
6a6b6b94cf | ||
|
|
4701b1b67c | ||
|
|
f1b2d5881e | ||
|
|
efed07ac8b | ||
|
|
e5ff987392 | ||
|
|
8a24517fb9 | ||
|
|
a4c8bcab18 | ||
|
|
4fd8ada4ff | ||
|
|
7bf94ffe42 | ||
|
|
088205385f | ||
|
|
39c743631b | ||
|
|
603a95debb | ||
|
|
28a8adb26d | ||
|
|
e2bfd26bb3 | ||
|
|
857aadfa61 | ||
|
|
b8bbbd5489 | ||
|
|
ffb9a8b7ed | ||
|
|
b751f98e91 | ||
|
|
c0ceaa4195 | ||
|
|
6121c35e02 | ||
|
|
c3ea0c74ee | ||
|
|
610f189839 | ||
|
|
5b74739c51 | ||
|
|
775e87ff1f | ||
|
|
c4b7d89713 | ||
|
|
5f17d7aa75 | ||
|
|
3595a94b67 | ||
|
|
435edad604 | ||
|
|
87e616ad23 | ||
|
|
9d556ecc0f | ||
|
|
ea2c2df614 | ||
|
|
202a02af10 | ||
|
|
6ee71e3a9e | ||
|
|
1e7ed14810 | ||
|
|
17383083fb | ||
|
|
f5fe49923b | ||
|
|
457d338a97 | ||
|
|
4928c044af | ||
|
|
299c69686e | ||
|
|
b7132af214 | ||
|
|
74a5c974e6 | ||
|
|
942a4e9616 | ||
|
|
76f732dc53 | ||
|
|
0462cc3d0c | ||
|
|
965207d688 | ||
|
|
0f0a836fa0 | ||
|
|
29fb1de882 | ||
|
|
a807dc81a0 | ||
|
|
a4d3173da9 | ||
|
|
4b6a76bf02 | ||
|
|
35a520f132 | ||
|
|
1c53d2c123 | ||
|
|
591d98b55b | ||
|
|
f39ec58435 | ||
|
|
e72a30ca59 | ||
|
|
368c943040 | ||
|
|
a16e387dff | ||
|
|
95cf828975 | ||
|
|
c09d85f46d | ||
|
|
c2803fe1e2 | ||
|
|
98dfc2e286 | ||
|
|
ea848dbfc0 | ||
|
|
f7cfee55d5 | ||
|
|
26a7b3325d | ||
|
|
47fd5bdc00 | ||
|
|
5dfa9cf501 | ||
|
|
854c99bafa | ||
|
|
937ccbc5bd | ||
|
|
bc7c494316 | ||
|
|
17a2be41da | ||
|
|
ab5966fa9d | ||
|
|
89fe063b02 | ||
|
|
7061a1ebfa | ||
|
|
ec9a947259 | ||
|
|
11441d632b | ||
|
|
a17b647e79 | ||
|
|
2d87d29fa0 | ||
|
|
844845223f | ||
|
|
f2159e26d2 | ||
|
|
d677762dff | ||
|
|
3801e0d60b | ||
|
|
f6a498854c | ||
|
|
29ded2483c | ||
|
|
050e17298a | ||
|
|
af882b172e | ||
|
|
caad8c25ad | ||
|
|
74ef9ece30 | ||
|
|
97bfb4004b | ||
|
|
3da1b3f05d | ||
|
|
f21b22ae15 | ||
|
|
327e904dbc | ||
|
|
57c449936f | ||
|
|
efe6137067 | ||
|
|
96e9400761 | ||
|
|
6a517feda3 | ||
|
|
3b7a928313 | ||
|
|
2739320f10 | ||
|
|
7866e3fc2a | ||
|
|
2abf89cd16 | ||
|
|
8b2dcf976f | ||
|
|
559b86efe1 | ||
|
|
949a26ca0e | ||
|
|
2880ca00da | ||
|
|
39da62532b | ||
|
|
483ba95d80 | ||
|
|
2300b0c692 | ||
|
|
cc59b36c54 | ||
|
|
61d9918dae | ||
|
|
2c361f9b0a | ||
|
|
775b3e8c52 | ||
|
|
2a989c6cc1 | ||
|
|
3d7adf6483 | ||
|
|
56079f70c7 | ||
|
|
6c9e969099 | ||
|
|
76f8e6d6ef | ||
|
|
174cf3ed95 | ||
|
|
540f468e8a |
29
.github/workflows/analysis.yml
vendored
29
.github/workflows/analysis.yml
vendored
@@ -3,11 +3,12 @@ name: "CodeQL"
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
codeql:
|
||||
name: 🐛 CodeQL
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -15,24 +16,30 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: ✋ Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: 'cpp'
|
||||
|
||||
- name: 📜 Restore cache
|
||||
uses: actions/cache@v2
|
||||
- name: 📜 Restore ccache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.ccache
|
||||
~/.cache/ccache
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
@@ -42,7 +49,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-10 CXX=g++-10 cmake \
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
@@ -53,4 +60,4 @@ jobs:
|
||||
make -j 4 install
|
||||
|
||||
- name: 🗯️ Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
280
.github/workflows/build.yml
vendored
280
.github/workflows/build.yml
vendored
@@ -2,7 +2,9 @@ name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
@@ -12,7 +14,7 @@ jobs:
|
||||
# Windows build
|
||||
win:
|
||||
runs-on: windows-2022
|
||||
name: 🟦 Windows MINGW64
|
||||
name: 🪟 Windows MINGW64
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
@@ -22,11 +24,33 @@ jobs:
|
||||
CCACHE_COMPRESS: "true"
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Prepare Cache
|
||||
id: prep-ccache
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "${CCACHE_DIR}"
|
||||
echo "::set-output name=dir::$CCACHE_DIR"
|
||||
|
||||
- name: 📜 Restore ccache
|
||||
uses: actions/cache@v3
|
||||
id: cache-ccache
|
||||
with:
|
||||
path: |
|
||||
${{ steps.prep-ccache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: 🟦 Install msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@@ -52,21 +76,6 @@ jobs:
|
||||
$USERPROFILE/.cargo/bin/rustup.exe target add x86_64-pc-windows-gnu
|
||||
$USERPROFILE/.cargo/bin/rustup.exe default nightly
|
||||
|
||||
- name: 📜 Prepare Cache
|
||||
id: prep-ccache
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "${CCACHE_DIR}"
|
||||
echo "::set-output name=dir::$CCACHE_DIR"
|
||||
|
||||
- name: 📜 Restore Cache
|
||||
uses: actions/cache@v1
|
||||
id: cache-ccache
|
||||
with:
|
||||
path: ${{ steps.prep-ccache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: |
|
||||
mkdir -p build
|
||||
@@ -84,20 +93,22 @@ jobs:
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DRUST_PATH="$USERPROFILE/.cargo/bin/" \
|
||||
..
|
||||
mingw32-make -j4 install
|
||||
cpack
|
||||
|
||||
- name: ⬆️ Upload Portable ZIP
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Windows Portable ZIP
|
||||
name: Windows Portable
|
||||
path: |
|
||||
build/install/*
|
||||
|
||||
- name: ⬆️ Upload Windows Installer
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Windows Installer
|
||||
path: |
|
||||
@@ -105,72 +116,89 @@ jobs:
|
||||
|
||||
# MacOS build
|
||||
macos:
|
||||
runs-on: macos-11.0
|
||||
runs-on: macos-11
|
||||
name: 🍎 macOS 11.0
|
||||
steps:
|
||||
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Restore ccache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/ccache
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build
|
||||
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
brew bundle --no-lock --file dist/Brewfile
|
||||
|
||||
- name: 📜 Restore cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.ccache
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=$(brew --prefix llvm)/bin/clang \
|
||||
CXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
CC=$(brew --prefix gcc@12)/bin/gcc-12 \
|
||||
CXX=$(brew --prefix gcc@12)/bin/g++-12 \
|
||||
OBJC=$(brew --prefix llvm)/bin/clang \
|
||||
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \
|
||||
MACOSX_DEPLOYMENT_TARGET="10.15" \
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCREATE_BUNDLE=ON \
|
||||
-DCREATE_PACKAGE=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
MACOSX_DEPLOYMENT_TARGET="10.15" \
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCREATE_BUNDLE=ON \
|
||||
-DCREATE_PACKAGE=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
..
|
||||
make -j4 package
|
||||
|
||||
- name: ⬆️ Upload DMG
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macOS DMG
|
||||
path: build/*.dmg
|
||||
|
||||
# Linux build
|
||||
linux:
|
||||
runs-on: ubuntu-20.04
|
||||
name: 🐧 Ubuntu 20.04
|
||||
runs-on: ubuntu-22.04
|
||||
name: 🐧 Ubuntu 22.04
|
||||
steps:
|
||||
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Restore cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.ccache
|
||||
~/.cache/ccache
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build
|
||||
|
||||
- name: 📜 Restore other caches
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
.flatpak-builder
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
@@ -180,6 +208,11 @@ jobs:
|
||||
|
||||
sudo apt update
|
||||
sudo bash dist/get_deps_debian.sh
|
||||
|
||||
sudo apt install -y python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse
|
||||
sudo wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /usr/local/bin/appimagetool
|
||||
sudo chmod +x /usr/local/bin/appimagetool
|
||||
sudo pip3 install appimage-builder==1.0.0
|
||||
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh
|
||||
sh rustup-init.sh -y --default-toolchain none
|
||||
@@ -192,16 +225,22 @@ jobs:
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-10 CXX=g++-10 cmake \
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install/usr" \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DRUST_PATH="$HOME/.cargo/bin/" \
|
||||
..
|
||||
make -j 4 install
|
||||
make -j 4 install DESTDIR=AppDir
|
||||
|
||||
- name: 📜 Set version variable
|
||||
run: |
|
||||
echo "version=`cat VERSION`" >> $GITHUB_ENV
|
||||
|
||||
#- name: 📦 Bundle Flatpak
|
||||
# run: |
|
||||
@@ -214,41 +253,122 @@ jobs:
|
||||
|
||||
- name: 📦 Bundle DEB
|
||||
run: |
|
||||
cp -r build/DEBIAN build/install
|
||||
dpkg-deb --build build/install
|
||||
mv build/install.deb imhex.deb
|
||||
rm -rf build/install/DEBIAN
|
||||
cp -r build/DEBIAN build/AppDir
|
||||
dpkg-deb --build build/AppDir
|
||||
mv build/AppDir.deb imhex-${{env.version}}.deb
|
||||
rm -rf build/AppDir/DEBIAN
|
||||
|
||||
- name: 📦 Bundle AppImage
|
||||
run: |
|
||||
dist/AppImage/package.sh build
|
||||
mv build/ImHex-x86_64.AppImage imhex.AppImage
|
||||
cd build
|
||||
appimage-builder --recipe ../dist/AppImageBuilder.yml
|
||||
mv ImHex-AppImage-x86_64.AppImage ../imhex-${{env.version}}.AppImage
|
||||
cd ..
|
||||
|
||||
- name: ⬆️ Upload ELF
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Linux ELF
|
||||
path: |
|
||||
build/install/*
|
||||
#- name: ⬆️ Upload Flatpak
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: Linux Flatpak
|
||||
# path: |
|
||||
# imhex.flatpak
|
||||
|
||||
- name: ⬆️ Upload Flatpak
|
||||
uses: actions/upload-artifact@v2
|
||||
- name: ⬆️ Upload DEB
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Linux Flatpak
|
||||
path: |
|
||||
imhex.flatpak
|
||||
|
||||
- name: ⬆️ Upload .deb
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Linux DEB
|
||||
path: |
|
||||
imhex.deb
|
||||
name: Linux DEB (Ubuntu 22.04)
|
||||
path: '*.deb'
|
||||
|
||||
- name: ⬆️ Upload AppImage
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Linux AppImage
|
||||
path: |
|
||||
imhex.AppImage
|
||||
path: '*.AppImage'
|
||||
|
||||
archlinux-build:
|
||||
name: 🐧 ArchLinux
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: archlinux:base-devel
|
||||
|
||||
steps:
|
||||
- name: ⬇️ Update all packages
|
||||
run: |
|
||||
pacman -Syyu --noconfirm
|
||||
|
||||
- name: ⬇️ Install setup dependencies
|
||||
run: |
|
||||
pacman -Syu git ccache --noconfirm
|
||||
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: ⬇️ Install ImHex dependencies
|
||||
run: |
|
||||
dist/get_deps_archlinux.sh --noconfirm
|
||||
|
||||
- name: 📜 Restore ccache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ccache
|
||||
key: archlinux-${{ secrets.CACHE_VERSION }}-build-${{ github.run_id }}
|
||||
restore-keys: archlinux-${{ secrets.CACHE_VERSION }}-build
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: archlinux-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc CXX=g++ cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
|
||||
-DRUST_PATH="$HOME/.cargo/bin/" \
|
||||
..
|
||||
make -j 4 install DESTDIR=installDir
|
||||
|
||||
- name: 📜 Set version variable
|
||||
run: |
|
||||
echo "version=`cat VERSION`" >> $GITHUB_ENV
|
||||
|
||||
- name: ✒️ Prepare PKGBUILD
|
||||
run: |
|
||||
cp dist/Arch/PKGBUILD build
|
||||
sed -i 's/%version%/${{env.version}}/g' build/PKGBUILD
|
||||
|
||||
# makepkg doesn't want to run as root, so I had to chmod 777 all over
|
||||
- name: 📦 Package ArchLinux .pkg.tar.zst
|
||||
run: |
|
||||
cd build
|
||||
|
||||
# the name is a small trick to make makepkg recognize it as the source
|
||||
# else, it would try to download the file from the release
|
||||
tar -cvf imhex-${{env.version}}-ArchLinux.pkg.tar.zst -C installDir .
|
||||
|
||||
chmod -R 777 .
|
||||
|
||||
sudo -u nobody makepkg
|
||||
|
||||
# Remplace the old file
|
||||
rm imhex-${{env.version}}-ArchLinux.pkg.tar.zst
|
||||
mv *.pkg.tar.zst imhex-${{env.version}}-ArchLinux.pkg.tar.zst
|
||||
|
||||
- name: ⬆️ Upload imhex-archlinux.pkg.tar.zst
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ArchLinux .pkg.tar.zst
|
||||
path: |
|
||||
build/imhex-${{env.version}}-ArchLinux.pkg.tar.zst
|
||||
|
||||
|
||||
101
.github/workflows/release.yml
vendored
Normal file
101
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
name: Release
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: ImHex
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Verify version and set version variable
|
||||
run: |
|
||||
project_version=`cat ImHex/VERSION`
|
||||
tag_version="${{github.event.release.tag_name}}"
|
||||
tag_version="${tag_version:1}"
|
||||
if [ "$project_version" != "$tag_version" ]; then
|
||||
echo "::warning::$project_version and $tag_version are not the same ! Refusing to populate release"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "version=$project_version" >> $GITHUB_ENV
|
||||
|
||||
- name: 🗜️ Create tarball from sources with dependencies
|
||||
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
|
||||
|
||||
- name: ⬇️ Download artifacts from latest workflow
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
workflow: build.yml
|
||||
branch: ${{ github.event.release.target_commitish }}
|
||||
workflow_conclusion: success
|
||||
skip_unpack: true
|
||||
|
||||
- name: 🗜️ Unzip files when needed
|
||||
run: |
|
||||
for zipfile in ./*.zip
|
||||
do
|
||||
if [ `zipinfo -1 "$zipfile" | wc -l` -eq 1 ];
|
||||
then
|
||||
echo "unzipping $zipfile"
|
||||
unzip "$zipfile"
|
||||
rm "$zipfile"
|
||||
else
|
||||
echo "keeping $zipfile zipped"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: 🟩 Rename Windows Portable Zip
|
||||
run: mv "Windows Portable.zip" imhex-${{env.version}}-Windows-Portable.zip
|
||||
|
||||
- name: ⬆️ Upload everything to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: '*'
|
||||
|
||||
- name: ✒️ Prepare PKGBUILD
|
||||
run: |
|
||||
cp ImHex/dist/Arch/PKGBUILD .
|
||||
|
||||
hash=`md5sum imhex-${{env.version}}-ArchLinux.pkg.tar.zst | cut -d ' ' -f 1`
|
||||
|
||||
sed -i 's/%version%/${{env.version}}/g' PKGBUILD
|
||||
sed -i "s/(SKIP)/($hash)/g" PKGBUILD
|
||||
|
||||
- name: ⬆️ Publish AUR package
|
||||
|
||||
# I couldn't make the condition in the env directly for some reason
|
||||
env:
|
||||
MY_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
if: "${{ env.MY_KEY != '' }}"
|
||||
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v2
|
||||
with:
|
||||
pkgname: imhex-bin
|
||||
pkgbuild: ./PKGBUILD
|
||||
commit_username: iTrooz
|
||||
commit_email: itrooz@protonmail.com
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: Bump to version ${{env.version}}
|
||||
ssh_keyscan_types: rsa,dsa,ecdsa,ed25519
|
||||
|
||||
- uses: ncipollo/release-action@v1
|
||||
env:
|
||||
MY_KEY: ${{ secrets.RELEASE_TOKEN }}
|
||||
if: "${{ env.MY_KEY != '' }}"
|
||||
with:
|
||||
tag: ImHex-v${{env.version}}
|
||||
repo: PatternLanguage
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
26
.github/workflows/tests.yml
vendored
26
.github/workflows/tests.yml
vendored
@@ -5,11 +5,12 @@ on:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: 🧪 Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -17,19 +18,26 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Restore cache
|
||||
uses: actions/cache@v2
|
||||
- name: 📜 Restore ccache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.ccache
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
~/.cache/ccache
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build
|
||||
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: ${{ runner.os }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
@@ -39,7 +47,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-10 CXX=g++-10 cmake \
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -26,3 +26,6 @@
|
||||
path = lib/external/libromfs
|
||||
url = https://github.com/WerWolv/libromfs
|
||||
ignore = dirty
|
||||
[submodule "lib/external/pattern_language"]
|
||||
path = lib/external/pattern_language
|
||||
url = https://github.com/WerWolv/PatternLanguage
|
||||
|
||||
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@@ -7,6 +7,8 @@
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/fmt" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/libromfs" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/nativefiledialog" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/pattern_language" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/pattern_language/external/fmt" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/xdgpp" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/yara/yara" vcs="Git" />
|
||||
</component>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Updating the version here will update it throughout ImHex as well
|
||||
set(IMHEX_VERSION "1.16.0")
|
||||
file(READ "VERSION" IMHEX_VERSION)
|
||||
project(imhex VERSION ${IMHEX_VERSION})
|
||||
message("Project version ${IMHEX_VERSION}")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
|
||||
include("${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake")
|
||||
|
||||
option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF)
|
||||
|
||||
# Make sure project is configured correctly
|
||||
setDefaultBuiltTypeIfUnset()
|
||||
detectBadClone()
|
||||
@@ -36,6 +39,3 @@ add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
|
||||
# Configure packaging
|
||||
createPackage()
|
||||
|
||||
# Download and install all current files from the ImHex-Patterns repo
|
||||
downloadImHexPatternsFiles()
|
||||
113
README.md
113
README.md
@@ -33,7 +33,7 @@ If you like my work, please consider supporting me on GitHub Sponsors, Patreon o
|
||||
- Hex string
|
||||
- C, C++, C#, Rust, Python, Java & JavaScript array
|
||||
- ASCII-Art hex view
|
||||
- HTML self contained div
|
||||
- HTML self-contained div
|
||||
- String and hex search
|
||||
- Colorful highlighting
|
||||
- Goto from start, end and current cursor position
|
||||
@@ -84,7 +84,7 @@ If you like my work, please consider supporting me on GitHub Sponsors, Patreon o
|
||||
- Built-in Content Store
|
||||
- Download all files found in the database directly from within ImHex
|
||||
- Yara Rules support
|
||||
- Quickly scan a file for vulnearabilities with official yara rules
|
||||
- Quickly scan a file for vulnerabilities with official yara rules
|
||||
- Helpful tools
|
||||
- Itanium and MSVC demangler
|
||||
- ASCII table
|
||||
@@ -98,7 +98,7 @@ If you like my work, please consider supporting me on GitHub Sponsors, Patreon o
|
||||
- File utilities
|
||||
- File splitter
|
||||
- File combiner
|
||||
- File shredderer
|
||||
- File shredder
|
||||
- Built-in cheat sheet for pattern language and Math evaluator
|
||||
- Doesn't burn out your retinas when used in late-night sessions
|
||||
|
||||
@@ -106,123 +106,38 @@ If you like my work, please consider supporting me on GitHub Sponsors, Patreon o
|
||||
|
||||
The custom C-like Pattern Language developed and used by ImHex is easy to read, understand and learn. A guide with all features of the language can be found [on the docs page](http://imhex.werwolv.net/docs).
|
||||
|
||||
## Database
|
||||
|
||||
For format patterns, includable libraries magic and constant files, check out the [ImHex-Patterns](https://github.com/WerWolv/ImHex-Patterns) repository. Feel free to PR your own files there as well!
|
||||
|
||||
## Plugin development
|
||||
|
||||
To develop plugins for ImHex, use one of the following two templates projects to get startet. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content.
|
||||
To develop plugins for ImHex, use one of the following two templates projects to get started. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content.
|
||||
- [C++ Plugin Template](https://github.com/WerWolv/ImHex-Cpp-Plugin-Template)
|
||||
- [Rust Plugin Template](https://github.com/WerWolv/ImHex-Rust-Plugin-Template)
|
||||
|
||||
## Additional Files
|
||||
|
||||
For format patterns, includable libraries and magic files, check out the [ImHex-Patterns](https://github.com/WerWolv/ImHex-Patterns) repository. Feel free to PR your own files there as well!
|
||||
|
||||
## Nightly builds
|
||||
|
||||
Nightlies are available via GitHub Actions [here](https://github.com/WerWolv/ImHex/actions?query=workflow%3ABuild).
|
||||
|
||||
- Windows • __x86_64__
|
||||
- [MSI](https://nightly.link/WerWolv/ImHex/workflows/build/master/Windows%20Installer.zip)
|
||||
- [EXE](https://nightly.link/WerWolv/ImHex/workflows/build/master/Windows%20Portable%20ZIP.zip)
|
||||
- [Installer](https://nightly.link/WerWolv/ImHex/workflows/build/master/Windows%20Installer.zip)
|
||||
- [Portable](https://nightly.link/WerWolv/ImHex/workflows/build/master/Windows%20Portable%20ZIP.zip)
|
||||
- MacOS • __x86_64__
|
||||
- [DMG](https://nightly.link/WerWolv/ImHex/workflows/build/master/macOS%20DMG.zip)
|
||||
- Linux • __x86_64__
|
||||
- [ELF](https://nightly.link/WerWolv/ImHex/workflows/build/master/Linux%20ELF.zip)
|
||||
- [DEB](https://nightly.link/WerWolv/ImHex/workflows/build/master/Linux%20DEB.zip)
|
||||
- [Flatpak](https://nightly.link/WerWolv/ImHex/workflows/build/master/Linux%20Flatpak.zip)
|
||||
- [AppImage](https://nightly.link/WerWolv/ImHex/workflows/build/master/Linux%20AppImage.zip)
|
||||
- [Arch Package](https://nightly.link/WerWolv/ImHex/workflows/build/master/ArchLinux%20.pkg.tar.zst.zip)
|
||||
|
||||
## Compiling
|
||||
|
||||
You need a C++20 compatible compiler such as GCC 10.2.0 to compile ImHex.
|
||||
To compile ImHex, a C++20 compiler is required. Releases are all mainly built using GCC, however on macOS, clang is also required to compile some ObjC code.
|
||||
|
||||
Many dependencies are bundled into the repository using submodules so make sure to clone it using the `--recurse-submodules` option.
|
||||
All dependencies that aren't bundled, can be installed using the dependency installer scripts found in the `/dist` folder.
|
||||
|
||||
For working examples
|
||||
|
||||
### Windows
|
||||
|
||||
On Windows, ImHex is built through msys2 / mingw. To install all dependencies, open a mys2 window and run the PKGCONFIG script in the [dist/msys2](dist/msys2) folder.
|
||||
After all the dependencies are installed, run the following commands to build ImHex:
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
To create a standalone zipfile on Windows, get the Python standard library (e.g. from https://github.com/python/cpython/tree/master/Lib) and place the files and folders in `lib/python3.8` next to your built executable. Don't forget to also copy the `libpython3.8.dll` and `libwinpthread-1.dll` from your mingw setup next to the executable.
|
||||
|
||||
- Copy the files inside the `/resources/lib/python` folder into the `lib` folder next to your built executable.
|
||||
- Place your magic databases in the `magic` folder next to your built executable
|
||||
- Place your patterns in the `pattern` folder next to your built executable
|
||||
- Place your include pattern files in the `include` folder next to your built executable
|
||||
|
||||
### macOS
|
||||
|
||||
To build ImHex on macOS, run the following commands:
|
||||
|
||||
```sh
|
||||
brew bundle --no-lock --file dist/Brewfile
|
||||
mkdir build
|
||||
cd build
|
||||
CC=$(brew --prefix llvm)/bin/clang CXX=$(brew --prefix llvm)/bin/clang++ PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j
|
||||
```
|
||||
|
||||
Install the ImHex executable as well as libimhex.dylib to wherever ImHex should be installed.
|
||||
|
||||
All other files belong in `~/Library/Application Support/imhex`:
|
||||
```
|
||||
Patterns: ~/Library/Application Support/imhex/patterns
|
||||
Pattern Includes: ~/Library/Application Support/imhex/includes
|
||||
Magic files: ~/Library/Application Support/imhex/magic
|
||||
Python: ~/Library/Application Support/imhex/lib/pythonX.X
|
||||
Plugins: ~/Library/Application Support/imhex/plugins
|
||||
Configuration: ~/Library/Application Support/imhex/config
|
||||
Resources: ~/Library/Application Support/imhex/resources
|
||||
```
|
||||
|
||||
If the build fails while trying to find the macOS libraries, make sure you have
|
||||
XCode installed with `xcode-select --install`. Homebrew will also help get the
|
||||
most recent SDK installed and configured with `brew doctor`.
|
||||
|
||||
### Linux
|
||||
|
||||
Dependency installation scripts are available for many common Linux distributions in the [/dist](dist) folder.
|
||||
After all the dependencies are installed, run the following commands to build ImHex:
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Put the ImHex executable into the `/usr/bin` folder.
|
||||
Put libimhex.so into the `/usr/lib` folder.
|
||||
Configuration files go to `/etc/xdg/imhex` or `~/.config/imhex`.
|
||||
All other files belong in `/usr/share/imhex` or `~/.local/share/imhex`:
|
||||
|
||||
```
|
||||
Patterns: /usr/share/imhex/patterns
|
||||
Pattern Includes: /usr/share/imhex/includes
|
||||
Magic files: /usr/share/imhex/magic
|
||||
Python: /usr/share/imhex/lib/pythonX.X
|
||||
Plugins: /usr/share/imhex/plugins
|
||||
Configuration: /etc/xdg/imhex/config
|
||||
Resources: /usr/share/imhex/resources
|
||||
```
|
||||
|
||||
All paths follow the XDG Base Directories standard, and can thus be modified
|
||||
with the environment variables `XDG_CONFIG_HOME`, `XDG_CONFIG_DIRS`,
|
||||
`XDG_DATA_HOME` and `XDG_DATA_DIRS`.
|
||||
|
||||
## Credits
|
||||
|
||||
### Contributors
|
||||
@@ -230,7 +145,9 @@ with the environment variables `XDG_CONFIG_HOME`, `XDG_CONFIG_DIRS`,
|
||||
- [Mary](https://github.com/Thog) for her immense help porting ImHex to MacOS and help during development
|
||||
- [Roblabla](https://github.com/Roblabla) for adding MSI Installer support to ImHex
|
||||
- [jam1garner](https://github.com/jam1garner) and [raytwo](https://github.com/raytwo) for their help with adding Rust support to plugins
|
||||
- All other people that have been reporting issues on Discord or GitHub that I had great conversations with :)
|
||||
- [Mailaender](https://github.com/Mailaender) for getting ImHex onto Flathub
|
||||
- [iTrooz](https://github.com/iTrooz) for many improvements related to release packaging and the GitHub Action runners.
|
||||
- Everybody else who has reported issues on Discord or GitHub that I had great conversations with :)
|
||||
|
||||
### Libraries
|
||||
|
||||
|
||||
@@ -70,19 +70,18 @@ macro(detectOS)
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
set(CMAKE_INSTALL_LIBDIR ".")
|
||||
set(PLUGINS_INSTALL_LOCATION "plugins")
|
||||
set(MAGIC_INSTALL_LOCATION "magic")
|
||||
elseif (APPLE)
|
||||
add_compile_definitions(OS_MACOS)
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
set(CMAKE_INSTALL_LIBDIR ".")
|
||||
set(PLUGINS_INSTALL_LOCATION "plugins")
|
||||
set(MAGIC_INSTALL_LOCATION "magic")
|
||||
enable_language(OBJC)
|
||||
enable_language(OBJCXX)
|
||||
elseif (UNIX AND NOT APPLE)
|
||||
add_compile_definitions(OS_LINUX)
|
||||
set(CMAKE_INSTALL_BINDIR "bin")
|
||||
set(CMAKE_INSTALL_LIBDIR "lib")
|
||||
set(PLUGINS_INSTALL_LOCATION "share/imhex/plugins")
|
||||
set(MAGIC_INSTALL_LOCATION "share/imhex/magic")
|
||||
else ()
|
||||
message(FATAL_ERROR "Unknown / unsupported system!")
|
||||
endif()
|
||||
@@ -133,14 +132,14 @@ macro(configurePackingResources)
|
||||
set(MACOSX_BUNDLE_INFO_STRING "WerWolv")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "ImHex")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "WerWolv.ImHex")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.WerWolv.ImHex")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${GIT_COMMIT_HASH}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 WerWolv and Thog. All rights reserved." )
|
||||
if ("${CMAKE_GENERATOR}" STREQUAL "Xcode")
|
||||
set ( bundle_path "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/imhex.app" )
|
||||
set ( bundle_path "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/ImHex.app" )
|
||||
else ()
|
||||
set ( bundle_path "${CMAKE_BINARY_DIR}/imhex.app" )
|
||||
set ( bundle_path "${CMAKE_BINARY_DIR}/ImHex.app" )
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -151,11 +150,11 @@ macro(createPackage)
|
||||
foreach (plugin IN LISTS PLUGINS)
|
||||
add_subdirectory("plugins/${plugin}")
|
||||
if (TARGET ${plugin})
|
||||
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
set_target_properties(${plugin} PROPERTIES CARGO_BUILD_TARGET_DIR ${CMAKE_BINARY_DIR}/plugins)
|
||||
|
||||
get_target_property(IS_RUST_PROJECT ${plugin} RUST_PROJECT)
|
||||
|
||||
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
|
||||
if (IS_RUST_PROJECT)
|
||||
set_target_properties(${plugin} PROPERTIES CARGO_BUILD_TARGET_DIR ${CMAKE_BINARY_DIR}/plugins)
|
||||
|
||||
@@ -163,7 +162,6 @@ macro(createPackage)
|
||||
|
||||
install(FILES "${PLUGIN_LOCATION}/../${plugin}.hexplug" DESTINATION "${PLUGINS_INSTALL_LOCATION}")
|
||||
else ()
|
||||
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
if (WIN32)
|
||||
install(TARGETS ${plugin} RUNTIME DESTINATION ${PLUGINS_INSTALL_LOCATION})
|
||||
elseif (APPLE)
|
||||
@@ -181,7 +179,6 @@ macro(createPackage)
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
if (WIN32)
|
||||
@@ -218,12 +215,20 @@ macro(createPackage)
|
||||
)
|
||||
endforeach()
|
||||
]])
|
||||
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
downloadImHexPatternsFiles("./")
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/dist/DEBIAN/control.in ${CMAKE_BINARY_DIR}/DEBIAN/control)
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/dist/DEBIAN/control.in ${CMAKE_BINARY_DIR}/DEBIAN/control)
|
||||
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
downloadImHexPatternsFiles("./share/imhex")
|
||||
endif()
|
||||
|
||||
|
||||
if (CREATE_BUNDLE)
|
||||
include(PostprocessBundle)
|
||||
|
||||
@@ -234,20 +239,21 @@ macro(createPackage)
|
||||
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${bundle_path}/Contents/MacOS/plugins")
|
||||
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${bundle_path}/Contents/Resources")
|
||||
|
||||
downloadImHexPatternsFiles("${bundle_path}/Contents/MacOS")
|
||||
|
||||
install(FILES ${IMHEX_ICON} DESTINATION "${bundle_path}/Contents/Resources")
|
||||
install(TARGETS main BUNDLE DESTINATION ".")
|
||||
install(FILES $<TARGET_FILE:main> DESTINATION "${bundle_path}")
|
||||
|
||||
# Update library references to make the bundle portable
|
||||
postprocess_bundle(imhex_all main)
|
||||
|
||||
# Enforce DragNDrop packaging.
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
|
||||
install(TARGETS main BUNDLE DESTINATION .)
|
||||
else()
|
||||
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
|
||||
if (CREATE_PACKAGE)
|
||||
include(apple)
|
||||
include(CPack)
|
||||
@@ -283,18 +289,20 @@ macro(detectBadClone)
|
||||
endmacro()
|
||||
|
||||
|
||||
function(downloadImHexPatternsFiles)
|
||||
FetchContent_Declare(
|
||||
imhex_patterns
|
||||
GIT_REPOSITORY https://github.com/WerWolv/ImHex-Patterns.git
|
||||
GIT_TAG master
|
||||
)
|
||||
function(downloadImHexPatternsFiles dest)
|
||||
if (NOT IMHEX_OFFLINE_BUILD)
|
||||
FetchContent_Declare(
|
||||
imhex_patterns
|
||||
GIT_REPOSITORY https://github.com/WerWolv/ImHex-Patterns.git
|
||||
GIT_TAG master
|
||||
)
|
||||
|
||||
FetchContent_Populate(imhex_patterns)
|
||||
FetchContent_Populate(imhex_patterns)
|
||||
|
||||
set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns yara magic)
|
||||
foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})
|
||||
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION "./")
|
||||
endforeach()
|
||||
set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic)
|
||||
foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})
|
||||
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION ${dest})
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
endfunction()
|
||||
40
dist/AppImage/Dockerfile
vendored
40
dist/AppImage/Dockerfile
vendored
@@ -1,40 +0,0 @@
|
||||
FROM debian:bullseye-slim
|
||||
LABEL maintainer Example <example@example.com>
|
||||
|
||||
ARG TAG=master
|
||||
ARG REPO=https://github.com/WerWolv/ImHex.git
|
||||
|
||||
USER root
|
||||
|
||||
# Bring packages up to date
|
||||
RUN apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get install -y \
|
||||
git \
|
||||
cmake \
|
||||
curl \
|
||||
squashfs-tools
|
||||
|
||||
# Fetch source and dependencies
|
||||
RUN mkdir -p /source \
|
||||
&& cd /source \
|
||||
&& git clone $REPO \
|
||||
&& cd ImHex \
|
||||
&& git checkout $TAG \
|
||||
&& git submodule update --init --recursive \
|
||||
&& cd /source/ImHex/dist \
|
||||
&& ./get_deps_debian.sh
|
||||
|
||||
ARG CXX=g++-10
|
||||
|
||||
# Build ImHex
|
||||
RUN mkdir -p /source/ImHex/build \
|
||||
&& cd /source/ImHex/build \
|
||||
&& cmake --install-prefix /usr -DCMAKE_BUILD_TYPE=Release .. \
|
||||
&& make -j
|
||||
|
||||
# Prepare for AppImage
|
||||
RUN cd /source/ImHex/dist/AppImage \
|
||||
&& ./package.sh /source/ImHex/build \
|
||||
&& mv /source/ImHex/build/ImHex-x86_64.AppImage /
|
||||
6
dist/AppImage/ImHex.desktop
vendored
6
dist/AppImage/ImHex.desktop
vendored
@@ -1,6 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=ImHex
|
||||
Exec=imhex
|
||||
Icon=imhex
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
22
dist/AppImage/README.md
vendored
22
dist/AppImage/README.md
vendored
@@ -1,22 +0,0 @@
|
||||
# Building an AppImage
|
||||
There are two ways of building an AppImage for ImHex, using the provided
|
||||
tools here.
|
||||
|
||||
If you want to create an AppImage and do not have a build to work from
|
||||
already, you can use docker to build ImHex and package an AppImage.
|
||||
|
||||
Alternatively you can create an AppImage using an existing build.
|
||||
|
||||
## Using docker
|
||||
First run `build.sh` to create a docker image. Then run `extract.sh` to get the
|
||||
AppImage out. This needs to be in two steps, as a docker build cannot copy
|
||||
files out. Nor can docker build use volume mounts.
|
||||
|
||||
The environment variable TAG can be set to build for a specific git tag.
|
||||
Without the master branch is build.
|
||||
|
||||
## Using an existing build
|
||||
Run `package.sh` with the build dir as an argument. E.g.:
|
||||
```
|
||||
./package.sh ../../build
|
||||
```
|
||||
16
dist/AppImage/build.sh
vendored
16
dist/AppImage/build.sh
vendored
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set the TAG environment variable to build a specific tag
|
||||
# Set the REPO environment variable to point at a different git repository
|
||||
|
||||
# Make sure we're in the same directory as this script
|
||||
pushd $(dirname "$(realpath "$0")")
|
||||
|
||||
BUILDARG=""
|
||||
SUFFIX=""
|
||||
[ -n "${TAG}" ] && BUILDARG="${BUILDARG} --build-arg=TAG=${TAG}" && SUFFIX=":${TAG}"
|
||||
[ -n "${REPO}" ] && BUILDARG="${BUILDARG} --build-arg=REPO=${REPO}"
|
||||
|
||||
docker build ${BUILDARG} -t imhex-appimage-build${SUFFIX} .
|
||||
|
||||
popd
|
||||
26
dist/AppImage/extract.sh
vendored
26
dist/AppImage/extract.sh
vendored
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set the TAG environment variable to move to a versioned name while extracting
|
||||
|
||||
# Make sure we're in the same directory as this script
|
||||
pushd $(dirname "$(realpath "$0")")
|
||||
|
||||
SUFFIX=""
|
||||
[ -n "$TAG" ] && SUFFIX=":$TAG"
|
||||
|
||||
# Remove old containers
|
||||
docker rm imhex 2>&1 > /dev/null
|
||||
|
||||
docker run -d --name imhex imhex-appimage-build${SUFFIX} sleep 30 &
|
||||
sleep 15
|
||||
docker cp imhex:/ImHex-x86_64.AppImage .
|
||||
|
||||
# Move to tagged name if $TAG set
|
||||
if [ -n "$TAG" ]; then
|
||||
mv ImHex-x86_64.AppImage ImHex-${TAG}-x86_64.AppImage
|
||||
echo -e "\nThe created AppImage can be found here:\n $(pwd)/ImHex-${TAG}-x86_64.AppImage\n\n"
|
||||
else
|
||||
echo -e "\nThe created AppImage can be found here:\n $(pwd)/ImHex-x86_64.AppImage\n\n"
|
||||
fi
|
||||
|
||||
popd
|
||||
BIN
dist/AppImage/imhex.png
vendored
BIN
dist/AppImage/imhex.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
58
dist/AppImage/package.sh
vendored
58
dist/AppImage/package.sh
vendored
@@ -1,58 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e # Exit on error
|
||||
set -o pipefail # Bash specific
|
||||
|
||||
usage() {
|
||||
echo "Tool to package an ImHex build into an AppImage"
|
||||
echo
|
||||
echo "Usage:"
|
||||
echo "$0 <build dir>"
|
||||
echo
|
||||
exit
|
||||
}
|
||||
|
||||
MYDIR=$(dirname "$(realpath "$0")")
|
||||
|
||||
# Check is a build dir has been specified and it's a dir
|
||||
[ -z "$1" ] && usage
|
||||
[ -d "$1" ] || usage
|
||||
|
||||
set -u # Throw errors when unset variables are used
|
||||
|
||||
BUILDDIR=$1
|
||||
APPDIR=${BUILDDIR}/ImHex.AppDir
|
||||
APPIMAGE=${BUILDDIR}/ImHex-x86_64.AppImage
|
||||
|
||||
# Prepare for AppImage
|
||||
## Fetch the needed AppImage binaries
|
||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/AppRun-x86_64 -o ${MYDIR}/AppRun-x86_64
|
||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/runtime-x86_64 -o ${MYDIR}/runtime-x86_64
|
||||
|
||||
## Setup directory structure
|
||||
mkdir -p ${BUILDDIR}/ImHex.AppDir/usr/{bin,lib} ${BUILDDIR}/ImHex.AppDir/usr/share/imhex/plugins
|
||||
|
||||
## Add ImHex files to structure
|
||||
cp ${BUILDDIR}/imhex ${APPDIR}/usr/bin
|
||||
cp ${BUILDDIR}/plugins/builtin/builtin.hexplug ${APPDIR}/usr/share/imhex/plugins
|
||||
cp ${MYDIR}/{AppRun-x86_64,ImHex.desktop,imhex.png} ${APPDIR}/
|
||||
mv ${BUILDDIR}/ImHex.AppDir/AppRun-x86_64 ${APPDIR}/AppRun
|
||||
chmod a+x ${BUILDDIR}/ImHex.AppDir/AppRun
|
||||
|
||||
## Add all dependencies
|
||||
ldd ${BUILDDIR}/imhex | awk '/ => /{print $3}' | awk '!/(libc|libstdc++|libc++|libdl|libpthread|libselinux|ld-linux|libgdk)/' | xargs -I '{}' cp '{}' ${APPDIR}/usr/lib
|
||||
ldd ${BUILDDIR}/plugins/builtin/builtin.hexplug | awk '/ => /{print $3}' | awk '!/(libc|libstdc++|libc++|libdl|libpthread|libselinux|ld-linux|libgdk)/' | xargs -I '{}' cp '{}' ${APPDIR}/usr/lib
|
||||
ldd ${BUILDDIR}/lib/libimhex/libimhex.so | awk '/ => /{print $3}' | awk '!/(libc|libstdc++|libc++|libdl|libpthread|libselinux|ld-linux|libgdk)/' | xargs -I '{}' cp '{}' ${APPDIR}/usr/lib
|
||||
|
||||
# Package it up as described here:
|
||||
# https://github.com/AppImage/AppImageKit#appimagetool-usage
|
||||
# under 'If you want to generate an AppImage manually'
|
||||
# This builds a v2 AppImage according to
|
||||
# https://github.com/AppImage/AppImageSpec/blob/master/draft.md#type-2-image-format
|
||||
mksquashfs ${APPDIR} ${BUILDDIR}/ImHex.squashfs -root-owned -noappend
|
||||
cat ${MYDIR}/runtime-x86_64 > ${APPIMAGE}
|
||||
cat ${BUILDDIR}/ImHex.squashfs >> ${APPIMAGE}
|
||||
chmod a+x ${APPIMAGE}
|
||||
|
||||
if [ ! -f /.dockerenv ]; then
|
||||
echo -e "\nThe created AppImage can be found here:\n ${APPIMAGE}\n\n"
|
||||
fi
|
||||
139
dist/AppImageBuilder.yml
vendored
Normal file
139
dist/AppImageBuilder.yml
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
# appimage-builder recipe see https://appimage-builder.readthedocs.io for details
|
||||
version: 1
|
||||
AppDir:
|
||||
path: .AppDir
|
||||
app_info:
|
||||
id: imhex
|
||||
name: ImHex
|
||||
icon: imhex
|
||||
version: AppImage
|
||||
exec: usr/bin/imhex
|
||||
exec_args: $@
|
||||
apt:
|
||||
arch:
|
||||
- amd64
|
||||
allow_unauthenticated: true
|
||||
sources:
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ jammy main restricted
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ jammy-updates main restricted
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ jammy universe
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ jammy-updates universe
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ jammy multiverse
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ jammy-updates multiverse
|
||||
- sourceline: deb http://.archive.ubuntu.com/ubuntu/ jammy-backports main restricted
|
||||
universe multiverse
|
||||
- sourceline: deb http://security.ubuntu.com/ubuntu jammy-security main restricted
|
||||
- sourceline: deb http://security.ubuntu.com/ubuntu jammy-security universe
|
||||
- sourceline: deb http://security.ubuntu.com/ubuntu jammy-security multiverse
|
||||
include:
|
||||
- librsvg2-common
|
||||
- libbz2-1.0:amd64
|
||||
- libcap2:amd64
|
||||
- libdbus-1-3:amd64
|
||||
- libgpg-error0:amd64
|
||||
- liblzma5:amd64
|
||||
- libnss-mdns:amd64
|
||||
- libpcre3:amd64
|
||||
- libselinux1:amd64
|
||||
- libtinfo6:amd64
|
||||
files:
|
||||
include:
|
||||
- /lib/x86_64-linux-gnu/libGLX.so.0
|
||||
- /lib/x86_64-linux-gnu/libGLdispatch.so.0
|
||||
- /lib/x86_64-linux-gnu/libLLVM-13.so.1
|
||||
- /lib/x86_64-linux-gnu/libOpenGL.so.0
|
||||
- /lib/x86_64-linux-gnu/libX11.so.6
|
||||
- /lib/x86_64-linux-gnu/libXau.so.6
|
||||
- /lib/x86_64-linux-gnu/libXcomposite.so.1
|
||||
- /lib/x86_64-linux-gnu/libXcursor.so.1
|
||||
- /lib/x86_64-linux-gnu/libXdamage.so.1
|
||||
- /lib/x86_64-linux-gnu/libXdmcp.so.6
|
||||
- /lib/x86_64-linux-gnu/libXext.so.6
|
||||
- /lib/x86_64-linux-gnu/libXfixes.so.3
|
||||
- /lib/x86_64-linux-gnu/libXi.so.6
|
||||
- /lib/x86_64-linux-gnu/libXinerama.so.1
|
||||
- /lib/x86_64-linux-gnu/libXrandr.so.2
|
||||
- /lib/x86_64-linux-gnu/libXrender.so.1
|
||||
- /lib/x86_64-linux-gnu/libXxf86vm.so.1
|
||||
- /lib/x86_64-linux-gnu/libatk-1.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libatspi.so.0
|
||||
- /lib/x86_64-linux-gnu/libblkid.so.1
|
||||
- /lib/x86_64-linux-gnu/libbrotlicommon.so.1
|
||||
- /lib/x86_64-linux-gnu/libbrotlidec.so.1
|
||||
- /lib/x86_64-linux-gnu/libbsd.so.0
|
||||
- /lib/x86_64-linux-gnu/libcairo-gobject.so.2
|
||||
- /lib/x86_64-linux-gnu/libcairo.so.2
|
||||
- /lib/x86_64-linux-gnu/libdatrie.so.1
|
||||
- /lib/x86_64-linux-gnu/libedit.so.2
|
||||
- /lib/x86_64-linux-gnu/libelf.so.1
|
||||
- /lib/x86_64-linux-gnu/libepoxy.so.0
|
||||
- /lib/x86_64-linux-gnu/libffi.so.8
|
||||
- /lib/x86_64-linux-gnu/libfontconfig.so.1
|
||||
- /lib/x86_64-linux-gnu/libfreetype.so.6
|
||||
- /lib/x86_64-linux-gnu/libfribidi.so.0
|
||||
- /lib/x86_64-linux-gnu/libgcrypt.so.20
|
||||
- /lib/x86_64-linux-gnu/libgdk-3.so.0
|
||||
- /lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libgio-2.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libglapi.so.0
|
||||
- /lib/x86_64-linux-gnu/libglfw.so.3
|
||||
- /lib/x86_64-linux-gnu/libglib-2.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libgmodule-2.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libgmp.so.10
|
||||
- /lib/x86_64-linux-gnu/libgnutls.so.30
|
||||
- /lib/x86_64-linux-gnu/libgobject-2.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libgraphite2.so.3
|
||||
- /lib/x86_64-linux-gnu/libharfbuzz.so.0
|
||||
- /lib/x86_64-linux-gnu/libhogweed.so.6
|
||||
- /lib/x86_64-linux-gnu/libicudata.so.70
|
||||
- /lib/x86_64-linux-gnu/libicuuc.so.70
|
||||
- /lib/x86_64-linux-gnu/libidn2.so.0
|
||||
- /lib/x86_64-linux-gnu/libjpeg.so.8
|
||||
- /lib/x86_64-linux-gnu/liblber-2.5.so.0
|
||||
- /lib/x86_64-linux-gnu/libldap-2.5.so.0
|
||||
- /lib/x86_64-linux-gnu/liblz4.so.1
|
||||
- /lib/x86_64-linux-gnu/libmagic.so.1
|
||||
- /lib/x86_64-linux-gnu/libmbedcrypto.so.7
|
||||
- /lib/x86_64-linux-gnu/libmbedtls.so.14
|
||||
- /lib/x86_64-linux-gnu/libmbedx509.so.1
|
||||
- /lib/x86_64-linux-gnu/libmd.so.0
|
||||
- /lib/x86_64-linux-gnu/libmount.so.1
|
||||
- /lib/x86_64-linux-gnu/libnettle.so.8
|
||||
- /lib/x86_64-linux-gnu/libp11-kit.so.0
|
||||
- /lib/x86_64-linux-gnu/libpango-1.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libpangocairo-1.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libpangoft2-1.0.so.0
|
||||
- /lib/x86_64-linux-gnu/libpcre2-8.so.0
|
||||
- /lib/x86_64-linux-gnu/libpixman-1.so.0
|
||||
- /lib/x86_64-linux-gnu/libpng16.so.16
|
||||
- /lib/x86_64-linux-gnu/libpython3.10.so.1.0
|
||||
- /lib/x86_64-linux-gnu/libsasl2.so.2
|
||||
- /lib/x86_64-linux-gnu/libsensors.so.5
|
||||
- /lib/x86_64-linux-gnu/libstdc++.so.6
|
||||
- /lib/x86_64-linux-gnu/libsystemd.so.0
|
||||
- /lib/x86_64-linux-gnu/libtasn1.so.6
|
||||
- /lib/x86_64-linux-gnu/libthai.so.0
|
||||
- /lib/x86_64-linux-gnu/libunistring.so.2
|
||||
- /lib/x86_64-linux-gnu/libuuid.so.1
|
||||
- /lib/x86_64-linux-gnu/libvulkan.so.1
|
||||
- /lib/x86_64-linux-gnu/libwayland-client.so.0
|
||||
- /lib/x86_64-linux-gnu/libwayland-cursor.so.0
|
||||
- /lib/x86_64-linux-gnu/libwayland-egl.so.1
|
||||
- /lib/x86_64-linux-gnu/libxcb-dri2.so.0
|
||||
- /lib/x86_64-linux-gnu/libxcb-dri3.so.0
|
||||
- /lib/x86_64-linux-gnu/libxcb-present.so.0
|
||||
- /lib/x86_64-linux-gnu/libxcb-sync.so.1
|
||||
- /lib/x86_64-linux-gnu/libxkbcommon.so.0
|
||||
- /lib/x86_64-linux-gnu/libxml2.so.2
|
||||
- /lib/x86_64-linux-gnu/libxshmfence.so.1
|
||||
- /lib/x86_64-linux-gnu/libzstd.so.1
|
||||
exclude:
|
||||
- usr/share/man
|
||||
- usr/share/doc/*/README.*
|
||||
- usr/share/doc/*/changelog.*
|
||||
- usr/share/doc/*/NEWS.*
|
||||
- usr/share/doc/*/TODO.*
|
||||
AppImage:
|
||||
arch: x86_64
|
||||
update-information: guess
|
||||
39
dist/Arch/PKGBUILD
vendored
Normal file
39
dist/Arch/PKGBUILD
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Maintainer: iTrooz_ <itrooz at protonmail dot com>
|
||||
pkgname=imhex-bin
|
||||
pkgver=%version%
|
||||
pkgrel=1
|
||||
pkgdesc="A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM. "
|
||||
arch=("x86_64")
|
||||
url="https://github.com/WerWolv/ImHex"
|
||||
repo=$url
|
||||
license=('GPL 2.0')
|
||||
groups=()
|
||||
depends=(glfw mbedtls python freetype2 libglvnd dbus xdg-desktop-portal)
|
||||
makedepends=(git)
|
||||
checkdepends=()
|
||||
optdepends=()
|
||||
provides=(imhex)
|
||||
conflicts=(imhex)
|
||||
replaces=()
|
||||
backup=()
|
||||
options=()
|
||||
source=($repo"/releases/download/v$pkgver/imhex-$pkgver-ArchLinux.pkg.tar.zst")
|
||||
noextract=()
|
||||
md5sums=(SKIP)
|
||||
validpgpkeys=()
|
||||
|
||||
package() {
|
||||
tar -xf imhex-$pkgver-ArchLinux.pkg.tar.zst
|
||||
|
||||
install -DT $srcdir/usr/bin/imhex $pkgdir/usr/bin/imhex
|
||||
install -DT $srcdir/usr/lib/libimhex.so $pkgdir/usr/lib/libimhex.so
|
||||
|
||||
for plugin in $srcdir/usr/share/imhex/plugins/*.hexplug;
|
||||
do
|
||||
install -DT $plugin $pkgdir/usr/share/imhex/plugins/`basename $plugin`
|
||||
done
|
||||
|
||||
cp -r $srcdir/usr/share/imhex/{constants,encodings,includes,magic,patterns} $pkgdir/usr/share/imhex
|
||||
cp -r $srcdir/usr/share/{applications,licenses} $pkgdir/usr/share
|
||||
install -d $pkgdir/usr/share
|
||||
}
|
||||
3
dist/Brewfile
vendored
3
dist/Brewfile
vendored
@@ -7,6 +7,5 @@ brew "python3"
|
||||
brew "freetype2"
|
||||
brew "libmagic"
|
||||
brew "pkg-config"
|
||||
|
||||
# TODO: Remove this when XCode version of clang will support the same level as LLVM 10
|
||||
brew "llvm"
|
||||
brew "gcc@12"
|
||||
|
||||
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: libglfw3-dev, libmagic-dev, libmbedtls-dev, libcapstone-dev, python3-dev, libfreetype-dev, libgtk-3-dev, libldap2-dev
|
||||
Depends: libglfw3, libmagic1, libmbedtls14, libpython3.10, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal
|
||||
Maintainer: WerWolv <hey@werwolv.net>
|
||||
Description: ImHex Hex Editor
|
||||
A Hex Editor for Reverse Engineers, Programmers and
|
||||
|
||||
11
dist/DEBIAN/imhex.desktop
vendored
11
dist/DEBIAN/imhex.desktop
vendored
@@ -1,11 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=ImHex
|
||||
Comment=ImHex Hex Editor
|
||||
GenericName=Hex Editor
|
||||
Exec=/usr/bin/imhex %U
|
||||
Icon=/usr/share/pixmaps/imhex.png
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Development;
|
||||
StartupWMClass=imhex
|
||||
|
||||
3
dist/Dockerfile
vendored
3
dist/Dockerfile
vendored
@@ -15,7 +15,8 @@ RUN pacman -S --needed --noconfirm \
|
||||
mbedtls \
|
||||
python3 \
|
||||
freetype2 \
|
||||
gtk3
|
||||
dbus \
|
||||
xdg-desktop-portal
|
||||
|
||||
# Clone ImHex
|
||||
RUN git clone https://github.com/WerWolv/ImHex --recurse-submodules /root/ImHex
|
||||
|
||||
3
dist/ImHex-9999.ebuild
vendored
3
dist/ImHex-9999.ebuild
vendored
@@ -27,6 +27,7 @@ RDEPEND="${DEPEND}
|
||||
sys-apps/file
|
||||
dev-libs/mbedtls
|
||||
dev-cpp/nlohmann_json
|
||||
x11-libs/gtk+
|
||||
dbus
|
||||
xdg-desktop-portal
|
||||
"
|
||||
BDEPEND="${DEPEND}"
|
||||
|
||||
41
dist/compiling/linux.md
vendored
Normal file
41
dist/compiling/linux.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
### Compiling ImHex on Linux
|
||||
|
||||
Dependency installation scripts are available for many common Linux distributions in the [/dist](dist) folder.
|
||||
After all the dependencies are installed, run the following commands to build ImHex:
|
||||
|
||||
```sh
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DRUST_PATH="$HOME/.cargo/bin/" \
|
||||
..
|
||||
make -j 4 install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Put the ImHex executable into the `/usr/bin` folder.
|
||||
Put libimhex.so into the `/usr/lib` folder.
|
||||
Configuration files go to `/usr/etc/imhex` or `~/.config/imhex`.
|
||||
All other files belong in `/usr/share/imhex` or `~/.local/share/imhex`:
|
||||
|
||||
```
|
||||
Patterns: /usr/share/imhex/patterns
|
||||
Pattern Includes: /usr/share/imhex/includes
|
||||
Magic files: /usr/share/imhex/magic
|
||||
Python: /usr/share/imhex/lib/pythonX.X
|
||||
Plugins: /usr/share/imhex/plugins
|
||||
Configuration: /etc/xdg/imhex/config
|
||||
```
|
||||
|
||||
All paths follow the XDG Base Directories standard, and can thus be modified
|
||||
with the environment variables `XDG_CONFIG_HOME`, `XDG_CONFIG_DIRS`,
|
||||
`XDG_DATA_HOME` and `XDG_DATA_DIRS`.
|
||||
41
dist/compiling/macOS.md
vendored
Normal file
41
dist/compiling/macOS.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
### Compiling ImHex on macOS
|
||||
|
||||
To build ImHex on macOS, run the following commands:
|
||||
|
||||
```sh
|
||||
brew bundle --no-lock --file dist/Brewfile
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=$(brew --prefix gcc@12)/bin/gcc-12 \
|
||||
CXX=$(brew --prefix gcc@12)/bin/g++-12 \
|
||||
OBJC=$(brew --prefix llvm)/bin/clang \
|
||||
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \
|
||||
MACOSX_DEPLOYMENT_TARGET="10.15" \
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCREATE_BUNDLE=ON \
|
||||
-DCREATE_PACKAGE=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
..
|
||||
make -j4 package
|
||||
```
|
||||
|
||||
Open the generated .dmg file and drag-n-drop the ImHex executable to the Applications folder
|
||||
|
||||
All other files belong in `~/Library/Application Support/imhex`:
|
||||
```
|
||||
Patterns: ~/Library/Application Support/imhex/patterns
|
||||
Pattern Includes: ~/Library/Application Support/imhex/includes
|
||||
Magic files: ~/Library/Application Support/imhex/magic
|
||||
Python: ~/Library/Application Support/imhex/lib/pythonX.X
|
||||
Plugins: ~/Library/Application Support/imhex/plugins
|
||||
Configuration: ~/Library/Application Support/imhex/config
|
||||
```
|
||||
|
||||
If the build fails while trying to find the macOS libraries, make sure you have
|
||||
XCode installed with `xcode-select --install`. Homebrew will also help get the
|
||||
most recent SDK installed and configured with `brew doctor`.
|
||||
23
dist/compiling/windows.md
vendored
Normal file
23
dist/compiling/windows.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
### Compiling ImHex on Windows
|
||||
|
||||
On Windows, ImHex is built through msys2 / mingw. To install all dependencies, open a mys2 window and run the PKGCONFIG script in the [dist/msys2](dist/msys2) folder.
|
||||
After all the dependencies are installed, run the following commands to build ImHex:
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "MinGW Makefiles" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DRUST_PATH="$USERPROFILE/.cargo/bin/" \
|
||||
..
|
||||
mingw32-make -j install
|
||||
```
|
||||
|
||||
ImHex will look for any extra resources either in various folders directly next to the executable or in `%localappdata%/imhex`
|
||||
5
dist/get_deps_archlinux.sh
vendored
5
dist/get_deps_archlinux.sh
vendored
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
pacman -S --needed \
|
||||
pacman -S $@ --needed \
|
||||
cmake \
|
||||
gcc \
|
||||
lld \
|
||||
@@ -9,4 +9,5 @@ pacman -S --needed \
|
||||
mbedtls \
|
||||
python3 \
|
||||
freetype2 \
|
||||
gtk3
|
||||
dbus \
|
||||
xdg-desktop-portal
|
||||
|
||||
14
dist/get_deps_debian.sh
vendored
14
dist/get_deps_debian.sh
vendored
@@ -1,9 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
echo "As of 2020-12, Debian stable does not include g++-10, needs debian testing or unstable."
|
||||
|
||||
# Tested on 2020-12-09 with Docker image bitnami/minideb:unstable
|
||||
|
||||
# Install pkgconf (adds minimum dependencies) only if the equivalent pkf-config is not already installed.
|
||||
if ! which pkg-config
|
||||
then
|
||||
@@ -12,8 +8,8 @@ fi
|
||||
|
||||
apt install -y \
|
||||
build-essential \
|
||||
gcc-10 \
|
||||
g++-10 \
|
||||
gcc-12 \
|
||||
g++-12 \
|
||||
lld \
|
||||
${PKGCONF:-} \
|
||||
cmake \
|
||||
@@ -25,7 +21,5 @@ apt install -y \
|
||||
libmbedtls-dev \
|
||||
python3-dev \
|
||||
libfreetype-dev \
|
||||
libgtk-3-dev \
|
||||
|
||||
echo "Please consider this before running cmake (useful on e.g. Ubuntu 20.04):"
|
||||
echo "export CXX=g++-10"
|
||||
libdbus-1-dev \
|
||||
xdg-desktop-portal
|
||||
|
||||
3
dist/get_deps_fedora.sh
vendored
3
dist/get_deps_fedora.sh
vendored
@@ -10,4 +10,5 @@ dnf install \
|
||||
mbedtls-devel \
|
||||
python-devel \
|
||||
freetype-devel \
|
||||
gtk3-devel
|
||||
dbus \
|
||||
xdg-desktop-portal
|
||||
|
||||
7
dist/imhex.desktop
vendored
7
dist/imhex.desktop
vendored
@@ -2,10 +2,9 @@
|
||||
Name=ImHex
|
||||
Comment=ImHex Hex Editor
|
||||
GenericName=Hex Editor
|
||||
Exec=/usr/bin/imhex %U
|
||||
Icon=/usr/share/pixmaps/imhex.png
|
||||
Exec=imhex %U
|
||||
Icon=imhex
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Development;
|
||||
Categories=Development;IDE;
|
||||
StartupWMClass=imhex
|
||||
|
||||
|
||||
2
lib/external/capstone
vendored
2
lib/external/capstone
vendored
Submodule lib/external/capstone updated: c7c665f8f3...d5141c0478
2
lib/external/curl
vendored
2
lib/external/curl
vendored
Submodule lib/external/curl updated: 801bd5138c...45ac4d0194
2
lib/external/fmt
vendored
2
lib/external/fmt
vendored
Submodule lib/external/fmt updated: d141cdbeb0...c4ee726532
805
lib/external/imgui/include/imgui_memory_editor.h
vendored
805
lib/external/imgui/include/imgui_memory_editor.h
vendored
@@ -1,805 +0,0 @@
|
||||
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
||||
// Get latest version at http://www.github.com/ocornut/imgui_club
|
||||
//
|
||||
// Right-click anywhere to access the Options menu!
|
||||
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
||||
// The code assume a mono-space font for simplicity!
|
||||
// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before caling this.
|
||||
//
|
||||
// Usage:
|
||||
// // Create a window and draw memory editor inside it:
|
||||
// static MemoryEditor mem_edit_1;
|
||||
// static char data[0x10000];
|
||||
// size_t data_size = 0x10000;
|
||||
// mem_edit_1.DrawWindow("Memory Editor", data, data_size);
|
||||
//
|
||||
// Usage:
|
||||
// // If you already have a window, use DrawContents() instead:
|
||||
// static MemoryEditor mem_edit_2;
|
||||
// ImGui::Begin("MyWindow")
|
||||
// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
|
||||
// ImGui::End();
|
||||
//
|
||||
// Changelog:
|
||||
// - v0.10: initial version
|
||||
// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write.
|
||||
// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61).
|
||||
// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns".
|
||||
// - v0.26 (2018/08/02): fixed clicking on hex region
|
||||
// - v0.30 (2018/08/02): added data preview for common data types
|
||||
// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar]
|
||||
// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char*
|
||||
// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting.
|
||||
// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble]
|
||||
// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69.
|
||||
// - v0.36 (2020/05/05): minor tweaks, minor refactor.
|
||||
// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions.
|
||||
// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled.
|
||||
// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out.
|
||||
//
|
||||
// Todo/Bugs:
|
||||
// - This is generally old code, it should work but please don't use this as reference!
|
||||
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
|
||||
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h> // sprintf, scanf
|
||||
#include <stdint.h> // uint8_t, etc.
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _PRISizeT "I"
|
||||
#define ImSnprintf _snprintf
|
||||
#else
|
||||
#define _PRISizeT "z"
|
||||
#define ImSnprintf snprintf
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||
#endif
|
||||
|
||||
ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);
|
||||
|
||||
struct MemoryEditor
|
||||
{
|
||||
enum DataFormat
|
||||
{
|
||||
DataFormat_Bin = 0,
|
||||
DataFormat_Dec = 1,
|
||||
DataFormat_Hex = 2,
|
||||
DataFormat_COUNT
|
||||
};
|
||||
|
||||
struct DecodeData {
|
||||
std::string data;
|
||||
size_t advance;
|
||||
ImColor color;
|
||||
};
|
||||
|
||||
// Settings
|
||||
bool ReadOnly; // = false // disable any editing.
|
||||
int Cols; // = 16 // number of columns to display.
|
||||
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
|
||||
bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
|
||||
bool OptShowAscii; // = true // display ASCII representation on the right side.
|
||||
bool OptShowAdvancedDecoding; // = true // display advanced decoding data on the right side.
|
||||
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
|
||||
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
|
||||
bool OptShowExtraInfo; // = true // display extra information about size of data and current selection
|
||||
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
|
||||
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
||||
ImU32 HighlightColor; // // background color of highlighted bytes.
|
||||
ImU8 (*ReadFn)(const ImU8* data, size_t off); // = 0 // optional handler to read bytes.
|
||||
void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes.
|
||||
bool (*HighlightFn)(const ImU8* data, size_t off, bool next);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||
void (*HoverFn)(const ImU8 *data, size_t off);
|
||||
DecodeData (*DecodeFn)(const ImU8 *data, size_t off);
|
||||
|
||||
// [Internal State]
|
||||
bool ContentsWidthChanged;
|
||||
size_t DataPreviewAddr;
|
||||
size_t DataPreviewAddrOld;
|
||||
size_t DataPreviewAddrEnd;
|
||||
size_t DataPreviewAddrEndOld;
|
||||
size_t DataEditingAddr;
|
||||
bool DataEditingTakeFocus;
|
||||
char DataInputBuf[32];
|
||||
char AddrInputBuf[32];
|
||||
size_t GotoAddr;
|
||||
size_t HighlightMin, HighlightMax;
|
||||
int PreviewEndianess;
|
||||
ImGuiDataType PreviewDataType;
|
||||
|
||||
MemoryEditor()
|
||||
{
|
||||
// Settings
|
||||
ReadOnly = false;
|
||||
Cols = 16;
|
||||
OptShowOptions = true;
|
||||
OptShowHexII = false;
|
||||
OptShowAscii = true;
|
||||
OptShowAdvancedDecoding = true;
|
||||
OptGreyOutZeroes = true;
|
||||
OptUpperCaseHex = true;
|
||||
OptMidColsCount = 8;
|
||||
OptAddrDigitsCount = 0;
|
||||
HighlightColor = IM_COL32(255, 255, 255, 50);
|
||||
ReadFn = NULL;
|
||||
WriteFn = NULL;
|
||||
HighlightFn = NULL;
|
||||
HoverFn = NULL;
|
||||
DecodeFn = NULL;
|
||||
|
||||
// State/Internals
|
||||
ContentsWidthChanged = false;
|
||||
DataPreviewAddr = DataEditingAddr = DataPreviewAddrEnd = (size_t)-1;
|
||||
DataPreviewAddrOld = DataPreviewAddrEndOld = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
||||
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
||||
GotoAddr = (size_t)-1;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
PreviewEndianess = 0;
|
||||
PreviewDataType = ImGuiDataType_S32;
|
||||
}
|
||||
|
||||
void GotoAddrAndHighlight(size_t addr_min, size_t addr_max)
|
||||
{
|
||||
GotoAddr = addr_min;
|
||||
HighlightMin = addr_min;
|
||||
HighlightMax = addr_max;
|
||||
}
|
||||
|
||||
void GotoAddrAndSelect(size_t addr_min, size_t addr_max)
|
||||
{
|
||||
GotoAddr = addr_min;
|
||||
DataPreviewAddr = addr_min;
|
||||
DataPreviewAddrEnd = addr_max;
|
||||
DataPreviewAddrOld = addr_min;
|
||||
DataPreviewAddrEndOld = addr_max;
|
||||
}
|
||||
|
||||
struct Sizes
|
||||
{
|
||||
int AddrDigitsCount;
|
||||
float LineHeight;
|
||||
float GlyphWidth;
|
||||
float HexCellWidth;
|
||||
float SpacingBetweenMidCols;
|
||||
float PosHexStart;
|
||||
float PosHexEnd;
|
||||
float PosAsciiStart;
|
||||
float PosAsciiEnd;
|
||||
float PosDecodingStart;
|
||||
float PosDecodingEnd;
|
||||
float WindowWidth;
|
||||
|
||||
Sizes() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
s.AddrDigitsCount = OptAddrDigitsCount;
|
||||
if (s.AddrDigitsCount == 0)
|
||||
for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4)
|
||||
s.AddrDigitsCount++;
|
||||
s.LineHeight = ImGui::GetTextLineHeight();
|
||||
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
|
||||
s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere
|
||||
s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
|
||||
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
||||
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
||||
|
||||
if (OptShowAscii && OptShowAdvancedDecoding) {
|
||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||
if (OptMidColsCount > 0)
|
||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
||||
|
||||
s.PosDecodingStart = s.PosAsciiEnd + s.GlyphWidth * 1;
|
||||
if (OptMidColsCount > 0)
|
||||
s.PosDecodingStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.PosDecodingEnd = s.PosDecodingStart + Cols * s.GlyphWidth;
|
||||
} else if (OptShowAscii) {
|
||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||
if (OptMidColsCount > 0)
|
||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
||||
} else if (OptShowAdvancedDecoding) {
|
||||
s.PosDecodingStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||
if (OptMidColsCount > 0)
|
||||
s.PosDecodingStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.PosDecodingEnd = s.PosDecodingStart + Cols * s.GlyphWidth;
|
||||
}
|
||||
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||
}
|
||||
|
||||
// Standalone Memory Editor window
|
||||
void DrawWindow(const char* title, bool *p_open, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
|
||||
{
|
||||
Sizes s;
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
|
||||
if (ImGui::Begin(title, p_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavInputs))
|
||||
{
|
||||
if (DataPreviewAddr != DataPreviewAddrOld || DataPreviewAddrEnd != DataPreviewAddrEndOld) {
|
||||
hex::Region selectionRegion = { std::min(DataPreviewAddr, DataPreviewAddrEnd) + base_display_addr, (std::max(DataPreviewAddr, DataPreviewAddrEnd) - std::min(DataPreviewAddr, DataPreviewAddrEnd)) + 1 };
|
||||
hex::EventManager::post<hex::EventRegionSelected>(selectionRegion);
|
||||
}
|
||||
|
||||
DataPreviewAddrOld = DataPreviewAddr;
|
||||
DataPreviewAddrEndOld = DataPreviewAddrEnd;
|
||||
|
||||
if (mem_size > 0)
|
||||
DrawContents(mem_data, mem_size, base_display_addr);
|
||||
|
||||
if (ContentsWidthChanged)
|
||||
{
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y));
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
}
|
||||
|
||||
// Memory Editor contents only
|
||||
void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000)
|
||||
{
|
||||
if (Cols < 1)
|
||||
Cols = 1;
|
||||
|
||||
ImU8* mem_data = (ImU8*)mem_data_void;
|
||||
Sizes s;
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
||||
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
||||
const float height_separator = style.ItemSpacing.y;
|
||||
float footer_height = 0;
|
||||
if (OptShowOptions)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 2;
|
||||
|
||||
ImGui::BeginChild("offset", ImVec2(0, s.LineHeight), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImGui::Text("%*c ", s.AddrDigitsCount, ' ');
|
||||
for (int i = 0; i < Cols; i++) {
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * i;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(i / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
ImGui::Text("%02llX", i + (base_display_addr % Cols));
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
|
||||
|
||||
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
|
||||
ImGuiListClipper clipper;
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||
clipper.Begin(line_total_count, s.LineHeight);
|
||||
const size_t visible_start_addr = clipper.DisplayStart * Cols;
|
||||
const size_t visible_end_addr = clipper.DisplayEnd * Cols;
|
||||
const size_t visible_count = visible_end_addr - visible_start_addr;
|
||||
|
||||
bool data_next = false;
|
||||
|
||||
if (DataEditingAddr >= mem_size)
|
||||
DataEditingAddr = (size_t)-1;
|
||||
if (DataPreviewAddr >= mem_size)
|
||||
DataPreviewAddr = (size_t)-1;
|
||||
if (DataPreviewAddrEnd >= mem_size)
|
||||
DataPreviewAddrEnd = (size_t)-1;
|
||||
|
||||
size_t data_editing_addr_backup = DataEditingAddr;
|
||||
size_t data_preview_addr_backup = DataPreviewAddr;
|
||||
size_t data_editing_addr_next = (size_t)-1;
|
||||
size_t data_preview_addr_next = (size_t)-1;
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
||||
if (DataEditingAddr != (size_t)-1) {
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataEditingAddr >= (size_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataEditingAddr < mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataEditingAddr > 0) { data_editing_addr_next = DataEditingAddr - 1; DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)) && DataEditingAddr > 0) { data_editing_addr_next = std::max<i64>(0, DataEditingAddr - visible_count); DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = std::min<i64>(mem_size - 1, DataEditingAddr + visible_count); DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)) && DataEditingAddr > 0) { data_editing_addr_next = 0; DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = mem_size - 1; DataEditingTakeFocus = true; }
|
||||
} else if (DataPreviewAddr != -1) {
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataPreviewAddr >= (size_t)Cols) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr - Cols; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataPreviewAddr < mem_size - Cols) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr + Cols; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataPreviewAddr > 0) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr - 1; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataPreviewAddr < mem_size - 1) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr + 1; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)) && DataPreviewAddr > 0) { DataPreviewAddr = data_preview_addr_next = std::max<i64>(0, DataPreviewAddr - visible_count); if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)) && DataPreviewAddr < mem_size - 1) { DataPreviewAddr = data_preview_addr_next = std::min<i64>(mem_size - 1, DataPreviewAddr + visible_count); if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)) && DataPreviewAddr > 0) { DataPreviewAddr = data_preview_addr_next = 0; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)) && DataPreviewAddr < mem_size - 1) { DataPreviewAddr = data_preview_addr_next = mem_size - 1; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
}
|
||||
} else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) {
|
||||
DataPreviewAddr = DataPreviewAddrOld = DataPreviewAddrEnd = DataPreviewAddrEndOld = data_preview_addr_next = (size_t)-1;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
|
||||
hex::EventManager::post<hex::EventRegionSelected>(hex::Region{ (size_t)-1, 0 });
|
||||
}
|
||||
|
||||
if (data_preview_addr_next != (size_t)-1 && (data_preview_addr_next / Cols) != (data_preview_addr_backup / Cols))
|
||||
{
|
||||
// Track cursor movements
|
||||
const int scroll_offset = ((int)(data_preview_addr_next / Cols) - (int)(data_preview_addr_backup / Cols));
|
||||
const bool scroll_desired = (scroll_offset < 0 && data_preview_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_preview_addr_next > visible_end_addr - Cols * 2);
|
||||
if (scroll_desired)
|
||||
ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight);
|
||||
}
|
||||
if (data_editing_addr_next != (size_t)-1 && (data_editing_addr_next / Cols) != (data_editing_addr_backup / Cols))
|
||||
{
|
||||
// Track cursor movements
|
||||
const int scroll_offset = ((int)(data_editing_addr_next / Cols) - (int)(data_editing_addr_backup / Cols));
|
||||
const bool scroll_desired = (scroll_offset < 0 && data_editing_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_editing_addr_next > visible_end_addr - Cols * 2);
|
||||
if (scroll_desired)
|
||||
ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight);
|
||||
}
|
||||
|
||||
// Draw vertical separator
|
||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||
float scrollX = ImGui::GetScrollX();
|
||||
|
||||
if (OptShowAscii)
|
||||
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth - scrollX, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth - scrollX, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||
if (OptShowAdvancedDecoding)
|
||||
draw_list->AddLine(ImVec2(window_pos.x + s.PosDecodingStart - s.GlyphWidth - scrollX, window_pos.y), ImVec2(window_pos.x + s.PosDecodingStart - s.GlyphWidth - scrollX, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||
|
||||
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
||||
|
||||
const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: ";
|
||||
const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x";
|
||||
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||
|
||||
bool tooltipShown = false;
|
||||
while (clipper.Step())
|
||||
{
|
||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
||||
{
|
||||
size_t addr = (size_t)(line_i * Cols);
|
||||
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||
|
||||
// Draw Hexadecimal
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos() - ImVec2(ImGui::GetStyle().CellPadding.x / 2, 0);
|
||||
float highlight_width = s.GlyphWidth * 2 + ImGui::GetStyle().CellPadding.x / 2;
|
||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) &&
|
||||
((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) ||
|
||||
(HighlightFn && HighlightFn(mem_data, addr + 1, true)) ||
|
||||
((addr + 1) >= DataPreviewAddr && (addr + 1) <= DataPreviewAddrEnd) || ((addr + 1) >= DataPreviewAddrEnd && (addr + 1) <= DataPreviewAddr));
|
||||
if (is_next_byte_highlighted)
|
||||
{
|
||||
highlight_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||
highlight_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), color);
|
||||
|
||||
if (is_highlight_from_preview) {
|
||||
size_t min = std::min(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
size_t max = std::max(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
|
||||
// Draw vertical line at the left of first byte and the start of the line
|
||||
if (n == 0 || addr == min)
|
||||
draw_list->AddLine(pos, pos + ImVec2(0, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
|
||||
// Draw vertical line at the right of the last byte and the end of the line
|
||||
if (n == Cols - 1 || addr == max) {
|
||||
draw_list->AddRectFilled(pos + ImVec2(highlight_width, 0), pos + ImVec2(highlight_width + 1, s.LineHeight), color);
|
||||
draw_list->AddLine(pos + ImVec2(highlight_width + 1, -1), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
}
|
||||
|
||||
// Draw horizontal line at the top of the bytes
|
||||
if ((addr - Cols) < min)
|
||||
draw_list->AddLine(pos, pos + ImVec2(highlight_width + 1, 0), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
|
||||
// Draw horizontal line at the bottom of the bytes
|
||||
if ((addr + Cols) == (max + 1) && OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 1)
|
||||
draw_list->AddLine(pos + ImVec2(-s.SpacingBetweenMidCols, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
else if ((addr + Cols) > max)
|
||||
draw_list->AddLine(pos + ImVec2(0, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr)
|
||||
{
|
||||
// Display text input on current byte
|
||||
bool data_write = false;
|
||||
ImGui::PushID((void*)addr);
|
||||
if (DataEditingTakeFocus)
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
ImGui::CaptureKeyboardFromApp(true);
|
||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
}
|
||||
ImGui::PushItemWidth(s.GlyphWidth * 2);
|
||||
struct UserData
|
||||
{
|
||||
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
||||
static int Callback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
UserData* user_data = (UserData*)data->UserData;
|
||||
if (!data->HasSelection())
|
||||
user_data->CursorPos = data->CursorPos;
|
||||
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||
{
|
||||
// When not editing a byte, always rewrite its content (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
|
||||
data->DeleteChars(0, data->BufTextLen);
|
||||
data->InsertChars(0, user_data->CurrentBufOverwrite);
|
||||
data->SelectionStart = 0;
|
||||
data->SelectionEnd = 2;
|
||||
data->CursorPos = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char CurrentBufOverwrite[3]; // Input
|
||||
int CursorPos; // Output
|
||||
};
|
||||
UserData user_data;
|
||||
user_data.CursorPos = -1;
|
||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_AlwaysInsertMode | ImGuiInputTextFlags_CallbackAlways;
|
||||
if (ImGui::InputText("##data", DataInputBuf, 32, flags, UserData::Callback, &user_data))
|
||||
data_write = data_next = true;
|
||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
ImGui::PopItemWidth();
|
||||
if (user_data.CursorPos >= 2)
|
||||
data_write = data_next = true;
|
||||
if (data_editing_addr_next != (size_t)-1)
|
||||
data_write = data_next = false;
|
||||
unsigned int data_input_value = 0;
|
||||
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||
{
|
||||
if (WriteFn)
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
||||
else
|
||||
mem_data[addr] = (ImU8)data_input_value;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
else
|
||||
{
|
||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
|
||||
if (OptShowHexII)
|
||||
{
|
||||
if ((b >= 32 && b < 128))
|
||||
ImGui::Text(".%c ", b);
|
||||
else if (b == 0xFF && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("## ");
|
||||
else if (b == 0x00)
|
||||
ImGui::Text(" ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b == 0 && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("00 ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && !tooltipShown) {
|
||||
if (HoverFn) {
|
||||
HoverFn(mem_data, addr);
|
||||
tooltipShown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OptShowAscii)
|
||||
{
|
||||
// Draw ASCII values
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
if (addr == DataEditingAddr)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
|
||||
pos.x += s.GlyphWidth;
|
||||
}
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (OptShowAdvancedDecoding && DecodeFn) {
|
||||
// Draw decoded bytes
|
||||
ImGui::SameLine(s.PosDecodingStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
for (int n = 0; n < Cols && addr < mem_size;)
|
||||
{
|
||||
auto decodedData = DecodeFn(mem_data, addr);
|
||||
|
||||
auto displayData = decodedData.data;
|
||||
auto glyphWidth = ImGui::CalcTextSize(displayData.c_str()).x + 1;
|
||||
|
||||
if (addr == DataEditingAddr)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
|
||||
draw_list->AddText(pos, decodedData.color, displayData.c_str(), displayData.c_str() + displayData.length());
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(glyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
|
||||
pos.x += glyphWidth;
|
||||
|
||||
if (addr <= 1) {
|
||||
n++;
|
||||
addr++;
|
||||
} else {
|
||||
n += decodedData.advance;
|
||||
addr += decodedData.advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::EndChild();
|
||||
|
||||
if (data_next && DataEditingAddr < mem_size)
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = DataEditingAddr + 1;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
else if (data_editing_addr_next != (size_t)-1)
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = data_editing_addr_next;
|
||||
}
|
||||
|
||||
if (OptShowOptions)
|
||||
{
|
||||
ImGui::Separator();
|
||||
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
|
||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
||||
ImGui::SetCursorPosX(s.WindowWidth);
|
||||
}
|
||||
|
||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
IM_UNUSED(mem_data);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const char* format_range = OptUpperCaseHex ? "Range 0x%0*" _PRISizeT "X..0x%0*" _PRISizeT "X" : "Range 0x%0*" _PRISizeT "x..0x%0*" _PRISizeT "x";
|
||||
const char* format_selection = OptUpperCaseHex ? "Selection 0x%0*" _PRISizeT "X..0x%0*" _PRISizeT "X (%ld [0x%lX] %s)" : "Range 0x%0*" _PRISizeT "x..0x%0*" _PRISizeT "x (%ld [0x%lX] %s)";
|
||||
|
||||
if (this->OptShowExtraInfo) {
|
||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
||||
if (DataPreviewAddr != (size_t)-1 && DataPreviewAddrEnd != (size_t)-1) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Spacing();
|
||||
ImGui::SameLine();
|
||||
|
||||
auto selectionStart = std::min(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
auto selectionEnd = std::max(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
|
||||
size_t regionSize = (selectionEnd - selectionStart) + 1;
|
||||
ImGui::Text(format_selection, s.AddrDigitsCount, base_display_addr + selectionStart, s.AddrDigitsCount, base_display_addr + selectionEnd, regionSize, regionSize, regionSize == 1 ? "byte" : "bytes");
|
||||
}
|
||||
}
|
||||
|
||||
if (GotoAddr != (size_t)-1)
|
||||
{
|
||||
if (GotoAddr < mem_size)
|
||||
{
|
||||
ImGui::BeginChild("##scrolling");
|
||||
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());
|
||||
ImGui::EndChild();
|
||||
}
|
||||
GotoAddr = (size_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsBigEndian()
|
||||
{
|
||||
uint16_t x = 1;
|
||||
char c[2];
|
||||
memcpy(c, &x, 2);
|
||||
return c[0] != 0;
|
||||
}
|
||||
|
||||
static void* EndianessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
{
|
||||
if (is_little_endian)
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
}
|
||||
else
|
||||
{
|
||||
return memcpy(_dst, _src, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void* EndianessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
{
|
||||
if (is_little_endian)
|
||||
{
|
||||
return memcpy(_dst, _src, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
}
|
||||
}
|
||||
|
||||
void* EndianessCopy(void* dst, void* src, size_t size) const
|
||||
{
|
||||
static void* (*fp)(void*, void*, size_t, int) = NULL;
|
||||
if (fp == NULL)
|
||||
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianess);
|
||||
}
|
||||
};
|
||||
|
||||
#undef _PRISizeT
|
||||
#undef ImSnprintf
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
2
lib/external/imgui/source/TextEditor.cpp
vendored
2
lib/external/imgui/source/TextEditor.cpp
vendored
@@ -821,7 +821,7 @@ void TextEditor::Render() {
|
||||
ImGui::Text("Error at line %d:", errorIt->first);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.2f, 1.0f));
|
||||
ImGui::Text("%s", errorIt->second.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTooltip();
|
||||
|
||||
58
lib/external/imgui/source/imgui_impl_glfw.cpp
vendored
58
lib/external/imgui/source/imgui_impl_glfw.cpp
vendored
@@ -47,6 +47,7 @@
|
||||
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_impl_glfw.h"
|
||||
|
||||
// GLFW
|
||||
@@ -136,6 +137,51 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
|
||||
glfwSetClipboardString((GLFWwindow*)user_data, text);
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
static const char* ImGui_ImplWin_GetClipboardText(void*)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
g.ClipboardHandlerData.clear();
|
||||
if (!::OpenClipboard(NULL))
|
||||
return NULL;
|
||||
HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
|
||||
if (wbuf_handle == NULL)
|
||||
{
|
||||
::CloseClipboard();
|
||||
return NULL;
|
||||
}
|
||||
if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
|
||||
{
|
||||
int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
|
||||
g.ClipboardHandlerData.resize(buf_len);
|
||||
::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
|
||||
}
|
||||
::GlobalUnlock(wbuf_handle);
|
||||
::CloseClipboard();
|
||||
return g.ClipboardHandlerData.Data;
|
||||
}
|
||||
|
||||
static void ImGui_ImplWin_SetClipboardText(void*, const char* text)
|
||||
{
|
||||
if (!::OpenClipboard(NULL))
|
||||
return;
|
||||
const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
|
||||
HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
|
||||
if (wbuf_handle == NULL)
|
||||
{
|
||||
::CloseClipboard();
|
||||
return;
|
||||
}
|
||||
WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
|
||||
::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
|
||||
::GlobalUnlock(wbuf_handle);
|
||||
::EmptyClipboard();
|
||||
if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
|
||||
::GlobalFree(wbuf_handle);
|
||||
::CloseClipboard();
|
||||
}
|
||||
#endif
|
||||
|
||||
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
|
||||
{
|
||||
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||
@@ -271,9 +317,15 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
|
||||
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
|
||||
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
|
||||
|
||||
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
|
||||
io.ClipboardUserData = bd->Window;
|
||||
#if defined(OS_WINDOWS)
|
||||
io.SetClipboardTextFn = ImGui_ImplWin_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplWin_GetClipboardText;
|
||||
io.ClipboardUserData = bd->Window;
|
||||
#else
|
||||
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
|
||||
io.ClipboardUserData = bd->Window;
|
||||
#endif
|
||||
|
||||
// Create mouse cursors
|
||||
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
|
||||
|
||||
2
lib/external/libromfs
vendored
2
lib/external/libromfs
vendored
Submodule lib/external/libromfs updated: 0842d22deb...58757f6cad
2
lib/external/nativefiledialog
vendored
2
lib/external/nativefiledialog
vendored
Submodule lib/external/nativefiledialog updated: 322d1bc2a9...3311592818
1
lib/external/pattern_language
vendored
Submodule
1
lib/external/pattern_language
vendored
Submodule
Submodule lib/external/pattern_language added at 8a4ee8dfd7
20
lib/external/yara/CMakeLists.txt
vendored
20
lib/external/yara/CMakeLists.txt
vendored
@@ -81,17 +81,19 @@ set(LIBYARA_SOURCE
|
||||
)
|
||||
|
||||
set(LIBYARA_MODULES
|
||||
${LIBYARA_SOURCE_PATH}/modules/tests/tests.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/pe/pe.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/pe/pe_utils.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/elf/elf.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/math/math.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/time/time.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/macho/macho.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/hash/hash.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/console/console.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/dex/dex.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/dotnet/dotnet.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/magic/magic.c)
|
||||
${LIBYARA_SOURCE_PATH}/modules/elf/elf.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/hash/hash.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/macho/macho.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/magic/magic.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/math/math.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/pe/pe.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/pe/pe_utils.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/tests/tests.c
|
||||
${LIBYARA_SOURCE_PATH}/modules/time/time.c
|
||||
)
|
||||
|
||||
# Add mbedtls crypto wrappers
|
||||
add_compile_definitions("HAVE_MBEDTLS")
|
||||
|
||||
2
lib/external/yara/yara
vendored
2
lib/external/yara/yara
vendored
Submodule lib/external/yara/yara updated: 8206dc6f72...d5a7565a8b
8
lib/libimhex-rs/Cargo.lock
generated
8
lib/libimhex-rs/Cargo.lock
generated
@@ -568,9 +568,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
version = "1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -579,9 +579,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
version = "0.6.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
|
||||
@@ -10,6 +10,7 @@ set_target_properties(imgui PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/microtar ${CMAKE_CURRENT_BINARY_DIR}/external/microtar EXCLUDE_FROM_ALL)
|
||||
set_target_properties(microtar PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(NFD_PORTAL ON CACHE BOOL "Use Portals for Linux file dialogs" FORCE)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/nativefiledialog ${CMAKE_CURRENT_BINARY_DIR}/external/nativefiledialog EXCLUDE_FROM_ALL)
|
||||
set_target_properties(nfd PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
@@ -71,15 +72,16 @@ if (NOT USE_SYSTEM_CAPSTONE)
|
||||
set(CAPSTONE_BUILD_SHARED OFF CACHE BOOL "Disable shared library building")
|
||||
set(CAPSTONE_BUILD_TESTS OFF CACHE BOOL "Disable tests")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/capstone ${CMAKE_CURRENT_BINARY_DIR}/external/capstone EXCLUDE_FROM_ALL)
|
||||
set_target_properties(capstone-static PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set(CAPSTONE_LIBRARIES "capstone-static")
|
||||
set_target_properties(capstone PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set(CAPSTONE_LIBRARIES "capstone")
|
||||
set(CAPSTONE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../external/capstone/include)
|
||||
else()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(CAPSTONE 4.0.2 REQUIRED capstone)
|
||||
endif()
|
||||
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/pattern_language ${CMAKE_CURRENT_BINARY_DIR}/external/pattern_language EXCLUDE_FROM_ALL)
|
||||
set_target_properties(libpl PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
find_package(mbedTLS 2.26.0 REQUIRED)
|
||||
configurePython()
|
||||
@@ -113,7 +115,7 @@ set(LIBIMHEX_SOURCES
|
||||
source/data_processor/node.cpp
|
||||
|
||||
source/helpers/utils.cpp
|
||||
source/helpers/paths.cpp
|
||||
source/helpers/fs.cpp
|
||||
source/helpers/magic.cpp
|
||||
source/helpers/crypto.cpp
|
||||
source/helpers/net.cpp
|
||||
@@ -122,22 +124,14 @@ set(LIBIMHEX_SOURCES
|
||||
source/helpers/patches.cpp
|
||||
source/helpers/project_file_handler.cpp
|
||||
source/helpers/encoding_file.cpp
|
||||
source/helpers/loader_script_handler.cpp
|
||||
source/helpers/logger.cpp
|
||||
|
||||
source/pattern_language/pattern_language.cpp
|
||||
source/pattern_language/preprocessor.cpp
|
||||
source/pattern_language/lexer.cpp
|
||||
source/pattern_language/parser.cpp
|
||||
source/pattern_language/validator.cpp
|
||||
source/pattern_language/evaluator.cpp
|
||||
source/pattern_language/log_console.cpp
|
||||
source/helpers/tar.cpp
|
||||
|
||||
source/providers/provider.cpp
|
||||
|
||||
source/ui/imgui_imhex_extensions.cpp
|
||||
source/ui/view.cpp
|
||||
)
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(OSX_11_0_SDK_PATH /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk)
|
||||
@@ -149,13 +143,16 @@ if (APPLE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES} source/helpers/paths_mac.mm)
|
||||
set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES}
|
||||
source/helpers/fs_macos.m
|
||||
source/helpers/utils_macos.m)
|
||||
endif ()
|
||||
|
||||
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
|
||||
|
||||
add_library(libimhex SHARED ${LIBIMHEX_SOURCES})
|
||||
set_target_properties(libimhex PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_options(libimhex PRIVATE -Wall -Wextra -Werror)
|
||||
|
||||
target_include_directories(libimhex PUBLIC include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${MAGIC_INCLUDE_DIRS} ${Python_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${YARA_INCLUDE_DIRS})
|
||||
target_link_directories(libimhex PUBLIC ${MBEDTLS_LIBRARY_DIR} ${CAPSTONE_LIBRARY_DIRS} ${MAGIC_LIBRARY_DIRS})
|
||||
@@ -165,4 +162,4 @@ if (APPLE)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PUBLIC dl imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs)
|
||||
target_link_libraries(libimhex PUBLIC dl imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs libpl)
|
||||
|
||||
@@ -3,28 +3,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <hex/helpers/types.hpp>
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
|
||||
constexpr static const auto ImHexApiURL = "https://api.werwolv.net/imhex";
|
||||
constexpr static const auto GitHubApiURL = "https://api.github.com/repos/WerWolv/ImHex";
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using u128 = __uint128_t;
|
||||
|
||||
using i8 = std::int8_t;
|
||||
using i16 = std::int16_t;
|
||||
using i32 = std::int32_t;
|
||||
using i64 = std::int64_t;
|
||||
using i128 = __int128_t;
|
||||
|
||||
using color_t = u32;
|
||||
|
||||
namespace hex {
|
||||
|
||||
struct Region {
|
||||
u64 address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,27 +2,33 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <pl/pattern_language.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
using ImGuiDataType = int;
|
||||
using ImGuiInputTextFlags = int;
|
||||
|
||||
namespace pl {
|
||||
class Evaluator;
|
||||
}
|
||||
|
||||
namespace hex {
|
||||
|
||||
class View;
|
||||
class LanguageDefinition;
|
||||
namespace pl {
|
||||
class Evaluator;
|
||||
}
|
||||
|
||||
namespace dp {
|
||||
class Node;
|
||||
}
|
||||
@@ -117,37 +123,28 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct ColorPalette {
|
||||
struct FunctionDefinition {
|
||||
pl::api::Namespace ns;
|
||||
std::string name;
|
||||
std::vector<u32> colors;
|
||||
|
||||
pl::api::FunctionParameterCount parameterCount;
|
||||
pl::api::FunctionCallback callback;
|
||||
|
||||
bool dangerous;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
|
||||
constexpr static u32 MoreParametersThan = 0x8000'0000;
|
||||
constexpr static u32 LessParametersThan = 0x4000'0000;
|
||||
constexpr static u32 ExactlyOrMoreParametersThan = 0x2000'0000;
|
||||
constexpr static u32 NoParameters = 0x0000'0000;
|
||||
std::unique_ptr<pl::PatternLanguage> createDefaultRuntime(prv::Provider *provider);
|
||||
|
||||
using Namespace = std::vector<std::string>;
|
||||
using Callback = std::function<std::optional<hex::pl::Token::Literal>(hex::pl::Evaluator *, const std::vector<hex::pl::Token::Literal> &)>;
|
||||
void addPragma(const std::string &name, const pl::api::PragmaHandler &handler);
|
||||
|
||||
struct Function {
|
||||
u32 parameterCount;
|
||||
Callback func;
|
||||
bool dangerous;
|
||||
};
|
||||
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
|
||||
void addFunction(const Namespace &ns, const std::string &name, u32 parameterCount, const Callback &func);
|
||||
void addDangerousFunction(const Namespace &ns, const std::string &name, u32 parameterCount, const Callback &func);
|
||||
std::map<std::string, ContentRegistry::PatternLanguage::Function> &getFunctions();
|
||||
std::map<std::string, pl::api::PragmaHandler> &getPragmas();
|
||||
std::vector<impl::FunctionDefinition> &getFunctions();
|
||||
|
||||
std::vector<impl::ColorPalette> &getPalettes();
|
||||
void addColorPalette(const std::string &unlocalizedName, const std::vector<u32> &colors);
|
||||
void setSelectedPalette(u32 index);
|
||||
u32 getNextColor();
|
||||
void resetPalette();
|
||||
}
|
||||
|
||||
/* View Registry. Allows adding of new windows */
|
||||
@@ -159,7 +156,7 @@ namespace hex {
|
||||
|
||||
}
|
||||
|
||||
template<hex::derived_from<View> T, typename... Args>
|
||||
template<std::derived_from<View> T, typename... Args>
|
||||
void add(Args &&...args) {
|
||||
return impl::add(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
@@ -237,7 +234,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
template<hex::derived_from<dp::Node> T, typename... Args>
|
||||
template<std::derived_from<dp::Node> T, typename... Args>
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, Args &&...args) {
|
||||
add(impl::Entry { unlocalizedCategory.c_str(), unlocalizedName.c_str(), [=] {
|
||||
auto node = new T(std::forward<Args>(args)...);
|
||||
@@ -329,7 +326,7 @@ namespace hex {
|
||||
|
||||
}
|
||||
|
||||
template<hex::derived_from<hex::prv::Provider> T>
|
||||
template<std::derived_from<hex::prv::Provider> T>
|
||||
void add(const std::string &unlocalizedName, bool addToList = true) {
|
||||
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = unlocalizedName](const std::string &name, hex::prv::Provider **provider) {
|
||||
if (name != expectedName) return;
|
||||
@@ -372,7 +369,7 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
using Callback = std::function<bool(fs::path)>;
|
||||
using Callback = std::function<bool(std::fs::path)>;
|
||||
struct Entry {
|
||||
std::vector<std::string> extensions;
|
||||
Callback callback;
|
||||
@@ -385,6 +382,113 @@ namespace hex {
|
||||
std::vector<impl::Entry> &getEntries();
|
||||
|
||||
}
|
||||
|
||||
namespace HexEditor {
|
||||
|
||||
class DataVisualizer {
|
||||
public:
|
||||
DataVisualizer(u16 bytesPerCell, u16 maxCharsPerCell)
|
||||
: m_bytesPerCell(bytesPerCell), m_maxCharsPerCell(maxCharsPerCell) {}
|
||||
|
||||
virtual ~DataVisualizer() = default;
|
||||
|
||||
virtual void draw(u64 address, const u8 *data, size_t size, bool upperCase) = 0;
|
||||
virtual bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) = 0;
|
||||
|
||||
[[nodiscard]] u16 getBytesPerCell() const { return this->m_bytesPerCell; }
|
||||
[[nodiscard]] u16 getMaxCharsPerCell() const { return this->m_maxCharsPerCell; }
|
||||
|
||||
protected:
|
||||
const static int TextInputFlags;
|
||||
|
||||
bool drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const;
|
||||
private:
|
||||
u16 m_bytesPerCell;
|
||||
u16 m_maxCharsPerCell;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
void addDataVisualizer(const std::string &unlocalizedName, DataVisualizer *visualizer);
|
||||
|
||||
std::map<std::string, DataVisualizer*> &getVisualizers();
|
||||
|
||||
}
|
||||
|
||||
template<std::derived_from<DataVisualizer> T, typename... Args>
|
||||
void addDataVisualizer(const std::string &unlocalizedName, Args &&...args) {
|
||||
return impl::addDataVisualizer(unlocalizedName, new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Hashes {
|
||||
|
||||
class Hash {
|
||||
public:
|
||||
Hash(std::string unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {}
|
||||
|
||||
class Function {
|
||||
public:
|
||||
using Callback = std::function<std::vector<u8>(const Region&, prv::Provider *)>;
|
||||
|
||||
Function(const Hash *type, std::string name, Callback callback)
|
||||
: m_type(type), m_name(std::move(name)), m_callback(std::move(callback)) {
|
||||
|
||||
}
|
||||
|
||||
[[nodiscard]] const Hash *getType() const { return this->m_type; }
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
|
||||
const std::vector<u8>& get(const Region& region, prv::Provider *provider) {
|
||||
if (this->m_cache.empty()) {
|
||||
this->m_cache = this->m_callback(region, provider);
|
||||
}
|
||||
|
||||
return this->m_cache;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
this->m_cache.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
const Hash *m_type;
|
||||
std::string m_name;
|
||||
Callback m_callback;
|
||||
|
||||
std::vector<u8> m_cache;
|
||||
};
|
||||
|
||||
virtual void draw() { }
|
||||
[[nodiscard]] virtual Function create(std::string name) = 0;
|
||||
|
||||
[[nodiscard]] const std::string &getUnlocalizedName() const {
|
||||
return this->m_unlocalizedName;
|
||||
}
|
||||
|
||||
protected:
|
||||
[[nodiscard]] Function create(const std::string &name, const Function::Callback &callback) const {
|
||||
return { this, name, callback };
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_unlocalizedName;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
std::vector<Hash*> &getHashes();
|
||||
|
||||
void add(Hash* hash);
|
||||
}
|
||||
|
||||
template<typename T, typename ... Args>
|
||||
void add(Args && ... args) {
|
||||
impl::add(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#define EVENT_DEF(event_name, ...) \
|
||||
struct event_name final : public hex::Event<__VA_ARGS__> { \
|
||||
@@ -16,7 +16,11 @@
|
||||
explicit event_name(Callback func) noexcept : Event(std::move(func)) { } \
|
||||
}
|
||||
|
||||
class GLFWwindow;
|
||||
struct GLFWwindow;
|
||||
|
||||
namespace pl {
|
||||
class Pattern;
|
||||
}
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -87,7 +91,7 @@ namespace hex {
|
||||
static void post(auto &&...args) noexcept {
|
||||
for (const auto &[id, event] : s_events) {
|
||||
if (id == E::id)
|
||||
(*reinterpret_cast<E *>(event))(std::forward<decltype(args)>(args)...);
|
||||
(*static_cast<E *const>(event))(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,12 +100,8 @@ namespace hex {
|
||||
static EventList s_events;
|
||||
};
|
||||
|
||||
namespace pl {
|
||||
class Pattern;
|
||||
}
|
||||
|
||||
/* Default Events */
|
||||
EVENT_DEF(EventFileLoaded, fs::path);
|
||||
EVENT_DEF(EventFileLoaded, std::fs::path);
|
||||
EVENT_DEF(EventFileUnloaded);
|
||||
EVENT_DEF(EventDataChanged);
|
||||
EVENT_DEF(EventHighlightingChanged);
|
||||
@@ -124,11 +124,17 @@ namespace hex {
|
||||
EVENT_DEF(RequestSetPatternLanguageCode, std::string);
|
||||
EVENT_DEF(RequestChangeWindowTitle, std::string);
|
||||
EVENT_DEF(RequestCloseImHex, bool);
|
||||
EVENT_DEF(RequestOpenFile, fs::path);
|
||||
EVENT_DEF(RequestOpenFile, std::fs::path);
|
||||
EVENT_DEF(RequestChangeTheme, u32);
|
||||
EVENT_DEF(RequestOpenPopup, std::string);
|
||||
EVENT_DEF(RequestCreateProvider, std::string, hex::prv::Provider **);
|
||||
|
||||
EVENT_DEF(QuerySelection, Region &);
|
||||
EVENT_DEF(RequestShowInfoPopup, std::string);
|
||||
EVENT_DEF(RequestShowErrorPopup, std::string);
|
||||
EVENT_DEF(RequestShowFatalErrorPopup, std::string);
|
||||
EVENT_DEF(RequestShowYesNoQuestionPopup, std::string, std::function<void()>, std::function<void()>);
|
||||
EVENT_DEF(RequestShowFileChooserPopup, std::vector<std::fs::path>, std::vector<nfdfilteritem_t>, std::function<void(std::fs::path)>);
|
||||
|
||||
EVENT_DEF(QuerySelection, std::optional<Region> &);
|
||||
|
||||
}
|
||||
@@ -6,10 +6,13 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <map>
|
||||
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/api/task.hpp>
|
||||
#include <hex/api/keybinding.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
using ImGuiID = unsigned int;
|
||||
struct ImVec2;
|
||||
@@ -31,37 +34,69 @@ namespace hex {
|
||||
|
||||
namespace HexEditor {
|
||||
|
||||
using TooltipFunction = std::function<void(u64, const u8*, size_t)>;
|
||||
|
||||
class Highlighting {
|
||||
public:
|
||||
Highlighting() = default;
|
||||
Highlighting(Region region, color_t color, std::string tooltip = "");
|
||||
Highlighting(Region region, color_t color);
|
||||
|
||||
[[nodiscard]] const Region &getRegion() const { return this->m_region; }
|
||||
[[nodiscard]] const color_t &getColor() const { return this->m_color; }
|
||||
[[nodiscard]] const std::string &getTooltip() const { return this->m_tooltip; }
|
||||
|
||||
private:
|
||||
Region m_region = {};
|
||||
color_t m_color = 0x00;
|
||||
std::string m_tooltip;
|
||||
};
|
||||
|
||||
class Tooltip {
|
||||
public:
|
||||
Tooltip() = default;
|
||||
Tooltip(Region region, std::string value, color_t color);
|
||||
|
||||
[[nodiscard]] const Region &getRegion() const { return this->m_region; }
|
||||
[[nodiscard]] const color_t &getColor() const { return this->m_color; }
|
||||
[[nodiscard]] const std::string &getValue() const { return this->m_value; }
|
||||
|
||||
private:
|
||||
Region m_region = {};
|
||||
std::string m_value;
|
||||
color_t m_color = 0x00;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
using HighlightingFunction = std::function<std::optional<Highlighting>(u64)>;
|
||||
using HighlightingFunction = std::function<std::optional<color_t>(u64, const u8*, size_t)>;
|
||||
|
||||
std::map<u32, Highlighting> &getHighlights();
|
||||
std::map<u32, HighlightingFunction> &getHighlightingFunctions();
|
||||
std::map<u32, Highlighting> &getBackgroundHighlights();
|
||||
std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions();
|
||||
std::map<u32, Highlighting> &getForegroundHighlights();
|
||||
std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions();
|
||||
std::map<u32, Tooltip> &getTooltips();
|
||||
std::map<u32, TooltipFunction> &getTooltipFunctions();
|
||||
|
||||
}
|
||||
|
||||
u32 addHighlight(const Region ®ion, color_t color, const std::string &tooltip = "");
|
||||
void removeHighlight(u32 id);
|
||||
u32 addBackgroundHighlight(const Region ®ion, color_t color);
|
||||
void removeBackgroundHighlight(u32 id);
|
||||
|
||||
u32 addHighlightingProvider(const impl::HighlightingFunction &function);
|
||||
void removeHighlightingProvider(u32 id);
|
||||
u32 addForegroundHighlight(const Region ®ion, color_t color);
|
||||
void removeForegroundHighlight(u32 id);
|
||||
|
||||
Region getSelection();
|
||||
u32 addTooltip(Region region, std::string value, color_t color);
|
||||
void removeTooltip(u32 id);
|
||||
|
||||
u32 addTooltipProvider(TooltipFunction function);
|
||||
void removeTooltipProvider(u32 id);
|
||||
|
||||
u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function);
|
||||
void removeBackgroundHighlightingProvider(u32 id);
|
||||
|
||||
u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function);
|
||||
void removeForegroundHighlightingProvider(u32 id);
|
||||
|
||||
bool isSelectionValid();
|
||||
std::optional<Region> getSelection();
|
||||
void setSelection(const Region ®ion);
|
||||
void setSelection(u64 address, size_t size);
|
||||
|
||||
@@ -76,8 +111,6 @@ namespace hex {
|
||||
std::string comment;
|
||||
u32 color;
|
||||
bool locked;
|
||||
|
||||
u32 highlightId;
|
||||
};
|
||||
|
||||
void add(u64 address, size_t size, const std::string &name, const std::string &comment, color_t color = 0x00000000);
|
||||
@@ -95,7 +128,7 @@ namespace hex {
|
||||
|
||||
void add(prv::Provider *provider);
|
||||
|
||||
template<hex::derived_from<prv::Provider> T>
|
||||
template<std::derived_from<prv::Provider> T>
|
||||
void add(auto &&...args) {
|
||||
add(new T(std::forward<decltype(args)>(args)...));
|
||||
}
|
||||
@@ -126,6 +159,11 @@ namespace hex {
|
||||
void setProgramArguments(int argc, char **argv, char **envp);
|
||||
|
||||
void setBorderlessWindowMode(bool enabled);
|
||||
|
||||
void setCustomFontPath(const std::fs::path &path);
|
||||
void setFontSize(float size);
|
||||
|
||||
void setGPUVendor(const std::string &vendor);
|
||||
}
|
||||
|
||||
struct ProgramArguments {
|
||||
@@ -134,6 +172,12 @@ namespace hex {
|
||||
char **envp;
|
||||
};
|
||||
|
||||
enum class Theme {
|
||||
Dark = 1,
|
||||
Light = 2,
|
||||
Classic = 3
|
||||
};
|
||||
|
||||
const ProgramArguments &getProgramArguments();
|
||||
|
||||
float getTargetFPS();
|
||||
@@ -149,6 +193,19 @@ namespace hex {
|
||||
|
||||
std::map<std::string, std::string> &getInitArguments();
|
||||
|
||||
const std::fs::path &getCustomFontPath();
|
||||
float getFontSize();
|
||||
|
||||
void setTheme(Theme theme);
|
||||
Theme getTheme();
|
||||
|
||||
void enableSystemThemeDetection(bool enabled);
|
||||
bool usesSystemThemeDetection();
|
||||
|
||||
const std::vector<std::fs::path> &getAdditionalFolderPaths();
|
||||
void setAdditionalFolderPaths(const std::vector<std::fs::path> &paths);
|
||||
|
||||
const std::string &getGPUVendor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ struct ImGuiWindow;
|
||||
|
||||
namespace hex {
|
||||
|
||||
struct View;
|
||||
class View;
|
||||
|
||||
enum class Keys
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace hex {
|
||||
public:
|
||||
LanguageDefinition(std::initializer_list<std::pair<std::string, std::string>> entries);
|
||||
|
||||
const std::map<std::string, std::string> &getEntries() const;
|
||||
[[nodiscard]] const std::map<std::string, std::string> &getEntries() const;
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> m_entries;
|
||||
@@ -20,7 +20,7 @@ namespace hex {
|
||||
class LangEntry {
|
||||
public:
|
||||
explicit LangEntry(const char *unlocalizedString);
|
||||
explicit LangEntry(const std::string &unlocalizedString);
|
||||
explicit LangEntry(std::string unlocalizedString);
|
||||
explicit LangEntry(std::string_view unlocalizedString);
|
||||
|
||||
operator std::string() const;
|
||||
|
||||
@@ -3,17 +3,23 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
struct ImGuiContext;
|
||||
|
||||
namespace hex {
|
||||
|
||||
class Plugin {
|
||||
public:
|
||||
explicit Plugin(const fs::path &path);
|
||||
explicit Plugin(const std::fs::path &path);
|
||||
Plugin(const Plugin &) = delete;
|
||||
Plugin(Plugin &&other) noexcept;
|
||||
~Plugin();
|
||||
@@ -26,7 +32,7 @@ namespace hex {
|
||||
void setImGuiContext(ImGuiContext *ctx) const;
|
||||
[[nodiscard]] bool isBuiltinPlugin() const;
|
||||
|
||||
[[nodiscard]] const fs::path &getPath() const;
|
||||
[[nodiscard]] const std::fs::path &getPath() const;
|
||||
|
||||
[[nodiscard]] bool isLoaded() const;
|
||||
|
||||
@@ -39,8 +45,12 @@ namespace hex {
|
||||
using SetImGuiContextFunc = void (*)(ImGuiContext *);
|
||||
using IsBuiltinPluginFunc = bool (*)();
|
||||
|
||||
void *m_handle = nullptr;
|
||||
fs::path m_path;
|
||||
#if defined(OS_WINDOWS)
|
||||
HMODULE m_handle = nullptr;
|
||||
#else
|
||||
void *m_handle = nullptr;
|
||||
#endif
|
||||
std::fs::path m_path;
|
||||
|
||||
mutable bool m_initialized = false;
|
||||
|
||||
@@ -65,7 +75,7 @@ namespace hex {
|
||||
public:
|
||||
PluginManager() = delete;
|
||||
|
||||
static bool load(const fs::path &pluginFolder);
|
||||
static bool load(const std::fs::path &pluginFolder);
|
||||
static void unload();
|
||||
static void reload();
|
||||
|
||||
@@ -74,7 +84,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
private:
|
||||
static fs::path s_pluginFolder;
|
||||
static std::fs::path s_pluginFolder;
|
||||
static std::vector<Plugin> s_plugins;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
#include <hex/data_processor/attribute.hpp>
|
||||
|
||||
#include <set>
|
||||
@@ -38,8 +39,8 @@ namespace hex::dp {
|
||||
virtual void drawNode() { }
|
||||
virtual void process() = 0;
|
||||
|
||||
virtual void store(nlohmann::json &j) { }
|
||||
virtual void load(nlohmann::json &j) { }
|
||||
virtual void store(nlohmann::json &j) { hex::unused(j); }
|
||||
virtual void load(nlohmann::json &j) { hex::unused(j); }
|
||||
|
||||
using NodeError = std::pair<Node *, std::string>;
|
||||
|
||||
@@ -90,11 +91,11 @@ namespace hex::dp {
|
||||
}
|
||||
|
||||
std::vector<u8> getBufferOnInput(u32 index);
|
||||
u64 getIntegerOnInput(u32 index);
|
||||
i64 getIntegerOnInput(u32 index);
|
||||
float getFloatOnInput(u32 index);
|
||||
|
||||
void setBufferOnOutput(u32 index, std::vector<u8> data);
|
||||
void setIntegerOnOutput(u32 index, u64 integer);
|
||||
void setBufferOnOutput(u32 index, const std::vector<u8> &data);
|
||||
void setIntegerOnOutput(u32 index, i64 integer);
|
||||
void setFloatOnOutput(u32 index, float floatingPoint);
|
||||
|
||||
void setOverlayData(u64 address, const std::vector<u8> &data);
|
||||
|
||||
@@ -5,151 +5,7 @@
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
namespace hex {
|
||||
|
||||
template<typename>
|
||||
struct is_integral_helper : public std::false_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<u8> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<i8> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<u16> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<i16> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<u32> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<i32> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<u64> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<i64> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<u128> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<i128> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<bool> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<char> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<char8_t> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<char16_t> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<char32_t> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_integral_helper<wchar_t> : public std::true_type { };
|
||||
|
||||
template<typename T>
|
||||
struct is_integral : public is_integral_helper<std::remove_cvref_t<T>>::type { };
|
||||
|
||||
|
||||
template<typename>
|
||||
struct is_signed_helper : public std::false_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<i8> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<i16> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<i32> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<i64> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<i128> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<char> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<float> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<double> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_signed_helper<long double> : public std::true_type { };
|
||||
|
||||
template<typename T>
|
||||
struct is_signed : public is_signed_helper<std::remove_cvref_t<T>>::type { };
|
||||
|
||||
|
||||
template<typename>
|
||||
struct is_floating_point_helper : public std::false_type { };
|
||||
|
||||
template<>
|
||||
struct is_floating_point_helper<float> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_floating_point_helper<double> : public std::true_type { };
|
||||
|
||||
template<>
|
||||
struct is_floating_point_helper<long double> : public std::true_type { };
|
||||
|
||||
template<typename T>
|
||||
struct is_floating_point : public is_floating_point_helper<std::remove_cvref_t<T>>::type { };
|
||||
|
||||
}
|
||||
|
||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 12000
|
||||
#if __has_include(<concepts>)
|
||||
// Make sure we break when derived_from is implemented in libc++. Then we can fix a compatibility version above
|
||||
#include <concepts>
|
||||
#endif
|
||||
// libcxx 12 still doesn't have many default concepts implemented, as a result we need to define it ourself using clang built-ins.
|
||||
// [concept.derived] (patch from https://reviews.llvm.org/D74292)
|
||||
namespace hex {
|
||||
template<class _Dp, class _Bp>
|
||||
concept derived_from =
|
||||
__is_base_of(_Bp, _Dp) && __is_convertible_to(const volatile _Dp *, const volatile _Bp *);
|
||||
}
|
||||
|
||||
#else
|
||||
// Assume supported
|
||||
#include <concepts>
|
||||
namespace hex {
|
||||
using std::derived_from;
|
||||
}
|
||||
#endif
|
||||
|
||||
// [concepts.arithmetic]
|
||||
namespace hex {
|
||||
|
||||
template<class T>
|
||||
concept integral = hex::is_integral<T>::value;
|
||||
|
||||
template<class T>
|
||||
concept signed_integral = integral<T> && hex::is_signed<T>::value;
|
||||
|
||||
template<class T>
|
||||
concept unsigned_integral = integral<T> && !signed_integral<T>;
|
||||
|
||||
template<class T>
|
||||
concept floating_point = std::is_floating_point<T>::value;
|
||||
|
||||
}
|
||||
#include <concepts>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -159,11 +15,6 @@ namespace hex {
|
||||
template<typename T, size_t Size>
|
||||
concept has_size = sizeof(T) == Size;
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace hex {
|
||||
|
||||
template<typename T>
|
||||
class Cloneable {
|
||||
public:
|
||||
|
||||
@@ -31,12 +31,12 @@ namespace hex {
|
||||
|
||||
class Disassembler {
|
||||
public:
|
||||
static constexpr cs_arch toCapstoneArchictecture(Architecture architecture) {
|
||||
static constexpr cs_arch toCapstoneArchitecture(Architecture architecture) {
|
||||
return static_cast<cs_arch>(architecture);
|
||||
}
|
||||
|
||||
static inline bool isSupported(Architecture architecture) {
|
||||
return cs_support(toCapstoneArchictecture(architecture));
|
||||
return cs_support(toCapstoneArchitecture(architecture));
|
||||
}
|
||||
|
||||
constexpr static const char *const ArchitectureNames[] = { "ARM32", "ARM64", "MIPS", "x86", "PowerPC", "Sparc", "SystemZ", "XCore", "68K", "TMS320C64x", "680X", "Ethereum", "MOS65XX", "WebAssembly", "Berkeley Packet Filter", "RISC-V" };
|
||||
|
||||
@@ -2,22 +2,34 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
// TODO: Workaround for weird issue picked up by GCC 12.1.0 and later. This seems like a compiler bug mentioned in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98465
|
||||
#pragma GCC diagnostic push
|
||||
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#if (__GNUC__ >= 12)
|
||||
#pragma GCC diagnostic ignored "-Wrestrict"
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overread"
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
class EncodingFile {
|
||||
public:
|
||||
enum class Type {
|
||||
enum class Type
|
||||
{
|
||||
Thingy
|
||||
};
|
||||
|
||||
EncodingFile() = default;
|
||||
EncodingFile(Type type, const fs::path &path);
|
||||
EncodingFile(Type type, const std::fs::path &path);
|
||||
|
||||
[[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(const std::vector<u8> &buffer) const;
|
||||
[[nodiscard]] size_t getLongestSequence() const { return this->m_longestSequence; }
|
||||
@@ -25,12 +37,12 @@ namespace hex {
|
||||
[[nodiscard]] bool valid() const { return this->m_valid; }
|
||||
|
||||
private:
|
||||
void parseThingyFile(std::ifstream &content);
|
||||
void parseThingyFile(fs::File &file);
|
||||
|
||||
bool m_valid = false;
|
||||
|
||||
std::map<u32, std::map<std::vector<u8>, std::string>> m_mapping;
|
||||
std::map<size_t, std::map<std::vector<u8>, std::string>> m_mapping;
|
||||
size_t m_longestSequence = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
#define off64_t off_t
|
||||
@@ -16,7 +16,7 @@
|
||||
#define ftruncate64 ftruncate
|
||||
#endif
|
||||
|
||||
namespace hex {
|
||||
namespace hex::fs {
|
||||
|
||||
class File {
|
||||
public:
|
||||
@@ -27,7 +27,7 @@ namespace hex {
|
||||
Create
|
||||
};
|
||||
|
||||
explicit File(const fs::path &path, Mode mode) noexcept;
|
||||
explicit File(const std::fs::path &path, Mode mode) noexcept;
|
||||
File() noexcept;
|
||||
File(const File &) = delete;
|
||||
File(File &&other) noexcept;
|
||||
@@ -38,7 +38,7 @@ namespace hex {
|
||||
|
||||
|
||||
[[nodiscard]] bool isValid() const {
|
||||
return this->m_file != nullptr && fs::exists(this->m_path) && !fs::is_directory(this->m_path);
|
||||
return this->m_file != nullptr && fs::exists(this->m_path) && !fs::isDirectory(this->m_path);
|
||||
}
|
||||
|
||||
void seek(u64 offset);
|
||||
@@ -47,10 +47,12 @@ namespace hex {
|
||||
size_t readBuffer(u8 *buffer, size_t size);
|
||||
std::vector<u8> readBytes(size_t numBytes = 0);
|
||||
std::string readString(size_t numBytes = 0);
|
||||
std::u8string readU8String(size_t numBytes = 0);
|
||||
|
||||
void write(const u8 *buffer, size_t size);
|
||||
void write(const std::vector<u8> &bytes);
|
||||
void write(const std::string &string);
|
||||
void write(const std::u8string &string);
|
||||
|
||||
[[nodiscard]] size_t getSize() const;
|
||||
void setSize(u64 size);
|
||||
@@ -59,13 +61,13 @@ namespace hex {
|
||||
bool remove();
|
||||
|
||||
auto getHandle() { return this->m_file; }
|
||||
const fs::path &getPath() { return this->m_path; }
|
||||
const std::fs::path &getPath() { return this->m_path; }
|
||||
|
||||
void disableBuffering();
|
||||
|
||||
private:
|
||||
FILE *m_file;
|
||||
fs::path m_path;
|
||||
std::fs::path m_path;
|
||||
};
|
||||
|
||||
}
|
||||
100
lib/libimhex/include/hex/helpers/fs.hpp
Normal file
100
lib/libimhex/include/hex/helpers/fs.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
|
||||
#include <nfd.hpp>
|
||||
|
||||
namespace std::fs {
|
||||
using namespace std::filesystem;
|
||||
}
|
||||
|
||||
namespace hex::fs {
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline bool exists(const std::fs::path &path) {
|
||||
std::error_code error;
|
||||
return std::filesystem::exists(path, error) && !error;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline bool createDirectories(const std::fs::path &path) {
|
||||
std::error_code error;
|
||||
return std::filesystem::create_directories(path, error) && !error;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline bool isRegularFile(const std::fs::path &path) {
|
||||
std::error_code error;
|
||||
return std::filesystem::is_regular_file(path, error) && !error;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline bool copyFile(const std::fs::path &from, const std::fs::path &to, std::fs::copy_options = std::fs::copy_options::none) {
|
||||
std::error_code error;
|
||||
return std::filesystem::copy_file(from, to, error) && !error;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline bool isDirectory(const std::fs::path &path) {
|
||||
std::error_code error;
|
||||
return std::filesystem::is_directory(path, error) && !error;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline bool remove(const std::fs::path &path) {
|
||||
std::error_code error;
|
||||
return std::filesystem::remove(path, error) && !error;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline bool removeAll(const std::fs::path &path) {
|
||||
std::error_code error;
|
||||
return std::filesystem::remove_all(path, error) && !error;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static inline uintmax_t getFileSize(const std::fs::path &path) {
|
||||
std::error_code error;
|
||||
auto size = std::filesystem::file_size(path, error);
|
||||
|
||||
if (error) return 0;
|
||||
else return size;
|
||||
}
|
||||
|
||||
bool isPathWritable(const std::fs::path &path);
|
||||
|
||||
std::fs::path toShortPath(const std::fs::path &path);
|
||||
|
||||
enum class DialogMode
|
||||
{
|
||||
Open,
|
||||
Save,
|
||||
Folder
|
||||
};
|
||||
|
||||
bool openFileBrowser(DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath = {});
|
||||
|
||||
enum class ImHexPath
|
||||
{
|
||||
Patterns,
|
||||
PatternsInclude,
|
||||
Magic,
|
||||
Python,
|
||||
Plugins,
|
||||
Yara,
|
||||
Config,
|
||||
Resources,
|
||||
Constants,
|
||||
Encodings,
|
||||
Logs
|
||||
};
|
||||
|
||||
std::optional<std::fs::path> getExecutablePath();
|
||||
|
||||
std::vector<std::fs::path> getDefaultPaths(ImHexPath path, bool listNonExisting = false);
|
||||
|
||||
}
|
||||
12
lib/libimhex/include/hex/helpers/fs_macos.hpp
Normal file
12
lib/libimhex/include/hex/helpers/fs_macos.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
extern "C" char * getMacExecutableDirectoryPath();
|
||||
extern "C" char * getMacApplicationSupportDirectoryPath();
|
||||
|
||||
extern "C" void macFree(void *ptr);
|
||||
|
||||
#endif
|
||||
13
lib/libimhex/include/hex/helpers/intrinsics.hpp
Normal file
13
lib/libimhex/include/hex/helpers/intrinsics.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace hex {
|
||||
|
||||
[[noreturn]] inline void unreachable() {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
inline void unused(auto && ... x) {
|
||||
((void)x, ...);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <hex/helpers/paths.hpp>
|
||||
|
||||
struct _object;
|
||||
typedef struct _object PyObject;
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv {
|
||||
class Provider;
|
||||
}
|
||||
|
||||
class LoaderScript {
|
||||
public:
|
||||
LoaderScript() = delete;
|
||||
|
||||
static bool processFile(const fs::path &scriptPath);
|
||||
|
||||
static void setFilePath(const fs::path &filePath) { LoaderScript::s_filePath = filePath; }
|
||||
static void setDataProvider(prv::Provider *provider) { LoaderScript::s_dataProvider = provider; }
|
||||
|
||||
private:
|
||||
static inline fs::path s_filePath;
|
||||
static inline prv::Provider *s_dataProvider;
|
||||
|
||||
static PyObject *Py_getFilePath(PyObject *self, PyObject *args);
|
||||
static PyObject *Py_addPatch(PyObject *self, PyObject *args);
|
||||
static PyObject *Py_addBookmark(PyObject *self, PyObject *args);
|
||||
|
||||
static PyObject *Py_addStruct(PyObject *self, PyObject *args);
|
||||
static PyObject *Py_addUnion(PyObject *self, PyObject *args);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <fmt/core.h>
|
||||
@@ -13,7 +15,7 @@ namespace hex::log {
|
||||
|
||||
namespace {
|
||||
|
||||
void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level) {
|
||||
[[maybe_unused]] void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level) {
|
||||
const auto now = fmt::localtime(std::chrono::system_clock::now());
|
||||
|
||||
fmt::print(dest, "[{0:%H:%M:%S}] ", now);
|
||||
@@ -27,7 +29,7 @@ namespace hex::log {
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto... args) {
|
||||
[[maybe_unused]] void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto... args) {
|
||||
auto dest = getDestination();
|
||||
|
||||
printPrefix(dest, ts, level);
|
||||
@@ -37,28 +39,30 @@ namespace hex::log {
|
||||
|
||||
}
|
||||
|
||||
void debug(const std::string &fmt, auto &&...args) {
|
||||
[[maybe_unused]] void debug(const std::string &fmt, auto &&...args) {
|
||||
#if defined(DEBUG)
|
||||
log::print(fg(fmt::color::green_yellow) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
|
||||
hex::log::print(fg(fmt::color::green_yellow) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
|
||||
#else
|
||||
hex::unused(fmt, args...);
|
||||
#endif
|
||||
}
|
||||
|
||||
void info(const std::string &fmt, auto &&...args) {
|
||||
log::print(fg(fmt::color::cadet_blue) | fmt::emphasis::bold, "[INFO] ", fmt, args...);
|
||||
[[maybe_unused]] void info(const std::string &fmt, auto &&...args) {
|
||||
hex::log::print(fg(fmt::color::cadet_blue) | fmt::emphasis::bold, "[INFO] ", fmt, args...);
|
||||
}
|
||||
|
||||
void warn(const std::string &fmt, auto &&...args) {
|
||||
log::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ", fmt, args...);
|
||||
[[maybe_unused]] void warn(const std::string &fmt, auto &&...args) {
|
||||
hex::log::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ", fmt, args...);
|
||||
}
|
||||
|
||||
void error(const std::string &fmt, auto &&...args) {
|
||||
log::print(fg(fmt::color::red) | fmt::emphasis::bold, "[ERROR]", fmt, args...);
|
||||
[[maybe_unused]] void error(const std::string &fmt, auto &&...args) {
|
||||
hex::log::print(fg(fmt::color::red) | fmt::emphasis::bold, "[ERROR]", fmt, args...);
|
||||
}
|
||||
|
||||
void fatal(const std::string &fmt, auto &&...args) {
|
||||
log::print(fg(fmt::color::purple) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
|
||||
[[maybe_unused]] void fatal(const std::string &fmt, auto &&...args) {
|
||||
hex::log::print(fg(fmt::color::purple) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
|
||||
}
|
||||
|
||||
void redirectToFile();
|
||||
[[maybe_unused]] void redirectToFile();
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <curl/system.h>
|
||||
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
using CURL = void;
|
||||
struct curl_slist;
|
||||
@@ -40,15 +40,18 @@ namespace hex {
|
||||
std::future<Response<std::string>> getString(const std::string &url, u32 timeout = DefaultTimeout);
|
||||
std::future<Response<nlohmann::json>> getJson(const std::string &url, u32 timeout = DefaultTimeout);
|
||||
|
||||
std::future<Response<std::string>> uploadFile(const std::string &url, const fs::path &filePath, u32 timeout = DefaultTimeout);
|
||||
std::future<Response<void>> downloadFile(const std::string &url, const fs::path &filePath, u32 timeout = DefaultTimeout);
|
||||
std::future<Response<std::string>> uploadFile(const std::string &url, const std::fs::path &filePath, u32 timeout = DefaultTimeout);
|
||||
std::future<Response<void>> downloadFile(const std::string &url, const std::fs::path &filePath, u32 timeout = DefaultTimeout);
|
||||
|
||||
[[nodiscard]] std::string encode(const std::string &input);
|
||||
[[nodiscard]] std::string decode(const std::string &input);
|
||||
|
||||
[[nodiscard]] float getProgress() const { return this->m_progress; }
|
||||
|
||||
void cancel() { this->m_shouldCancel = true; }
|
||||
|
||||
static void setProxy(const std::string &url);
|
||||
|
||||
private:
|
||||
void setCommonSettings(std::string &response, const std::string &url, u32 timeout = 2000, const std::map<std::string, std::string> &extraHeaders = {}, const std::string &body = {});
|
||||
std::optional<i32> execute();
|
||||
@@ -62,6 +65,8 @@ namespace hex {
|
||||
std::mutex m_transmissionActive;
|
||||
float m_progress = 0.0F;
|
||||
bool m_shouldCancel = false;
|
||||
|
||||
static std::string s_proxyUrl;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
enum class ImHexPath {
|
||||
Patterns,
|
||||
PatternsInclude,
|
||||
Magic,
|
||||
Python,
|
||||
Plugins,
|
||||
Yara,
|
||||
Config,
|
||||
Resources,
|
||||
Constants,
|
||||
Encodings,
|
||||
Logs
|
||||
};
|
||||
|
||||
std::string getExecutablePath();
|
||||
|
||||
std::vector<fs::path> getPath(ImHexPath path, bool listNonExisting = false);
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
#include <hex/helpers/paths.hpp>
|
||||
|
||||
namespace hex {
|
||||
std::string getMacExecutableDirectoryPath();
|
||||
std::string getMacApplicationSupportDirectoryPath();
|
||||
}
|
||||
#endif
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace hex {
|
||||
public:
|
||||
ProjectFile() = delete;
|
||||
|
||||
static bool load(const fs::path &filePath);
|
||||
static bool store(fs::path filePath = {});
|
||||
static bool load(const std::fs::path &filePath);
|
||||
static bool store(std::fs::path filePath = {});
|
||||
|
||||
[[nodiscard]] static bool hasUnsavedChanges() {
|
||||
return ProjectFile::s_hasUnsavedChanged;
|
||||
@@ -29,10 +29,10 @@ namespace hex {
|
||||
ProjectFile::s_hasUnsavedChanged = true;
|
||||
|
||||
if (setWindowTitle)
|
||||
EventManager::post<RequestChangeWindowTitle>(fs::path(getFilePath()).filename().string());
|
||||
EventManager::post<RequestChangeWindowTitle>(std::fs::path(getFilePath()).filename().string());
|
||||
}
|
||||
|
||||
[[nodiscard]] static const fs::path &getProjectFilePath() {
|
||||
[[nodiscard]] static const std::fs::path &getProjectFilePath() {
|
||||
return ProjectFile::s_currProjectFilePath;
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] static const fs::path &getFilePath() {
|
||||
[[nodiscard]] static const std::fs::path &getFilePath() {
|
||||
return ProjectFile::s_filePath;
|
||||
}
|
||||
|
||||
static void setFilePath(const fs::path &filePath) {
|
||||
static void setFilePath(const std::fs::path &filePath) {
|
||||
ProjectFile::s_filePath = filePath;
|
||||
|
||||
EventManager::post<RequestChangeWindowTitle>(filePath.filename().string());
|
||||
@@ -92,10 +92,10 @@ namespace hex {
|
||||
}
|
||||
|
||||
private:
|
||||
static fs::path s_currProjectFilePath;
|
||||
static std::fs::path s_currProjectFilePath;
|
||||
static bool s_hasUnsavedChanged;
|
||||
|
||||
static fs::path s_filePath;
|
||||
static std::fs::path s_filePath;
|
||||
static std::string s_pattern;
|
||||
static Patches s_patches;
|
||||
static std::list<ImHexApi::Bookmarks::Entry> s_bookmarks;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace hex {
|
||||
public:
|
||||
Socket() = default;
|
||||
Socket(const Socket &) = delete;
|
||||
Socket(Socket &&other);
|
||||
Socket(Socket &&other) noexcept;
|
||||
|
||||
Socket(const std::string &address, u16 port);
|
||||
~Socket();
|
||||
@@ -36,8 +36,8 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] bool isConnected() const;
|
||||
|
||||
std::string readString(size_t size = 0x1000) const;
|
||||
std::vector<u8> readBytes(size_t size = 0x1000) const;
|
||||
[[nodiscard]] std::string readString(size_t size = 0x1000) const;
|
||||
[[nodiscard]] std::vector<u8> readBytes(size_t size = 0x1000) const;
|
||||
|
||||
void writeString(const std::string &string) const;
|
||||
void writeBytes(const std::vector<u8> &bytes) const;
|
||||
|
||||
35
lib/libimhex/include/hex/helpers/tar.hpp
Normal file
35
lib/libimhex/include/hex/helpers/tar.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#include <microtar.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
class Tar {
|
||||
public:
|
||||
enum class Mode {
|
||||
Read,
|
||||
Write,
|
||||
Create
|
||||
};
|
||||
|
||||
Tar() = default;
|
||||
Tar(const std::fs::path &path, Mode mode);
|
||||
~Tar();
|
||||
|
||||
std::vector<u8> read(const std::fs::path &path);
|
||||
void write(const std::fs::path &path, const std::vector<u8> &data);
|
||||
|
||||
std::vector<std::fs::path> listEntries();
|
||||
|
||||
void extract(const std::fs::path &path, const std::fs::path &outputPath);
|
||||
void extractAll(const std::fs::path &outputPath);
|
||||
|
||||
private:
|
||||
mtar_t m_ctx = { };
|
||||
};
|
||||
|
||||
}
|
||||
44
lib/libimhex/include/hex/helpers/types.hpp
Normal file
44
lib/libimhex/include/hex/helpers/types.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using u128 = __uint128_t;
|
||||
|
||||
using i8 = std::int8_t;
|
||||
using i16 = std::int16_t;
|
||||
using i32 = std::int32_t;
|
||||
using i64 = std::int64_t;
|
||||
using i128 = __int128_t;
|
||||
|
||||
using color_t = u32;
|
||||
|
||||
namespace hex {
|
||||
|
||||
struct Region {
|
||||
u64 address;
|
||||
size_t size;
|
||||
|
||||
[[nodiscard]] constexpr bool isWithin(const Region &other) const {
|
||||
return (this->getStartAddress() >= other.getStartAddress()) && (this->getEndAddress() <= other.getEndAddress());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool overlaps(const Region &other) const {
|
||||
return (this->getEndAddress() >= other.getStartAddress()) && (this->getStartAddress() < other.getEndAddress());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 getStartAddress() const {
|
||||
return this->address;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 getEndAddress() const {
|
||||
return this->address + this->size - 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr size_t getSize() const {
|
||||
return this->size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
@@ -18,8 +19,6 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <nfd.hpp>
|
||||
|
||||
#define TOKEN_CONCAT_IMPL(x, y) x##y
|
||||
#define TOKEN_CONCAT(x, y) TOKEN_CONCAT_IMPL(x, y)
|
||||
#define ANONYMOUS_VARIABLE(prefix) TOKEN_CONCAT(prefix, __COUNTER__)
|
||||
@@ -44,9 +43,7 @@ namespace hex {
|
||||
std::string encodeByteString(const std::vector<u8> &bytes);
|
||||
std::vector<u8> decodeByteString(const std::string &string);
|
||||
|
||||
bool isPathWritable(fs::path path);
|
||||
|
||||
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const hex::unsigned_integral auto &value) {
|
||||
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const std::unsigned_integral auto &value) {
|
||||
if (from < to) std::swap(from, to);
|
||||
|
||||
using ValueType = std::remove_cvref_t<decltype(value)>;
|
||||
@@ -58,9 +55,6 @@ namespace hex {
|
||||
[[nodiscard]] inline u64 extract(u32 from, u32 to, const std::vector<u8> &bytes) {
|
||||
u8 index = 0;
|
||||
while (from > 32 && to > 32) {
|
||||
if (from - 8 < 0 || to - 8 < 0)
|
||||
return 0;
|
||||
|
||||
from -= 8;
|
||||
to -= 8;
|
||||
index++;
|
||||
@@ -78,6 +72,18 @@ namespace hex {
|
||||
return (value ^ mask) - mask;
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr inline T swapBitOrder(size_t numBits, T value) {
|
||||
T result = 0x00;
|
||||
|
||||
for (size_t bit = 0; bit < numBits; bit++) {
|
||||
result <<= 1;
|
||||
result |= (value & (1 << bit)) != 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template<class... Ts>
|
||||
@@ -132,7 +138,7 @@ namespace hex {
|
||||
swapped = (u128(__builtin_bswap64(unswapped & 0xFFFF'FFFF'FFFF'FFFF)) << 64) | __builtin_bswap64(u128(unswapped) >> 64);
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
hex::unreachable();
|
||||
}
|
||||
|
||||
T result;
|
||||
@@ -172,7 +178,7 @@ namespace hex {
|
||||
swapped = (u128(__builtin_bswap64(unswapped & 0xFFFF'FFFF'FFFF'FFFF)) << 64) | __builtin_bswap64(u128(unswapped) >> 64);
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
hex::unreachable();
|
||||
}
|
||||
|
||||
T result = 0;
|
||||
@@ -194,6 +200,24 @@ namespace hex {
|
||||
return T(1) << bit_width(T(x - 1));
|
||||
}
|
||||
|
||||
template<std::integral T, std::integral U>
|
||||
auto powi(T base, U exp) {
|
||||
using ResultType = decltype(T{} * U{});
|
||||
|
||||
if (exp < 0)
|
||||
return ResultType(0);
|
||||
|
||||
ResultType result = 1;
|
||||
|
||||
while (exp != 0) {
|
||||
if ((exp & 0b1) == 0b1)
|
||||
result *= base;
|
||||
exp >>= 1;
|
||||
base *= base;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void moveToVector(std::vector<T> &buffer, T &&first, Args &&...rest) {
|
||||
buffer.push_back(std::move(first));
|
||||
@@ -240,7 +264,7 @@ namespace hex {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string toBinaryString(hex::unsigned_integral auto number) {
|
||||
inline std::string toBinaryString(std::unsigned_integral auto number) {
|
||||
if (number == 0) return "0";
|
||||
|
||||
std::string result;
|
||||
@@ -268,15 +292,6 @@ namespace hex {
|
||||
trimRight(s);
|
||||
}
|
||||
|
||||
enum class DialogMode
|
||||
{
|
||||
Open,
|
||||
Save,
|
||||
Folder
|
||||
};
|
||||
|
||||
bool openFileBrowser(const std::string &title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(fs::path)> &callback, const std::string &defaultPath = {});
|
||||
|
||||
float float16ToFloat32(u16 float16);
|
||||
|
||||
inline bool equalsIgnoreCase(const std::string &left, const std::string &right) {
|
||||
@@ -302,12 +317,21 @@ namespace hex {
|
||||
return *value;
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
T alignTo(T value, T alignment) {
|
||||
T remainder = value % alignment;
|
||||
|
||||
return remainder != 0 ? value + (alignment - remainder) : value;
|
||||
}
|
||||
|
||||
bool isProcessElevated();
|
||||
|
||||
std::optional<std::string> getEnvironmentVariable(const std::string &env);
|
||||
|
||||
namespace scope_guard {
|
||||
|
||||
#define SCOPE_GUARD ::hex::scope_guard::ScopeGuardOnExit() + [&]()
|
||||
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_) = SCOPE_GUARD
|
||||
#define ON_SCOPE_EXIT [[maybe_unused]] auto ANONYMOUS_VARIABLE(SCOPE_EXIT_) = SCOPE_GUARD
|
||||
|
||||
template<class F>
|
||||
class ScopeGuard {
|
||||
@@ -316,7 +340,7 @@ namespace hex {
|
||||
bool m_active;
|
||||
|
||||
public:
|
||||
constexpr ScopeGuard(F func) : m_func(std::move(func)), m_active(true) { }
|
||||
explicit constexpr ScopeGuard(F func) : m_func(std::move(func)), m_active(true) { }
|
||||
~ScopeGuard() {
|
||||
if (this->m_active) { this->m_func(); }
|
||||
}
|
||||
@@ -342,12 +366,12 @@ namespace hex {
|
||||
|
||||
namespace first_time_exec {
|
||||
|
||||
#define FIRST_TIME static auto ANONYMOUS_VARIABLE(FIRST_TIME_) = ::hex::first_time_exec::FirstTimeExecutor() + [&]()
|
||||
#define FIRST_TIME [[maybe_unused]] static auto ANONYMOUS_VARIABLE(FIRST_TIME_) = ::hex::first_time_exec::FirstTimeExecutor() + [&]()
|
||||
|
||||
template<class F>
|
||||
class FirstTimeExecute {
|
||||
public:
|
||||
constexpr FirstTimeExecute(F func) { func(); }
|
||||
explicit constexpr FirstTimeExecute(F func) { func(); }
|
||||
|
||||
FirstTimeExecute &operator=(FirstTimeExecute &&) = delete;
|
||||
};
|
||||
@@ -365,14 +389,14 @@ namespace hex {
|
||||
|
||||
namespace final_cleanup {
|
||||
|
||||
#define FINAL_CLEANUP static auto ANONYMOUS_VARIABLE(ON_EXIT_) = ::hex::final_cleanup::FinalCleanupExecutor() + [&]()
|
||||
#define FINAL_CLEANUP [[maybe_unused]] static auto ANONYMOUS_VARIABLE(ON_EXIT_) = ::hex::final_cleanup::FinalCleanupExecutor() + [&]()
|
||||
|
||||
template<class F>
|
||||
class FinalCleanupExecute {
|
||||
F m_func;
|
||||
|
||||
public:
|
||||
constexpr FinalCleanupExecute(F func) : m_func(func) { }
|
||||
explicit constexpr FinalCleanupExecute(F func) : m_func(func) { }
|
||||
constexpr ~FinalCleanupExecute() { this->m_func(); }
|
||||
|
||||
FinalCleanupExecute &operator=(FinalCleanupExecute &&) = delete;
|
||||
|
||||
10
lib/libimhex/include/hex/helpers/utils_macos.hpp
Normal file
10
lib/libimhex/include/hex/helpers/utils_macos.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
#include <string>
|
||||
|
||||
extern "C" void openWebpageMacos(const char *url);
|
||||
extern "C" bool isMacosSystemDarkModeEnabled();
|
||||
|
||||
#endif
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Pattern;
|
||||
class Evaluator;
|
||||
|
||||
class ASTNode : public Cloneable<ASTNode> {
|
||||
public:
|
||||
constexpr ASTNode() = default;
|
||||
|
||||
constexpr virtual ~ASTNode() = default;
|
||||
|
||||
constexpr ASTNode(const ASTNode &) = default;
|
||||
|
||||
[[nodiscard]] constexpr u32 getLineNumber() const { return this->m_lineNumber; }
|
||||
|
||||
[[maybe_unused]] constexpr void setLineNumber(u32 lineNumber) { this->m_lineNumber = lineNumber; }
|
||||
|
||||
[[nodiscard]] virtual std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const { return this->clone(); }
|
||||
|
||||
[[nodiscard]] virtual std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const { return {}; }
|
||||
|
||||
using FunctionResult = std::optional<Token::Literal>;
|
||||
virtual FunctionResult execute(Evaluator *evaluator) const { LogConsole::abortEvaluation("cannot execute non-function statement", this); }
|
||||
|
||||
private:
|
||||
u32 m_lineNumber = 1;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_builtin_type.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_while_statement.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_padding.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeArrayVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeArrayVariableDecl(std::string name, std::unique_ptr<ASTNode> &&type, std::unique_ptr<ASTNode> &&size, std::unique_ptr<ASTNode> &&placementOffset = {})
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_size(std::move(size)), m_placementOffset(std::move(placementOffset)) { }
|
||||
|
||||
ASTNodeArrayVariableDecl(const ASTNodeArrayVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
if (other.m_size != nullptr)
|
||||
this->m_size = other.m_size->clone();
|
||||
else
|
||||
this->m_size = nullptr;
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeArrayVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
auto evaluatedPlacement = this->m_placementOffset->evaluate(evaluator);
|
||||
auto offset = dynamic_cast<ASTNodeLiteral *>(evaluatedPlacement.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return offset; } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
if (dynamic_cast<ASTNodeBuiltinType *>(type.get()))
|
||||
pattern = createStaticArray(evaluator);
|
||||
else if (auto attributable = dynamic_cast<Attributable *>(type.get())) {
|
||||
bool isStaticType = attributable->hasAttribute("static", false);
|
||||
|
||||
if (isStaticType)
|
||||
pattern = createStaticArray(evaluator);
|
||||
else
|
||||
pattern = createDynamicArray(evaluator);
|
||||
} else {
|
||||
LogConsole::abortEvaluation("invalid type used in array", this);
|
||||
}
|
||||
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
std::unique_ptr<ASTNode> m_size;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
|
||||
std::unique_ptr<Pattern> createStaticArray(Evaluator *evaluator) const {
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
auto templatePatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &templatePattern = templatePatterns.front();
|
||||
|
||||
evaluator->dataOffset() = startOffset;
|
||||
|
||||
i128 entryCount = 0;
|
||||
|
||||
if (this->m_size != nullptr) {
|
||||
auto sizeNode = this->m_size->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(sizeNode.get())) {
|
||||
entryCount = std::visit(overloaded {
|
||||
[this](const std::string &) -> i128 { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> i128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[](auto &&size) -> i128 { return size; } },
|
||||
literal->getValue());
|
||||
} else if (auto whileStatement = dynamic_cast<ASTNodeWhileStatement *>(sizeNode.get())) {
|
||||
while (whileStatement->evaluateCondition(evaluator)) {
|
||||
entryCount++;
|
||||
evaluator->dataOffset() += templatePattern->getSize();
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
}
|
||||
|
||||
if (entryCount < 0)
|
||||
LogConsole::abortEvaluation("array cannot have a negative size", this);
|
||||
} else {
|
||||
std::vector<u8> buffer(templatePattern->getSize());
|
||||
while (true) {
|
||||
if (evaluator->dataOffset() > evaluator->getProvider()->getActualSize() - buffer.size())
|
||||
LogConsole::abortEvaluation("reached end of file before finding end of unsized array", this);
|
||||
|
||||
evaluator->getProvider()->read(evaluator->dataOffset(), buffer.data(), buffer.size());
|
||||
evaluator->dataOffset() += buffer.size();
|
||||
|
||||
entryCount++;
|
||||
|
||||
bool reachedEnd = true;
|
||||
for (u8 &byte : buffer) {
|
||||
if (byte != 0x00) {
|
||||
reachedEnd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Pattern> outputPattern;
|
||||
if (dynamic_cast<PatternPadding *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternPadding(evaluator, startOffset, 0));
|
||||
} else if (dynamic_cast<PatternCharacter *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternString(evaluator, startOffset, 0));
|
||||
} else if (dynamic_cast<PatternWideCharacter *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternWideString(evaluator, startOffset, 0));
|
||||
} else {
|
||||
auto arrayPattern = std::make_unique<PatternArrayStatic>(evaluator, startOffset, 0);
|
||||
arrayPattern->setEntries(templatePattern->clone(), entryCount);
|
||||
outputPattern = std::move(arrayPattern);
|
||||
}
|
||||
|
||||
outputPattern->setVariableName(this->m_name);
|
||||
outputPattern->setEndian(templatePattern->getEndian());
|
||||
outputPattern->setTypeName(templatePattern->getTypeName());
|
||||
outputPattern->setSize(templatePattern->getSize() * entryCount);
|
||||
|
||||
evaluator->dataOffset() = startOffset + outputPattern->getSize();
|
||||
|
||||
return outputPattern;
|
||||
}
|
||||
|
||||
std::unique_ptr<Pattern> createDynamicArray(Evaluator *evaluator) const {
|
||||
auto arrayPattern = std::make_unique<PatternArrayDynamic>(evaluator, evaluator->dataOffset(), 0);
|
||||
arrayPattern->setVariableName(this->m_name);
|
||||
|
||||
std::vector<std::shared_ptr<Pattern>> entries;
|
||||
|
||||
size_t size = 0;
|
||||
u64 entryIndex = 0;
|
||||
|
||||
auto addEntries = [&](std::vector<std::unique_ptr<Pattern>> &&patterns) {
|
||||
for (auto &pattern : patterns) {
|
||||
pattern->setVariableName(hex::format("[{}]", entryIndex));
|
||||
pattern->setEndian(arrayPattern->getEndian());
|
||||
|
||||
size += pattern->getSize();
|
||||
entryIndex++;
|
||||
|
||||
entries.push_back(std::move(pattern));
|
||||
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
};
|
||||
|
||||
auto discardEntries = [&](u32 count) {
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
entries.pop_back();
|
||||
entryIndex--;
|
||||
}
|
||||
};
|
||||
|
||||
if (this->m_size != nullptr) {
|
||||
auto sizeNode = this->m_size->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(sizeNode.get())) {
|
||||
auto entryCount = std::visit(overloaded {
|
||||
[this](const std::string &) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[](auto &&size) -> u128 { return size; } },
|
||||
literal->getValue());
|
||||
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryCount > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
for (u64 i = 0; i < entryCount; i++) {
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
size_t patternCount = patterns.size();
|
||||
|
||||
if (!patterns.empty())
|
||||
addEntries(std::move(patterns));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
|
||||
discardEntries(patternCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (auto whileStatement = dynamic_cast<ASTNodeWhileStatement *>(sizeNode.get())) {
|
||||
while (whileStatement->evaluateCondition(evaluator)) {
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryIndex > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
size_t patternCount = patterns.size();
|
||||
|
||||
if (!patterns.empty())
|
||||
addEntries(std::move(patterns));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
discardEntries(patternCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (true) {
|
||||
bool reachedEnd = true;
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryIndex > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
|
||||
for (auto &pattern : patterns) {
|
||||
std::vector<u8> buffer(pattern->getSize());
|
||||
|
||||
if (evaluator->dataOffset() > evaluator->getProvider()->getActualSize() - buffer.size()) {
|
||||
LogConsole::abortEvaluation("reached end of file before finding end of unsized array", this);
|
||||
}
|
||||
|
||||
const auto patternSize = pattern->getSize();
|
||||
addEntries(hex::moveToVector(std::move(pattern)));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::None)
|
||||
break;
|
||||
|
||||
evaluator->getProvider()->read(evaluator->dataOffset() - patternSize, buffer.data(), buffer.size());
|
||||
reachedEnd = true;
|
||||
for (u8 &byte : buffer) {
|
||||
if (byte != 0x00) {
|
||||
reachedEnd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
}
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
discardEntries(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
}
|
||||
}
|
||||
|
||||
arrayPattern->setEntries(std::move(entries));
|
||||
|
||||
if (auto &arrayEntries = arrayPattern->getEntries(); !entries.empty())
|
||||
arrayPattern->setTypeName(arrayEntries.front()->getTypeName());
|
||||
|
||||
arrayPattern->setSize(size);
|
||||
|
||||
return std::move(arrayPattern);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeAssignment : public ASTNode {
|
||||
public:
|
||||
ASTNodeAssignment(std::string lvalueName, std::unique_ptr<ASTNode> &&rvalue) : m_lvalueName(std::move(lvalueName)), m_rvalue(std::move(rvalue)) {
|
||||
}
|
||||
|
||||
ASTNodeAssignment(const ASTNodeAssignment &other) : ASTNode(other) {
|
||||
this->m_lvalueName = other.m_lvalueName;
|
||||
this->m_rvalue = other.m_rvalue->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeAssignment(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getLValueName() const {
|
||||
return this->m_lvalueName;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getRValue() const {
|
||||
return this->m_rvalue;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
const auto node = this->getRValue()->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
if (this->getLValueName() == "$")
|
||||
evaluator->dataOffset() = Token::literalToUnsigned(literal->getValue());
|
||||
else
|
||||
evaluator->setVariable(this->getLValueName(), literal->getValue());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_lvalueName;
|
||||
std::unique_ptr<ASTNode> m_rvalue;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeAttribute : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeAttribute(std::string attribute, std::optional<std::string> value = std::nullopt)
|
||||
: ASTNode(), m_attribute(std::move(attribute)), m_value(std::move(value)) { }
|
||||
|
||||
~ASTNodeAttribute() override = default;
|
||||
|
||||
ASTNodeAttribute(const ASTNodeAttribute &other) : ASTNode(other) {
|
||||
this->m_attribute = other.m_attribute;
|
||||
this->m_value = other.m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeAttribute(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getAttribute() const {
|
||||
return this->m_attribute;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<std::string> &getValue() const {
|
||||
return this->m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_attribute;
|
||||
std::optional<std::string> m_value;
|
||||
};
|
||||
|
||||
|
||||
class Attributable {
|
||||
protected:
|
||||
Attributable() = default;
|
||||
|
||||
Attributable(const Attributable &other) {
|
||||
for (auto &attribute : other.m_attributes) {
|
||||
auto copy = attribute->clone();
|
||||
if (auto node = dynamic_cast<ASTNodeAttribute *>(copy.get()))
|
||||
this->m_attributes.push_back(std::unique_ptr<ASTNodeAttribute>(node));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void addAttribute(std::unique_ptr<ASTNodeAttribute> &&attribute) {
|
||||
this->m_attributes.push_back(std::move(attribute));
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getAttributes() const {
|
||||
return this->m_attributes;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasAttribute(const std::string &key, bool needsParameter) const {
|
||||
return std::any_of(this->m_attributes.begin(), this->m_attributes.end(), [&](const std::unique_ptr<ASTNodeAttribute> &attribute) {
|
||||
if (attribute->getAttribute() == key) {
|
||||
if (needsParameter && !attribute->getValue().has_value())
|
||||
LogConsole::abortEvaluation(hex::format("attribute '{}' expected a parameter"), attribute);
|
||||
else if (!needsParameter && attribute->getValue().has_value())
|
||||
LogConsole::abortEvaluation(hex::format("attribute '{}' did not expect a parameter "), attribute);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::string> getAttributeValue(const std::string &key) const {
|
||||
auto attribute = std::find_if(this->m_attributes.begin(), this->m_attributes.end(), [&](const std::unique_ptr<ASTNodeAttribute> &attribute) {
|
||||
return attribute->getAttribute() == key;
|
||||
});
|
||||
|
||||
if (attribute != this->m_attributes.end())
|
||||
return (*attribute)->getValue();
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ASTNodeAttribute>> m_attributes;
|
||||
};
|
||||
|
||||
|
||||
inline void applyTypeAttributes(Evaluator *evaluator, const ASTNode *node, Pattern *pattern) {
|
||||
auto attributable = dynamic_cast<const Attributable *>(node);
|
||||
if (attributable == nullptr)
|
||||
LogConsole::abortEvaluation("attribute cannot be applied here", node);
|
||||
|
||||
if (attributable->hasAttribute("inline", false)) {
|
||||
auto inlinable = dynamic_cast<Inlinable *>(pattern);
|
||||
|
||||
if (inlinable == nullptr)
|
||||
LogConsole::abortEvaluation("inline attribute can only be applied to nested types", node);
|
||||
else
|
||||
inlinable->setInlined(true);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("format"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find formatter function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("formatter function needs exactly one parameter", node);
|
||||
|
||||
pattern->setFormatterFunction(function);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("format_entries"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find formatter function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("formatter function needs exactly one parameter", node);
|
||||
|
||||
auto array = dynamic_cast<PatternArrayDynamic *>(pattern);
|
||||
if (array == nullptr)
|
||||
LogConsole::abortEvaluation("inline_array attribute can only be applied to array types", node);
|
||||
|
||||
for (const auto &entry : array->getEntries()) {
|
||||
entry->setFormatterFunction(function);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("transform"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find transform function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("transform function needs exactly one parameter", node);
|
||||
|
||||
pattern->setTransformFunction(function);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("pointer_base"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find pointer base function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("pointer base function needs exactly one parameter", node);
|
||||
|
||||
if (auto pointerPattern = dynamic_cast<PatternPointer *>(pattern)) {
|
||||
u128 pointerValue = pointerPattern->getPointedAtAddress();
|
||||
|
||||
auto result = function.func(evaluator, { pointerValue });
|
||||
|
||||
if (!result.has_value())
|
||||
LogConsole::abortEvaluation("pointer base function did not return a value", node);
|
||||
|
||||
pointerPattern->setPointedAtAddress(Token::literalToUnsigned(result.value()) + pointerValue);
|
||||
} else {
|
||||
LogConsole::abortEvaluation("pointer_base attribute may only be applied to a pointer");
|
||||
}
|
||||
}
|
||||
|
||||
if (attributable->hasAttribute("hidden", false)) {
|
||||
pattern->setHidden(true);
|
||||
}
|
||||
|
||||
if (!pattern->hasOverriddenColor()) {
|
||||
if (auto colorValue = attributable->getAttributeValue("color"); colorValue) {
|
||||
u32 color = strtoul(colorValue->c_str(), nullptr, 16);
|
||||
pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8);
|
||||
} else if (auto singleColor = attributable->hasAttribute("single_color", false); singleColor) {
|
||||
pattern->setColor(ContentRegistry::PatternLanguage::getNextColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void applyVariableAttributes(Evaluator *evaluator, const ASTNode *node, Pattern *pattern) {
|
||||
auto attributable = dynamic_cast<const Attributable *>(node);
|
||||
if (attributable == nullptr)
|
||||
LogConsole::abortEvaluation("attribute cannot be applied here", node);
|
||||
|
||||
auto endOffset = evaluator->dataOffset();
|
||||
evaluator->dataOffset() = pattern->getOffset();
|
||||
ON_SCOPE_EXIT { evaluator->dataOffset() = endOffset; };
|
||||
|
||||
applyTypeAttributes(evaluator, node, pattern);
|
||||
|
||||
if (auto colorValue = attributable->getAttributeValue("color"); colorValue) {
|
||||
u32 color = strtoul(colorValue->c_str(), nullptr, 16);
|
||||
pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8);
|
||||
} else if (auto singleColor = attributable->hasAttribute("single_color", false); singleColor) {
|
||||
pattern->setColor(ContentRegistry::PatternLanguage::getNextColor());
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("name"); value) {
|
||||
pattern->setDisplayName(*value);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("comment"); value) {
|
||||
pattern->setComment(*value);
|
||||
}
|
||||
|
||||
if (attributable->hasAttribute("no_unique_address", false)) {
|
||||
endOffset -= pattern->getSize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeBitfield : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeBitfield() : ASTNode() { }
|
||||
|
||||
ASTNodeBitfield(const ASTNodeBitfield &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace_back(name, entry->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeBitfield(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> &getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, std::unique_ptr<ASTNode> &&size) { this->m_entries.emplace_back(name, std::move(size)); }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternBitfield>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
size_t bitOffset = 0;
|
||||
std::vector<std::shared_ptr<Pattern>> fields;
|
||||
|
||||
BitfieldOrder order = evaluator->getBitfieldOrder();
|
||||
if (this->hasAttribute("left_to_right", false))
|
||||
order = BitfieldOrder::LeftToRight;
|
||||
else if (this->hasAttribute("right_to_left", false))
|
||||
order = BitfieldOrder::RightToLeft;
|
||||
|
||||
std::vector<std::pair<std::string, ASTNode *>> entries;
|
||||
for (const auto &[name, entry] : this->m_entries)
|
||||
entries.push_back({ name, entry.get() });
|
||||
|
||||
if (order == BitfieldOrder::LeftToRight)
|
||||
std::reverse(entries.begin(), entries.end());
|
||||
|
||||
evaluator->pushScope(pattern.get(), fields);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &[name, bitSizeNode] : entries) {
|
||||
auto literal = bitSizeNode->evaluate(evaluator);
|
||||
|
||||
u8 bitSize = std::visit(overloaded {
|
||||
[this](const std::string &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u8 { return static_cast<u8>(offset); } },
|
||||
dynamic_cast<ASTNodeLiteral *>(literal.get())->getValue());
|
||||
|
||||
// If a field is named padding, it was created through a padding expression and only advances the bit position
|
||||
if (name != "padding") {
|
||||
auto field = std::make_unique<PatternBitfieldField>(evaluator, evaluator->dataOffset(), bitOffset, bitSize, pattern.get());
|
||||
field->setVariableName(name);
|
||||
|
||||
fields.push_back(std::move(field));
|
||||
}
|
||||
|
||||
bitOffset += bitSize;
|
||||
}
|
||||
|
||||
pattern->setSize((bitOffset + 7) / 8);
|
||||
pattern->setFields(std::move(fields));
|
||||
|
||||
evaluator->dataOffset() += pattern->getSize();
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> m_entries;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_padding.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeBuiltinType : public ASTNode {
|
||||
public:
|
||||
constexpr explicit ASTNodeBuiltinType(Token::ValueType type)
|
||||
: ASTNode(), m_type(type) { }
|
||||
|
||||
[[nodiscard]] constexpr const auto &getType() const { return this->m_type; }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeBuiltinType(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto offset = evaluator->dataOffset();
|
||||
auto size = Token::getTypeSize(this->m_type);
|
||||
|
||||
evaluator->dataOffset() += size;
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
if (Token::isUnsigned(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternUnsigned(evaluator, offset, size));
|
||||
else if (Token::isSigned(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternSigned(evaluator, offset, size));
|
||||
else if (Token::isFloatingPoint(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternFloat(evaluator, offset, size));
|
||||
else if (this->m_type == Token::ValueType::Boolean)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternBoolean(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Character)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternCharacter(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Character16)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternWideCharacter(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Padding)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternPadding(evaluator, offset, 1));
|
||||
else if (this->m_type == Token::ValueType::String)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternString(evaluator, offset, 1));
|
||||
else if (this->m_type == Token::ValueType::Auto)
|
||||
return {};
|
||||
else
|
||||
LogConsole::abortEvaluation("invalid built-in type", this);
|
||||
|
||||
pattern->setTypeName(Token::getTypeName(this->m_type));
|
||||
|
||||
return hex::moveToVector(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
const Token::ValueType m_type;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeCast : public ASTNode {
|
||||
public:
|
||||
ASTNodeCast(std::unique_ptr<ASTNode> &&value, std::unique_ptr<ASTNode> &&type) : m_value(std::move(value)), m_type(std::move(type)) { }
|
||||
|
||||
ASTNodeCast(const ASTNodeCast &other) : ASTNode(other) {
|
||||
this->m_value = other.m_value->clone();
|
||||
this->m_type = other.m_type->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeCast(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto evaluatedValue = this->m_value->evaluate(evaluator);
|
||||
auto evaluatedType = this->m_type->evaluate(evaluator);
|
||||
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(evaluatedValue.get());
|
||||
auto type = dynamic_cast<ASTNodeBuiltinType *>(evaluatedType.get())->getType();
|
||||
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
auto typePatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &typePattern = typePatterns.front();
|
||||
|
||||
return std::unique_ptr<ASTNode>(std::visit(overloaded {
|
||||
[&, this](const std::shared_ptr<Pattern> &value) -> ASTNode * { LogConsole::abortEvaluation(hex::format("cannot cast custom type '{}' to '{}'", value->getTypeName(), Token::getTypeName(type)), this); },
|
||||
[&, this](const std::string &) -> ASTNode * { LogConsole::abortEvaluation(hex::format("cannot cast string to '{}'", Token::getTypeName(type)), this); },
|
||||
[&, this](auto &&value) -> ASTNode * {
|
||||
auto endianAdjustedValue = hex::changeEndianess(value, typePattern->getSize(), typePattern->getEndian());
|
||||
switch (type) {
|
||||
case Token::ValueType::Unsigned8Bit:
|
||||
return new ASTNodeLiteral(u128(u8(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned16Bit:
|
||||
return new ASTNodeLiteral(u128(u16(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned32Bit:
|
||||
return new ASTNodeLiteral(u128(u32(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned64Bit:
|
||||
return new ASTNodeLiteral(u128(u64(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned128Bit:
|
||||
return new ASTNodeLiteral(u128(endianAdjustedValue));
|
||||
case Token::ValueType::Signed8Bit:
|
||||
return new ASTNodeLiteral(i128(i8(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed16Bit:
|
||||
return new ASTNodeLiteral(i128(i16(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed32Bit:
|
||||
return new ASTNodeLiteral(i128(i32(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed64Bit:
|
||||
return new ASTNodeLiteral(i128(i64(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed128Bit:
|
||||
return new ASTNodeLiteral(i128(endianAdjustedValue));
|
||||
case Token::ValueType::Float:
|
||||
return new ASTNodeLiteral(double(float(endianAdjustedValue)));
|
||||
case Token::ValueType::Double:
|
||||
return new ASTNodeLiteral(double(endianAdjustedValue));
|
||||
case Token::ValueType::Character:
|
||||
return new ASTNodeLiteral(char(endianAdjustedValue));
|
||||
case Token::ValueType::Character16:
|
||||
return new ASTNodeLiteral(u128(char16_t(endianAdjustedValue)));
|
||||
case Token::ValueType::Boolean:
|
||||
return new ASTNodeLiteral(bool(endianAdjustedValue));
|
||||
case Token::ValueType::String:
|
||||
{
|
||||
std::string string(sizeof(value), '\x00');
|
||||
std::memcpy(string.data(), &value, string.size());
|
||||
hex::trim(string);
|
||||
|
||||
if (typePattern->getEndian() != std::endian::native)
|
||||
std::reverse(string.begin(), string.end());
|
||||
|
||||
return new ASTNodeLiteral(string);
|
||||
}
|
||||
default:
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast value to '{}'", Token::getTypeName(type)), this);
|
||||
}
|
||||
},
|
||||
},
|
||||
literal->getValue()));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_value;
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeCompoundStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeCompoundStatement(std::vector<std::unique_ptr<ASTNode>> &&statements, bool newScope = false) : m_statements(std::move(statements)), m_newScope(newScope) {
|
||||
}
|
||||
|
||||
ASTNodeCompoundStatement(const ASTNodeCompoundStatement &other) : ASTNode(other) {
|
||||
for (const auto &statement : other.m_statements) {
|
||||
this->m_statements.push_back(statement->clone());
|
||||
}
|
||||
|
||||
this->m_newScope = other.m_newScope;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeCompoundStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
std::unique_ptr<ASTNode> result = nullptr;
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
result = statement->evaluate(evaluator);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::unique_ptr<Pattern>> result;
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
auto patterns = statement->createPatterns(evaluator);
|
||||
std::move(patterns.begin(), patterns.end(), std::back_inserter(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
FunctionResult result;
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
u32 startVariableCount = variables.size();
|
||||
|
||||
if (this->m_newScope) {
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
}
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
result = statement->execute(evaluator);
|
||||
if (evaluator->getCurrentControlFlowStatement() != ControlFlowStatement::None)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this->m_newScope) {
|
||||
i64 stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
|
||||
evaluator->popScope();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::unique_ptr<ASTNode>> m_statements;
|
||||
bool m_newScope = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeConditionalStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeConditionalStatement(std::unique_ptr<ASTNode> condition, std::vector<std::unique_ptr<ASTNode>> &&trueBody, std::vector<std::unique_ptr<ASTNode>> &&falseBody)
|
||||
: ASTNode(), m_condition(std::move(condition)), m_trueBody(std::move(trueBody)), m_falseBody(std::move(falseBody)) { }
|
||||
|
||||
|
||||
ASTNodeConditionalStatement(const ASTNodeConditionalStatement &other) : ASTNode(other) {
|
||||
this->m_condition = other.m_condition->clone();
|
||||
|
||||
for (auto &statement : other.m_trueBody)
|
||||
this->m_trueBody.push_back(statement->clone());
|
||||
for (auto &statement : other.m_falseBody)
|
||||
this->m_falseBody.push_back(statement->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeConditionalStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto &scope = *evaluator->getScope(0).scope;
|
||||
auto &body = evaluateCondition(evaluator) ? this->m_trueBody : this->m_falseBody;
|
||||
|
||||
for (auto &node : body) {
|
||||
auto newPatterns = node->createPatterns(evaluator);
|
||||
for (auto &pattern : newPatterns) {
|
||||
scope.push_back(std::move(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getCondition() {
|
||||
return this->m_condition;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
auto &body = evaluateCondition(evaluator) ? this->m_trueBody : this->m_falseBody;
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
|
||||
u32 startVariableCount = variables.size();
|
||||
ON_SCOPE_EXIT {
|
||||
i64 stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
};
|
||||
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
evaluator->getScope(0).parameterPack = parameterPack;
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &statement : body) {
|
||||
auto result = statement->execute(evaluator);
|
||||
if (auto ctrlStatement = evaluator->getCurrentControlFlowStatement(); ctrlStatement != ControlFlowStatement::None) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool evaluateCondition(Evaluator *evaluator) const {
|
||||
const auto node = this->m_condition->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](Pattern *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return value != 0; } },
|
||||
literal->getValue());
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTNode> m_condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_trueBody, m_falseBody;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeControlFlowStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeControlFlowStatement(ControlFlowStatement type, std::unique_ptr<ASTNode> &&rvalue) : m_type(type), m_rvalue(std::move(rvalue)) {
|
||||
}
|
||||
|
||||
ASTNodeControlFlowStatement(const ASTNodeControlFlowStatement &other) : ASTNode(other) {
|
||||
this->m_type = other.m_type;
|
||||
|
||||
if (other.m_rvalue != nullptr)
|
||||
this->m_rvalue = other.m_rvalue->clone();
|
||||
else
|
||||
this->m_rvalue = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeControlFlowStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getReturnValue() const {
|
||||
return this->m_rvalue;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
auto returnValue = this->m_rvalue->evaluate(evaluator);
|
||||
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(returnValue.get());
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(this->m_type);
|
||||
|
||||
if (literal == nullptr)
|
||||
return std::nullopt;
|
||||
else
|
||||
return literal->getValue();
|
||||
}
|
||||
|
||||
private:
|
||||
ControlFlowStatement m_type;
|
||||
std::unique_ptr<ASTNode> m_rvalue;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeEnum : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
explicit ASTNodeEnum(std::unique_ptr<ASTNode> &&underlyingType) : ASTNode(), m_underlyingType(std::move(underlyingType)) { }
|
||||
|
||||
ASTNodeEnum(const ASTNodeEnum &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace(name, entry->clone());
|
||||
this->m_underlyingType = other.m_underlyingType->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeEnum(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternEnum>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
std::vector<std::pair<Token::Literal, std::string>> enumEntries;
|
||||
for (const auto &[name, value] : this->m_entries) {
|
||||
const auto node = value->evaluate(evaluator);
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
enumEntries.emplace_back(literal->getValue(), name);
|
||||
}
|
||||
|
||||
pattern->setEnumValues(enumEntries);
|
||||
|
||||
const auto nodes = this->m_underlyingType->createPatterns(evaluator);
|
||||
auto &underlying = nodes.front();
|
||||
|
||||
pattern->setSize(underlying->getSize());
|
||||
pattern->setEndian(underlying->getEndian());
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::map<std::string, std::unique_ptr<ASTNode>> &getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, std::unique_ptr<ASTNode> &&expression) { this->m_entries.insert({ name, std::move(expression) }); }
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getUnderlyingType() { return this->m_underlyingType; }
|
||||
|
||||
private:
|
||||
std::map<std::string, std::unique_ptr<ASTNode>> m_entries;
|
||||
std::unique_ptr<ASTNode> m_underlyingType;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_parameter_pack.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_mathematical_expression.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeFunctionCall : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeFunctionCall(std::string functionName, std::vector<std::unique_ptr<ASTNode>> &¶ms)
|
||||
: ASTNode(), m_functionName(std::move(functionName)), m_params(std::move(params)) { }
|
||||
|
||||
ASTNodeFunctionCall(const ASTNodeFunctionCall &other) : ASTNode(other) {
|
||||
this->m_functionName = other.m_functionName;
|
||||
|
||||
for (auto ¶m : other.m_params)
|
||||
this->m_params.push_back(param->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeFunctionCall(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getFunctionName() {
|
||||
return this->m_functionName;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getParams() const {
|
||||
return this->m_params;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
std::vector<Token::Literal> evaluatedParams;
|
||||
for (auto ¶m : this->m_params) {
|
||||
const auto expression = param->evaluate(evaluator)->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(expression.get())) {
|
||||
evaluatedParams.push_back(literal->getValue());
|
||||
} else if (auto parameterPack = dynamic_cast<ASTNodeParameterPack *>(expression.get())) {
|
||||
for (auto &value : parameterPack->getValues()) {
|
||||
evaluatedParams.push_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto &customFunctions = evaluator->getCustomFunctions();
|
||||
auto functions = ContentRegistry::PatternLanguage::getFunctions();
|
||||
|
||||
for (auto &func : customFunctions)
|
||||
functions.insert(func);
|
||||
|
||||
if (!functions.contains(this->m_functionName)) {
|
||||
if (this->m_functionName.starts_with("std::")) {
|
||||
evaluator->getConsole().log(LogConsole::Level::Warning, "This function might be part of the standard library.\nYou can install the standard library though\nthe Content Store found under Help -> Content Store and then\ninclude the correct file.");
|
||||
}
|
||||
|
||||
LogConsole::abortEvaluation(hex::format("call to unknown function '{}'", this->m_functionName), this);
|
||||
}
|
||||
|
||||
auto function = functions[this->m_functionName];
|
||||
if (function.parameterCount == ContentRegistry::PatternLanguage::UnlimitedParameters) {
|
||||
; // Don't check parameter count
|
||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::LessParametersThan) {
|
||||
if (evaluatedParams.size() >= (function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan))
|
||||
LogConsole::abortEvaluation(hex::format("too many parameters for function '{0}'. Expected less than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan), this);
|
||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::MoreParametersThan) {
|
||||
if (evaluatedParams.size() <= (function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan))
|
||||
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan), this);
|
||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan) {
|
||||
if (evaluatedParams.size() < (function.parameterCount & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan))
|
||||
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, (function.parameterCount - 1) & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan), this);
|
||||
} else if (function.parameterCount != evaluatedParams.size()) {
|
||||
LogConsole::abortEvaluation(hex::format("invalid number of parameters for function '{0}'. Expected {1}", this->m_functionName, function.parameterCount), this);
|
||||
}
|
||||
|
||||
try {
|
||||
if (function.dangerous && evaluator->getDangerousFunctionPermission() != DangerousFunctionPermission::Allow) {
|
||||
evaluator->dangerousFunctionCalled();
|
||||
|
||||
while (evaluator->getDangerousFunctionPermission() == DangerousFunctionPermission::Ask) {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
if (evaluator->getDangerousFunctionPermission() == DangerousFunctionPermission::Deny) {
|
||||
LogConsole::abortEvaluation(hex::format("calling of dangerous function '{}' is not allowed", this->m_functionName), this);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = function.func(evaluator, evaluatedParams);
|
||||
|
||||
if (result.has_value())
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(std::move(result.value())));
|
||||
else
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMathematicalExpression(nullptr, nullptr, Token::Operator::Plus));
|
||||
} catch (std::string &error) {
|
||||
LogConsole::abortEvaluation(error, this);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
(void)this->evaluate(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_functionName;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_params;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeFunctionDefinition : public ASTNode {
|
||||
public:
|
||||
ASTNodeFunctionDefinition(std::string name, std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> &¶ms, std::vector<std::unique_ptr<ASTNode>> &&body, std::optional<std::string> parameterPack)
|
||||
: m_name(std::move(name)), m_params(std::move(params)), m_body(std::move(body)), m_parameterPack(std::move(parameterPack)) {
|
||||
}
|
||||
|
||||
ASTNodeFunctionDefinition(const ASTNodeFunctionDefinition &other) : ASTNode(other) {
|
||||
this->m_name = other.m_name;
|
||||
|
||||
for (const auto &[name, type] : other.m_params) {
|
||||
this->m_params.emplace_back(name, type->clone());
|
||||
}
|
||||
|
||||
for (auto &statement : other.m_body) {
|
||||
this->m_body.push_back(statement->clone());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeFunctionDefinition(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const {
|
||||
return this->m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getParams() const {
|
||||
return this->m_params;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getBody() const {
|
||||
return this->m_body;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
|
||||
size_t paramCount = this->m_params.size();
|
||||
|
||||
if (this->m_parameterPack.has_value())
|
||||
paramCount |= ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan;
|
||||
|
||||
evaluator->addCustomFunction(this->m_name, paramCount, [this](Evaluator *ctx, const std::vector<Token::Literal> ¶ms) -> std::optional<Token::Literal> {
|
||||
std::vector<std::shared_ptr<Pattern>> variables;
|
||||
|
||||
ctx->pushScope(nullptr, variables);
|
||||
ON_SCOPE_EXIT {
|
||||
ctx->popScope();
|
||||
};
|
||||
|
||||
if (this->m_parameterPack.has_value()) {
|
||||
std::vector<Token::Literal> parameterPackContent;
|
||||
for (u32 paramIndex = this->m_params.size(); paramIndex < params.size(); paramIndex++)
|
||||
parameterPackContent.push_back(params[paramIndex]);
|
||||
|
||||
ctx->createParameterPack(this->m_parameterPack.value(), parameterPackContent);
|
||||
}
|
||||
|
||||
for (u32 paramIndex = 0; paramIndex < this->m_params.size(); paramIndex++) {
|
||||
const auto &[name, type] = this->m_params[paramIndex];
|
||||
|
||||
ctx->createVariable(name, type.get(), params[paramIndex]);
|
||||
ctx->setVariable(name, params[paramIndex]);
|
||||
}
|
||||
|
||||
for (auto &statement : this->m_body) {
|
||||
auto result = statement->execute(ctx);
|
||||
|
||||
if (ctx->getCurrentControlFlowStatement() != ControlFlowStatement::None) {
|
||||
switch (ctx->getCurrentControlFlowStatement()) {
|
||||
case ControlFlowStatement::Break:
|
||||
LogConsole::abortEvaluation("break statement not within a loop", statement);
|
||||
case ControlFlowStatement::Continue:
|
||||
LogConsole::abortEvaluation("continue statement not within a loop", statement);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> m_params;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_body;
|
||||
std::optional<std::string> m_parameterPack;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeLiteral : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeLiteral(Token::Literal literal) : ASTNode(), m_literal(std::move(literal)) { }
|
||||
|
||||
ASTNodeLiteral(const ASTNodeLiteral &) = default;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getValue() const {
|
||||
return this->m_literal;
|
||||
}
|
||||
|
||||
private:
|
||||
Token::Literal m_literal;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
#define FLOAT_BIT_OPERATION(name) \
|
||||
auto name(hex::floating_point auto left, auto right) const { \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(auto left, hex::floating_point auto right) const { \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(hex::floating_point auto left, hex::floating_point auto right) const { \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(hex::integral auto left, hex::integral auto right) const
|
||||
|
||||
class ASTNodeMathematicalExpression : public ASTNode {
|
||||
|
||||
FLOAT_BIT_OPERATION(shiftLeft) {
|
||||
return left << right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(shiftRight) {
|
||||
return left >> right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitAnd) {
|
||||
return left & right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitOr) {
|
||||
return left | right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitXor) {
|
||||
return left ^ right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitNot) {
|
||||
return ~right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(modulus) {
|
||||
return left % right;
|
||||
}
|
||||
|
||||
#undef FLOAT_BIT_OPERATION
|
||||
public:
|
||||
ASTNodeMathematicalExpression(std::unique_ptr<ASTNode> &&left, std::unique_ptr<ASTNode> &&right, Token::Operator op)
|
||||
: ASTNode(), m_left(std::move(left)), m_right(std::move(right)), m_operator(op) { }
|
||||
|
||||
ASTNodeMathematicalExpression(const ASTNodeMathematicalExpression &other) : ASTNode(other) {
|
||||
this->m_operator = other.m_operator;
|
||||
this->m_left = other.m_left->clone();
|
||||
this->m_right = other.m_right->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMathematicalExpression(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getLeftOperand() == nullptr || this->getRightOperand() == nullptr)
|
||||
LogConsole::abortEvaluation("attempted to use void expression in mathematical expression", this);
|
||||
|
||||
auto leftValue = this->getLeftOperand()->evaluate(evaluator);
|
||||
auto rightValue = this->getRightOperand()->evaluate(evaluator);
|
||||
|
||||
auto *left = dynamic_cast<ASTNodeLiteral *>(leftValue.get());
|
||||
auto *right = dynamic_cast<ASTNodeLiteral *>(rightValue.get());
|
||||
|
||||
return std::unique_ptr<ASTNode>(std::visit(overloaded {
|
||||
// TODO: :notlikethis:
|
||||
[this](u128 left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](i128 left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](double left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](char left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](bool left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::string &left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, u128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, i128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, double right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, char right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, bool right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
|
||||
[this](auto &&left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::string &left, auto &&right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Star:
|
||||
{
|
||||
std::string result;
|
||||
for (auto i = 0; i < right; i++)
|
||||
result += left;
|
||||
return new ASTNodeLiteral(result);
|
||||
}
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](const std::string &left, const std::string &right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
case Token::Operator::BoolEquals:
|
||||
return new ASTNodeLiteral(left == right);
|
||||
case Token::Operator::BoolNotEquals:
|
||||
return new ASTNodeLiteral(left != right);
|
||||
case Token::Operator::BoolGreaterThan:
|
||||
return new ASTNodeLiteral(left > right);
|
||||
case Token::Operator::BoolLessThan:
|
||||
return new ASTNodeLiteral(left < right);
|
||||
case Token::Operator::BoolGreaterThanOrEquals:
|
||||
return new ASTNodeLiteral(left >= right);
|
||||
case Token::Operator::BoolLessThanOrEquals:
|
||||
return new ASTNodeLiteral(left <= right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](const std::string &left, char right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](char left, const std::string &right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](auto &&left, auto &&right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
case Token::Operator::Minus:
|
||||
return new ASTNodeLiteral(left - right);
|
||||
case Token::Operator::Star:
|
||||
return new ASTNodeLiteral(left * right);
|
||||
case Token::Operator::Slash:
|
||||
if (right == 0) LogConsole::abortEvaluation("division by zero!", this);
|
||||
return new ASTNodeLiteral(left / right);
|
||||
case Token::Operator::Percent:
|
||||
if (right == 0) LogConsole::abortEvaluation("division by zero!", this);
|
||||
return new ASTNodeLiteral(modulus(left, right));
|
||||
case Token::Operator::ShiftLeft:
|
||||
return new ASTNodeLiteral(shiftLeft(left, right));
|
||||
case Token::Operator::ShiftRight:
|
||||
return new ASTNodeLiteral(shiftRight(left, right));
|
||||
case Token::Operator::BitAnd:
|
||||
return new ASTNodeLiteral(bitAnd(left, right));
|
||||
case Token::Operator::BitXor:
|
||||
return new ASTNodeLiteral(bitXor(left, right));
|
||||
case Token::Operator::BitOr:
|
||||
return new ASTNodeLiteral(bitOr(left, right));
|
||||
case Token::Operator::BitNot:
|
||||
return new ASTNodeLiteral(bitNot(left, right));
|
||||
case Token::Operator::BoolEquals:
|
||||
return new ASTNodeLiteral(bool(left == right));
|
||||
case Token::Operator::BoolNotEquals:
|
||||
return new ASTNodeLiteral(bool(left != right));
|
||||
case Token::Operator::BoolGreaterThan:
|
||||
return new ASTNodeLiteral(bool(left > right));
|
||||
case Token::Operator::BoolLessThan:
|
||||
return new ASTNodeLiteral(bool(left < right));
|
||||
case Token::Operator::BoolGreaterThanOrEquals:
|
||||
return new ASTNodeLiteral(bool(left >= right));
|
||||
case Token::Operator::BoolLessThanOrEquals:
|
||||
return new ASTNodeLiteral(bool(left <= right));
|
||||
case Token::Operator::BoolAnd:
|
||||
return new ASTNodeLiteral(bool(left && right));
|
||||
case Token::Operator::BoolXor:
|
||||
return new ASTNodeLiteral(bool(left && !right || !left && right));
|
||||
case Token::Operator::BoolOr:
|
||||
return new ASTNodeLiteral(bool(left || right));
|
||||
case Token::Operator::BoolNot:
|
||||
return new ASTNodeLiteral(bool(!right));
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
} },
|
||||
left->getValue(),
|
||||
right->getValue()));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getLeftOperand() const { return this->m_left; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getRightOperand() const { return this->m_right; }
|
||||
[[nodiscard]] Token::Operator getOperator() const { return this->m_operator; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_left, m_right;
|
||||
Token::Operator m_operator;
|
||||
};
|
||||
|
||||
#undef FLOAT_BIT_OPERATION
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeMultiVariableDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeMultiVariableDecl(std::vector<std::unique_ptr<ASTNode>> &&variables) : m_variables(std::move(variables)) { }
|
||||
|
||||
ASTNodeMultiVariableDecl(const ASTNodeMultiVariableDecl &other) : ASTNode(other) {
|
||||
for (auto &variable : other.m_variables)
|
||||
this->m_variables.push_back(variable->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMultiVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getVariables() {
|
||||
return this->m_variables;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::unique_ptr<Pattern>> patterns;
|
||||
|
||||
for (auto &node : this->m_variables) {
|
||||
auto newPatterns = node->createPatterns(evaluator);
|
||||
std::move(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
for (auto &variable : this->m_variables) {
|
||||
auto variableDecl = dynamic_cast<ASTNodeVariableDecl *>(variable.get());
|
||||
auto variableType = variableDecl->getType()->evaluate(evaluator);
|
||||
|
||||
evaluator->createVariable(variableDecl->getName(), variableType.get());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ASTNode>> m_variables;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeParameterPack : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeParameterPack(std::vector<Token::Literal> &&values) : m_values(std::move(values)) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeParameterPack(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<Token::Literal> &getValues() const {
|
||||
return this->m_values;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Token::Literal> m_values;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodePointerVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodePointerVariableDecl(std::string name, std::shared_ptr<ASTNode> &&type, std::shared_ptr<ASTNode> &&sizeType, std::unique_ptr<ASTNode> &&placementOffset = nullptr)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_sizeType(std::move(sizeType)), m_placementOffset(std::move(placementOffset)) { }
|
||||
|
||||
ASTNodePointerVariableDecl(const ASTNodePointerVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_sizeType = other.m_sizeType->clone();
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodePointerVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr const std::shared_ptr<ASTNode> &getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr const std::shared_ptr<ASTNode> &getSizeType() const { return this->m_sizeType; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
const auto node = this->m_placementOffset->evaluate(evaluator);
|
||||
const auto offset = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return u64(offset); } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto pointerStartOffset = evaluator->dataOffset();
|
||||
|
||||
const auto sizePatterns = this->m_sizeType->createPatterns(evaluator);
|
||||
const auto &sizePattern = sizePatterns.front();
|
||||
|
||||
auto pattern = std::make_unique<PatternPointer>(evaluator, pointerStartOffset, sizePattern->getSize());
|
||||
pattern->setVariableName(this->m_name);
|
||||
|
||||
auto pointerEndOffset = evaluator->dataOffset();
|
||||
|
||||
{
|
||||
u128 pointerAddress = 0;
|
||||
evaluator->getProvider()->read(pattern->getOffset(), &pointerAddress, pattern->getSize());
|
||||
pointerAddress = hex::changeEndianess(pointerAddress, sizePattern->getSize(), sizePattern->getEndian());
|
||||
|
||||
evaluator->dataOffset() = pointerStartOffset;
|
||||
|
||||
pattern->setPointedAtAddress(pointerAddress);
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
evaluator->dataOffset() = pattern->getPointedAtAddress();
|
||||
|
||||
auto pointedAtPatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &pointedAtPattern = pointedAtPatterns.front();
|
||||
|
||||
pattern->setPointedAtPattern(std::move(pointedAtPattern));
|
||||
pattern->setEndian(sizePattern->getEndian());
|
||||
}
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
} else {
|
||||
evaluator->dataOffset() = pointerEndOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNode> m_type;
|
||||
std::shared_ptr<ASTNode> m_sizeType;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_parameter_pack.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_struct.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_union.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeRValue : public ASTNode {
|
||||
public:
|
||||
using PathSegment = std::variant<std::string, std::unique_ptr<ASTNode>>;
|
||||
using Path = std::vector<PathSegment>;
|
||||
|
||||
explicit ASTNodeRValue(Path &&path) : ASTNode(), m_path(std::move(path)) { }
|
||||
|
||||
ASTNodeRValue(const ASTNodeRValue &other) : ASTNode(other) {
|
||||
for (auto &part : other.m_path) {
|
||||
if (auto stringPart = std::get_if<std::string>(&part); stringPart != nullptr)
|
||||
this->m_path.push_back(*stringPart);
|
||||
else if (auto nodePart = std::get_if<std::unique_ptr<ASTNode>>(&part); nodePart != nullptr)
|
||||
this->m_path.push_back((*nodePart)->clone());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeRValue(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const Path &getPath() const {
|
||||
return this->m_path;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getPath().size() == 1) {
|
||||
if (auto name = std::get_if<std::string>(&this->getPath().front()); name != nullptr) {
|
||||
if (*name == "$") return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(evaluator->dataOffset())));
|
||||
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
if (parameterPack && *name == parameterPack->name)
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeParameterPack(std::move(parameterPack->values)));
|
||||
}
|
||||
}
|
||||
|
||||
auto patterns = this->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
|
||||
Token::Literal literal;
|
||||
if (dynamic_cast<PatternUnsigned *>(pattern.get()) || dynamic_cast<PatternEnum *>(pattern.get())) {
|
||||
u128 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternSigned *>(pattern.get())) {
|
||||
i128 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
value = hex::signExtend(pattern->getSize() * 8, value);
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternFloat *>(pattern.get())) {
|
||||
if (pattern->getSize() == sizeof(u16)) {
|
||||
u16 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = double(float16ToFloat32(value));
|
||||
} else if (pattern->getSize() == sizeof(float)) {
|
||||
float value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = double(value);
|
||||
} else if (pattern->getSize() == sizeof(double)) {
|
||||
double value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else LogConsole::abortEvaluation("invalid floating point type access", this);
|
||||
} else if (dynamic_cast<PatternCharacter *>(pattern.get())) {
|
||||
char value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternBoolean *>(pattern.get())) {
|
||||
bool value = false;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternString *>(pattern.get())) {
|
||||
std::string value;
|
||||
|
||||
if (pattern->isLocal()) {
|
||||
auto &variableValue = evaluator->getStack()[pattern->getOffset()];
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](char assignmentValue) { if (assignmentValue != 0x00) value = std::string({ assignmentValue }); },
|
||||
[&](std::string &assignmentValue) { value = assignmentValue; },
|
||||
[&, this](Pattern *const &assignmentValue) {
|
||||
if (!dynamic_cast<PatternString *>(assignmentValue) && !dynamic_cast<PatternCharacter *>(assignmentValue))
|
||||
LogConsole::abortEvaluation(hex::format("cannot assign '{}' to string", pattern->getTypeName()), this);
|
||||
|
||||
readVariable(evaluator, value, assignmentValue);
|
||||
},
|
||||
[&, this](auto &&assignmentValue) { LogConsole::abortEvaluation(hex::format("cannot assign '{}' to string", pattern->getTypeName()), this); } },
|
||||
variableValue);
|
||||
} else {
|
||||
value.resize(pattern->getSize());
|
||||
evaluator->getProvider()->read(pattern->getOffset(), value.data(), value.size());
|
||||
value.erase(std::find(value.begin(), value.end(), '\0'), value.end());
|
||||
}
|
||||
|
||||
literal = value;
|
||||
} else if (auto bitfieldFieldPattern = dynamic_cast<PatternBitfieldField *>(pattern.get())) {
|
||||
u64 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = u128(hex::extract(bitfieldFieldPattern->getBitOffset() + (bitfieldFieldPattern->getBitSize() - 1), bitfieldFieldPattern->getBitOffset(), value));
|
||||
} else {
|
||||
literal = pattern->clone();
|
||||
}
|
||||
|
||||
if (auto transformFunc = pattern->getTransformFunction(); transformFunc.has_value()) {
|
||||
auto result = transformFunc->func(evaluator, { std::move(literal) });
|
||||
|
||||
if (!result.has_value())
|
||||
LogConsole::abortEvaluation("transform function did not return a value", this);
|
||||
literal = std::move(result.value());
|
||||
}
|
||||
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(std::move(literal)));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::shared_ptr<Pattern>> searchScope;
|
||||
std::unique_ptr<Pattern> currPattern;
|
||||
i32 scopeIndex = 0;
|
||||
|
||||
|
||||
if (!evaluator->isGlobalScope()) {
|
||||
auto globalScope = evaluator->getGlobalScope().scope;
|
||||
std::copy(globalScope->begin(), globalScope->end(), std::back_inserter(searchScope));
|
||||
}
|
||||
|
||||
{
|
||||
auto currScope = evaluator->getScope(scopeIndex).scope;
|
||||
std::copy(currScope->begin(), currScope->end(), std::back_inserter(searchScope));
|
||||
}
|
||||
|
||||
for (const auto &part : this->getPath()) {
|
||||
|
||||
if (part.index() == 0) {
|
||||
// Variable access
|
||||
auto name = std::get<std::string>(part);
|
||||
|
||||
if (name == "parent") {
|
||||
scopeIndex--;
|
||||
|
||||
if (-scopeIndex >= evaluator->getScopeCount())
|
||||
LogConsole::abortEvaluation("cannot access parent of global scope", this);
|
||||
|
||||
searchScope = *evaluator->getScope(scopeIndex).scope;
|
||||
auto currParent = evaluator->getScope(scopeIndex).parent;
|
||||
|
||||
if (currParent == nullptr) {
|
||||
currPattern = nullptr;
|
||||
} else {
|
||||
currPattern = currParent->clone();
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (name == "this") {
|
||||
searchScope = *evaluator->getScope(scopeIndex).scope;
|
||||
|
||||
auto currParent = evaluator->getScope(0).parent;
|
||||
|
||||
if (currParent == nullptr)
|
||||
LogConsole::abortEvaluation("invalid use of 'this' outside of struct-like type", this);
|
||||
|
||||
currPattern = currParent->clone();
|
||||
continue;
|
||||
} else {
|
||||
bool found = false;
|
||||
for (auto iter = searchScope.crbegin(); iter != searchScope.crend(); ++iter) {
|
||||
if ((*iter)->getVariableName() == name) {
|
||||
currPattern = (*iter)->clone();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "$")
|
||||
LogConsole::abortEvaluation("invalid use of placeholder operator in rvalue");
|
||||
|
||||
if (!found) {
|
||||
LogConsole::abortEvaluation(hex::format("no variable named '{}' found", name), this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Array indexing
|
||||
auto node = std::get<std::unique_ptr<ASTNode>>(part)->evaluate(evaluator);
|
||||
auto index = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
std::visit(overloaded {
|
||||
[this](const std::string &) { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[&, this](auto &&index) {
|
||||
if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(currPattern.get())) {
|
||||
if (index >= searchScope.size() || index < 0)
|
||||
LogConsole::abortEvaluation("array index out of bounds", this);
|
||||
|
||||
currPattern = searchScope[index]->clone();
|
||||
} else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(currPattern.get())) {
|
||||
if (index >= staticArrayPattern->getEntryCount() || index < 0)
|
||||
LogConsole::abortEvaluation("array index out of bounds", this);
|
||||
|
||||
auto newPattern = searchScope.front()->clone();
|
||||
newPattern->setOffset(staticArrayPattern->getOffset() + index * staticArrayPattern->getTemplate()->getSize());
|
||||
currPattern = std::move(newPattern);
|
||||
}
|
||||
} },
|
||||
index->getValue());
|
||||
}
|
||||
|
||||
if (currPattern == nullptr)
|
||||
break;
|
||||
|
||||
if (auto pointerPattern = dynamic_cast<PatternPointer *>(currPattern.get())) {
|
||||
currPattern = pointerPattern->getPointedAtPattern()->clone();
|
||||
}
|
||||
|
||||
std::shared_ptr<Pattern> indexPattern;
|
||||
if (currPattern->isLocal()) {
|
||||
auto stackLiteral = evaluator->getStack()[currPattern->getOffset()];
|
||||
if (auto stackPattern = std::get_if<std::shared_ptr<Pattern>>(&stackLiteral); stackPattern != nullptr)
|
||||
indexPattern = *stackPattern;
|
||||
else
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
|
||||
} else
|
||||
indexPattern = currPattern->clone();
|
||||
|
||||
if (auto structPattern = dynamic_cast<PatternStruct *>(indexPattern.get()))
|
||||
searchScope = structPattern->getMembers();
|
||||
else if (auto unionPattern = dynamic_cast<PatternUnion *>(indexPattern.get()))
|
||||
searchScope = unionPattern->getMembers();
|
||||
else if (auto bitfieldPattern = dynamic_cast<PatternBitfield *>(indexPattern.get()))
|
||||
searchScope = bitfieldPattern->getFields();
|
||||
else if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(indexPattern.get()))
|
||||
searchScope = dynamicArrayPattern->getEntries();
|
||||
else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(indexPattern.get()))
|
||||
searchScope = { staticArrayPattern->getTemplate() };
|
||||
}
|
||||
|
||||
if (currPattern == nullptr)
|
||||
LogConsole::abortEvaluation("cannot reference global scope", this);
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
|
||||
}
|
||||
|
||||
private:
|
||||
Path m_path;
|
||||
|
||||
void readVariable(Evaluator *evaluator, auto &value, Pattern *variablePattern) const {
|
||||
constexpr bool isString = std::same_as<std::remove_cvref_t<decltype(value)>, std::string>;
|
||||
|
||||
if (variablePattern->isLocal()) {
|
||||
auto &literal = evaluator->getStack()[variablePattern->getOffset()];
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](std::string &assignmentValue) {
|
||||
if constexpr (isString) value = assignmentValue;
|
||||
},
|
||||
[&](std::shared_ptr<Pattern> &assignmentValue) { readVariable(evaluator, value, assignmentValue.get()); },
|
||||
[&](auto &&assignmentValue) { value = assignmentValue; } },
|
||||
literal);
|
||||
} else {
|
||||
if constexpr (isString) {
|
||||
value.resize(variablePattern->getSize());
|
||||
evaluator->getProvider()->read(variablePattern->getOffset(), value.data(), value.size());
|
||||
value.erase(std::find(value.begin(), value.end(), '\0'), value.end());
|
||||
} else {
|
||||
evaluator->getProvider()->read(variablePattern->getOffset(), &value, variablePattern->getSize());
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (!isString)
|
||||
value = hex::changeEndianess(value, variablePattern->getSize(), variablePattern->getEndian());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeScopeResolution : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeScopeResolution(std::unique_ptr<ASTNode> &&type, std::string name) : ASTNode(), m_type(std::move(type)), m_name(std::move(name)) { }
|
||||
|
||||
ASTNodeScopeResolution(const ASTNodeScopeResolution &other) : ASTNode(other) {
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_name = other.m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeScopeResolution(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
if (auto enumType = dynamic_cast<ASTNodeEnum *>(type.get())) {
|
||||
for (auto &[name, value] : enumType->getEntries()) {
|
||||
if (name == this->m_name)
|
||||
return value->evaluate(evaluator);
|
||||
}
|
||||
} else {
|
||||
LogConsole::abortEvaluation("invalid scope resolution. Cannot access this type");
|
||||
}
|
||||
|
||||
LogConsole::abortEvaluation(hex::format("could not find constant '{}'", this->m_name), this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_struct.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeStruct : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeStruct() : ASTNode() { }
|
||||
|
||||
ASTNodeStruct(const ASTNodeStruct &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
for (const auto &otherInheritance : other.getInheritance())
|
||||
this->m_inheritance.push_back(otherInheritance->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeStruct(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternStruct>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
std::vector<std::shared_ptr<Pattern>> memberPatterns;
|
||||
|
||||
evaluator->pushScope(pattern.get(), memberPatterns);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &inheritance : this->m_inheritance) {
|
||||
auto inheritancePatterns = inheritance->createPatterns(evaluator);
|
||||
auto &inheritancePattern = inheritancePatterns.front();
|
||||
|
||||
if (auto structPattern = dynamic_cast<PatternStruct *>(inheritancePattern.get())) {
|
||||
for (auto &member : structPattern->getMembers()) {
|
||||
memberPatterns.push_back(member->clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &member : this->m_members) {
|
||||
for (auto &memberPattern : member->createPatterns(evaluator)) {
|
||||
memberPatterns.push_back(std::move(memberPattern));
|
||||
}
|
||||
}
|
||||
|
||||
pattern->setMembers(std::move(memberPatterns));
|
||||
pattern->setSize(evaluator->dataOffset() - startOffset);
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getMembers() const { return this->m_members; }
|
||||
void addMember(std::shared_ptr<ASTNode> &&node) { this->m_members.push_back(std::move(node)); }
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getInheritance() const { return this->m_inheritance; }
|
||||
void addInheritance(std::shared_ptr<ASTNode> &&node) { this->m_inheritance.push_back(std::move(node)); }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<ASTNode>> m_members;
|
||||
std::vector<std::shared_ptr<ASTNode>> m_inheritance;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTernaryExpression : public ASTNode {
|
||||
public:
|
||||
ASTNodeTernaryExpression(std::unique_ptr<ASTNode> &&first, std::unique_ptr<ASTNode> &&second, std::unique_ptr<ASTNode> &&third, Token::Operator op)
|
||||
: ASTNode(), m_first(std::move(first)), m_second(std::move(second)), m_third(std::move(third)), m_operator(op) { }
|
||||
|
||||
ASTNodeTernaryExpression(const ASTNodeTernaryExpression &other) : ASTNode(other) {
|
||||
this->m_operator = other.m_operator;
|
||||
this->m_first = other.m_first->clone();
|
||||
this->m_second = other.m_second->clone();
|
||||
this->m_third = other.m_third->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTernaryExpression(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getFirstOperand() == nullptr || this->getSecondOperand() == nullptr || this->getThirdOperand() == nullptr)
|
||||
LogConsole::abortEvaluation("attempted to use void expression in mathematical expression", this);
|
||||
|
||||
auto firstNode = this->getFirstOperand()->evaluate(evaluator);
|
||||
auto secondNode = this->getSecondOperand()->evaluate(evaluator);
|
||||
auto thirdNode = this->getThirdOperand()->evaluate(evaluator);
|
||||
|
||||
auto *first = dynamic_cast<ASTNodeLiteral *>(firstNode.get());
|
||||
auto *second = dynamic_cast<ASTNodeLiteral *>(secondNode.get());
|
||||
auto *third = dynamic_cast<ASTNodeLiteral *>(thirdNode.get());
|
||||
|
||||
auto condition = std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return bool(value); } },
|
||||
first->getValue());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[condition]<typename T>(const T &second, const T &third) -> std::unique_ptr<ASTNode> { return std::unique_ptr<ASTNode>(new ASTNodeLiteral(condition ? second : third)); },
|
||||
[this](auto &&second, auto &&third) -> std::unique_ptr<ASTNode> { LogConsole::abortEvaluation("operands to ternary expression have different types", this); } },
|
||||
second->getValue(),
|
||||
third->getValue());
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getFirstOperand() const { return this->m_first; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getSecondOperand() const { return this->m_second; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getThirdOperand() const { return this->m_third; }
|
||||
[[nodiscard]] Token::Operator getOperator() const { return this->m_operator; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_first, m_second, m_third;
|
||||
Token::Operator m_operator;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeTypeDecl(std::string name, std::shared_ptr<ASTNode> &&type, std::optional<std::endian> endian = std::nullopt)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_endian(endian) { }
|
||||
|
||||
ASTNodeTypeDecl(const ASTNodeTypeDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_endian = other.m_endian;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTypeDecl(*this));
|
||||
}
|
||||
|
||||
void setName(const std::string &name) { this->m_name = name; }
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] const std::shared_ptr<ASTNode> &getType() { return this->m_type; }
|
||||
[[nodiscard]] std::optional<std::endian> getEndian() const { return this->m_endian; }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
if (auto attributable = dynamic_cast<Attributable *>(type.get())) {
|
||||
for (auto &attribute : this->getAttributes()) {
|
||||
if (auto node = dynamic_cast<ASTNodeAttribute *>(attribute.get()))
|
||||
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(node));
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
|
||||
for (auto &pattern : patterns) {
|
||||
if (pattern == nullptr)
|
||||
continue;
|
||||
|
||||
if (!this->m_name.empty())
|
||||
pattern->setTypeName(this->m_name);
|
||||
|
||||
if (this->m_endian.has_value())
|
||||
pattern->setEndian(this->m_endian.value());
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
void addAttribute(std::unique_ptr<ASTNodeAttribute> &&attribute) override {
|
||||
if (auto attributable = dynamic_cast<Attributable *>(this->m_type.get()); attributable != nullptr) {
|
||||
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(static_cast<ASTNodeAttribute *>(attribute->clone().release())));
|
||||
}
|
||||
|
||||
Attributable::addAttribute(std::move(attribute));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNode> m_type;
|
||||
std::optional<std::endian> m_endian;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTypeOperator : public ASTNode {
|
||||
public:
|
||||
ASTNodeTypeOperator(Token::Operator op, std::unique_ptr<ASTNode> &&expression) : m_op(op), m_expression(std::move(expression)) {
|
||||
}
|
||||
|
||||
ASTNodeTypeOperator(const ASTNodeTypeOperator &other) : ASTNode(other) {
|
||||
this->m_op = other.m_op;
|
||||
this->m_expression = other.m_expression->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTypeOperator(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] Token::Operator getOperator() const {
|
||||
return this->m_op;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getExpression() const {
|
||||
return this->m_expression;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto patterns = this->m_expression->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::AddressOf:
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(pattern->getOffset())));
|
||||
case Token::Operator::SizeOf:
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(pattern->getSize())));
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid type operator", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Token::Operator m_op;
|
||||
std::unique_ptr<ASTNode> m_expression;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_union.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeUnion : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeUnion() : ASTNode() { }
|
||||
|
||||
ASTNodeUnion(const ASTNodeUnion &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeUnion(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternUnion>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
size_t size = 0;
|
||||
std::vector<std::shared_ptr<Pattern>> memberPatterns;
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
evaluator->pushScope(pattern.get(), memberPatterns);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &member : this->m_members) {
|
||||
for (auto &memberPattern : member->createPatterns(evaluator)) {
|
||||
memberPattern->setOffset(startOffset);
|
||||
size = std::max(memberPattern->getSize(), size);
|
||||
|
||||
memberPatterns.push_back(std::move(memberPattern));
|
||||
}
|
||||
}
|
||||
|
||||
evaluator->dataOffset() = startOffset + size;
|
||||
pattern->setMembers(std::move(memberPatterns));
|
||||
pattern->setSize(size);
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getMembers() const { return this->m_members; }
|
||||
void addMember(std::shared_ptr<ASTNode> &&node) { this->m_members.push_back(std::move(node)); }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<ASTNode>> m_members;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeVariableDecl(std::string name, std::unique_ptr<ASTNode> &&type, std::unique_ptr<ASTNode> &&placementOffset = nullptr, bool inVariable = false, bool outVariable = false)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_placementOffset(std::move(placementOffset)), m_inVariable(inVariable), m_outVariable(outVariable) { }
|
||||
|
||||
ASTNodeVariableDecl(const ASTNodeVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
|
||||
this->m_inVariable = other.m_inVariable;
|
||||
this->m_outVariable = other.m_outVariable;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
[[nodiscard]] constexpr bool isInVariable() const { return this->m_inVariable; }
|
||||
[[nodiscard]] constexpr bool isOutVariable() const { return this->m_outVariable; }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
const auto node = this->m_placementOffset->evaluate(evaluator);
|
||||
const auto offset = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return offset; } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
pattern->setVariableName(this->m_name);
|
||||
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
evaluator->createVariable(this->getName(), this->getType().get());
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
|
||||
bool m_inVariable = false, m_outVariable = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeWhileStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeWhileStatement(std::unique_ptr<ASTNode> &&condition, std::vector<std::unique_ptr<ASTNode>> &&body, std::unique_ptr<ASTNode> &&postExpression = nullptr)
|
||||
: ASTNode(), m_condition(std::move(condition)), m_body(std::move(body)), m_postExpression(std::move(postExpression)) { }
|
||||
|
||||
ASTNodeWhileStatement(const ASTNodeWhileStatement &other) : ASTNode(other) {
|
||||
this->m_condition = other.m_condition->clone();
|
||||
|
||||
for (auto &statement : other.m_body)
|
||||
this->m_body.push_back(statement->clone());
|
||||
|
||||
if (other.m_postExpression != nullptr)
|
||||
this->m_postExpression = other.m_postExpression->clone();
|
||||
else
|
||||
this->m_postExpression = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeWhileStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getCondition() {
|
||||
return this->m_condition;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getBody() {
|
||||
return this->m_body;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
|
||||
u64 loopIterations = 0;
|
||||
while (evaluateCondition(evaluator)) {
|
||||
evaluator->handleAbort();
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
u32 startVariableCount = variables.size();
|
||||
ON_SCOPE_EXIT {
|
||||
ssize_t stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
};
|
||||
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
evaluator->getScope(0).parameterPack = parameterPack;
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
auto ctrlFlow = ControlFlowStatement::None;
|
||||
for (auto &statement : this->m_body) {
|
||||
auto result = statement->execute(evaluator);
|
||||
|
||||
ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
if (ctrlFlow == ControlFlowStatement::Return)
|
||||
return result;
|
||||
else if (ctrlFlow != ControlFlowStatement::None)
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->m_postExpression != nullptr)
|
||||
this->m_postExpression->execute(evaluator);
|
||||
|
||||
loopIterations++;
|
||||
if (loopIterations >= evaluator->getLoopLimit())
|
||||
LogConsole::abortEvaluation(hex::format("loop iterations exceeded limit of {}", evaluator->getLoopLimit()), this);
|
||||
|
||||
evaluator->handleAbort();
|
||||
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue)
|
||||
continue;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool evaluateCondition(Evaluator *evaluator) const {
|
||||
const auto node = this->m_condition->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](Pattern *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return value != 0; } },
|
||||
literal->getValue());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_body;
|
||||
std::unique_ptr<ASTNode> m_postExpression;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternLanguageError : public std::exception {
|
||||
public:
|
||||
PatternLanguageError(u32 lineNumber, std::string message) : m_lineNumber(lineNumber), m_message(std::move(message)) { }
|
||||
|
||||
[[nodiscard]] const char *what() const noexcept override {
|
||||
return this->m_message.c_str();
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 getLineNumber() const {
|
||||
return this->m_lineNumber;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 m_lineNumber;
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
class Provider;
|
||||
}
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
enum class DangerousFunctionPermission
|
||||
{
|
||||
Ask,
|
||||
Deny,
|
||||
Allow
|
||||
};
|
||||
|
||||
enum class ControlFlowStatement
|
||||
{
|
||||
None,
|
||||
Continue,
|
||||
Break,
|
||||
Return
|
||||
};
|
||||
|
||||
enum class BitfieldOrder
|
||||
{
|
||||
RightToLeft,
|
||||
LeftToRight
|
||||
};
|
||||
|
||||
class Pattern;
|
||||
class PatternCreationLimiter;
|
||||
class ASTNode;
|
||||
|
||||
class Evaluator {
|
||||
public:
|
||||
Evaluator() = default;
|
||||
|
||||
std::optional<std::vector<std::shared_ptr<Pattern>>> evaluate(const std::vector<std::shared_ptr<ASTNode>> &ast);
|
||||
|
||||
[[nodiscard]] LogConsole &getConsole() {
|
||||
return this->m_console;
|
||||
}
|
||||
|
||||
struct ParameterPack {
|
||||
std::string name;
|
||||
std::vector<Token::Literal> values;
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
Pattern *parent;
|
||||
std::vector<std::shared_ptr<Pattern>> *scope;
|
||||
std::optional<ParameterPack> parameterPack;
|
||||
};
|
||||
void pushScope(Pattern *parent, std::vector<std::shared_ptr<Pattern>> &scope) {
|
||||
if (this->m_scopes.size() > this->getEvaluationDepth())
|
||||
LogConsole::abortEvaluation(hex::format("evaluation depth exceeded set limit of {}", this->getEvaluationDepth()));
|
||||
|
||||
this->handleAbort();
|
||||
|
||||
this->m_scopes.push_back({ parent, &scope });
|
||||
}
|
||||
|
||||
void popScope() {
|
||||
this->m_scopes.pop_back();
|
||||
}
|
||||
|
||||
Scope &getScope(i32 index) {
|
||||
return this->m_scopes[this->m_scopes.size() - 1 + index];
|
||||
}
|
||||
|
||||
const Scope &getScope(i32 index) const {
|
||||
return this->m_scopes[this->m_scopes.size() - 1 + index];
|
||||
}
|
||||
|
||||
Scope &getGlobalScope() {
|
||||
return this->m_scopes.front();
|
||||
}
|
||||
|
||||
const Scope &getGlobalScope() const {
|
||||
return this->m_scopes.front();
|
||||
}
|
||||
|
||||
size_t getScopeCount() {
|
||||
return this->m_scopes.size();
|
||||
}
|
||||
|
||||
bool isGlobalScope() {
|
||||
return this->m_scopes.size() == 1;
|
||||
}
|
||||
|
||||
void setProvider(prv::Provider *provider) {
|
||||
this->m_provider = provider;
|
||||
}
|
||||
|
||||
void setInVariables(const std::map<std::string, Token::Literal> &inVariables) {
|
||||
this->m_inVariables = inVariables;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::map<std::string, Token::Literal> getOutVariables() const {
|
||||
std::map<std::string, Token::Literal> result;
|
||||
|
||||
for (const auto &[name, offset] : this->m_outVariables) {
|
||||
result.insert({ name, this->getStack()[offset] });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] prv::Provider *getProvider() const {
|
||||
return this->m_provider;
|
||||
}
|
||||
|
||||
void setDefaultEndian(std::endian endian) {
|
||||
this->m_defaultEndian = endian;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::endian getDefaultEndian() const {
|
||||
return this->m_defaultEndian;
|
||||
}
|
||||
|
||||
void setEvaluationDepth(u64 evalDepth) {
|
||||
this->m_evalDepth = evalDepth;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getEvaluationDepth() const {
|
||||
return this->m_evalDepth;
|
||||
}
|
||||
|
||||
void setArrayLimit(u64 arrayLimit) {
|
||||
this->m_arrayLimit = arrayLimit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getArrayLimit() const {
|
||||
return this->m_arrayLimit;
|
||||
}
|
||||
|
||||
void setPatternLimit(u64 limit) {
|
||||
this->m_patternLimit = limit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getPatternLimit() {
|
||||
return this->m_patternLimit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getPatternCount() {
|
||||
return this->m_currPatternCount;
|
||||
}
|
||||
|
||||
void setLoopLimit(u64 limit) {
|
||||
this->m_loopLimit = limit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getLoopLimit() {
|
||||
return this->m_loopLimit;
|
||||
}
|
||||
|
||||
void setBitfieldOrder(BitfieldOrder order) {
|
||||
this->m_bitfieldOrder = order;
|
||||
}
|
||||
|
||||
[[nodiscard]] BitfieldOrder getBitfieldOrder() {
|
||||
return this->m_bitfieldOrder;
|
||||
}
|
||||
|
||||
u64 &dataOffset() { return this->m_currOffset; }
|
||||
|
||||
bool addCustomFunction(const std::string &name, u32 numParams, const ContentRegistry::PatternLanguage::Callback &function) {
|
||||
const auto [iter, inserted] = this->m_customFunctions.insert({
|
||||
name, {numParams, function}
|
||||
});
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::map<std::string, ContentRegistry::PatternLanguage::Function> &getCustomFunctions() const {
|
||||
return this->m_customFunctions;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Token::Literal> &getStack() {
|
||||
return this->m_stack;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<Token::Literal> &getStack() const {
|
||||
return this->m_stack;
|
||||
}
|
||||
|
||||
void createParameterPack(const std::string &name, const std::vector<Token::Literal> &values);
|
||||
void createVariable(const std::string &name, ASTNode *type, const std::optional<Token::Literal> &value = std::nullopt, bool outVariable = false);
|
||||
void setVariable(const std::string &name, const Token::Literal &value);
|
||||
|
||||
void abort() {
|
||||
this->m_aborted = true;
|
||||
}
|
||||
|
||||
void handleAbort() {
|
||||
if (this->m_aborted)
|
||||
LogConsole::abortEvaluation("evaluation aborted by user");
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<Token::Literal> getEnvVariable(const std::string &name) const {
|
||||
if (this->m_envVariables.contains(name)) {
|
||||
auto value = this->m_envVariables.at(name);
|
||||
return this->m_envVariables.at(name);
|
||||
} else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void setEnvVariable(const std::string &name, const Token::Literal &value) {
|
||||
this->m_envVariables[name] = value;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasDangerousFunctionBeenCalled() const {
|
||||
return this->m_dangerousFunctionCalled;
|
||||
}
|
||||
|
||||
void dangerousFunctionCalled() {
|
||||
this->m_dangerousFunctionCalled = true;
|
||||
}
|
||||
|
||||
void allowDangerousFunctions(bool allow) {
|
||||
this->m_allowDangerousFunctions = allow ? DangerousFunctionPermission::Allow : DangerousFunctionPermission::Deny;
|
||||
this->m_dangerousFunctionCalled = false;
|
||||
}
|
||||
|
||||
[[nodiscard]] DangerousFunctionPermission getDangerousFunctionPermission() const {
|
||||
return this->m_allowDangerousFunctions;
|
||||
}
|
||||
|
||||
void setCurrentControlFlowStatement(ControlFlowStatement statement) {
|
||||
this->m_currControlFlowStatement = statement;
|
||||
}
|
||||
|
||||
[[nodiscard]] ControlFlowStatement getCurrentControlFlowStatement() const {
|
||||
return this->m_currControlFlowStatement;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<Token::Literal> &getMainResult() {
|
||||
return this->m_mainResult;
|
||||
}
|
||||
|
||||
private:
|
||||
void patternCreated();
|
||||
void patternDestroyed();
|
||||
|
||||
private:
|
||||
u64 m_currOffset;
|
||||
prv::Provider *m_provider = nullptr;
|
||||
LogConsole m_console;
|
||||
|
||||
std::endian m_defaultEndian = std::endian::native;
|
||||
u64 m_evalDepth;
|
||||
u64 m_arrayLimit;
|
||||
u64 m_patternLimit;
|
||||
u64 m_loopLimit;
|
||||
|
||||
u64 m_currPatternCount;
|
||||
|
||||
std::atomic<bool> m_aborted;
|
||||
|
||||
std::vector<Scope> m_scopes;
|
||||
std::map<std::string, ContentRegistry::PatternLanguage::Function> m_customFunctions;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_customFunctionDefinitions;
|
||||
std::vector<Token::Literal> m_stack;
|
||||
|
||||
std::optional<Token::Literal> m_mainResult;
|
||||
|
||||
std::map<std::string, Token::Literal> m_envVariables;
|
||||
std::map<std::string, Token::Literal> m_inVariables;
|
||||
std::map<std::string, size_t> m_outVariables;
|
||||
|
||||
std::atomic<bool> m_dangerousFunctionCalled = false;
|
||||
std::atomic<DangerousFunctionPermission> m_allowDangerousFunctions = DangerousFunctionPermission::Ask;
|
||||
ControlFlowStatement m_currControlFlowStatement;
|
||||
BitfieldOrder m_bitfieldOrder = BitfieldOrder::RightToLeft;
|
||||
|
||||
friend class PatternCreationLimiter;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
Lexer() = default;
|
||||
|
||||
std::optional<std::vector<Token>> lex(const std::string &code);
|
||||
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::optional<PatternLanguageError> m_error;
|
||||
|
||||
[[noreturn]] static void throwLexerError(const std::string &error, u32 lineNumber) {
|
||||
throw PatternLanguageError(lineNumber, "Lexer: " + error);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user