mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||
249
.github/workflows/build.yml
vendored
249
.github/workflows/build.yml
vendored
@@ -2,7 +2,9 @@ name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
@@ -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
|
||||
@@ -184,7 +212,7 @@ jobs:
|
||||
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
|
||||
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
|
||||
@@ -197,17 +225,23 @@ jobs:
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-11 CXX=g++-11 cmake \
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-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 DESTDIR=AppDir
|
||||
|
||||
- name: 📜 Set version variable
|
||||
run: |
|
||||
echo "version=`cat VERSION`" >> $GITHUB_ENV
|
||||
|
||||
#- name: 📦 Bundle Flatpak
|
||||
# run: |
|
||||
# sudo apt install flatpak flatpak-builder
|
||||
@@ -217,45 +251,124 @@ jobs:
|
||||
# flatpak-builder --jobs=4 --repo=imhex _flatpak dist/net.werwolv.ImHex.yaml --ccache --keep-build-dirs
|
||||
# flatpak build-bundle imhex imhex.flatpak net.werwolv.ImHex stable
|
||||
|
||||
- name: ⬆️ Upload ELF
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Linux ELF
|
||||
path: |
|
||||
build/AppDir/*
|
||||
|
||||
- name: 📦 Bundle DEB
|
||||
run: |
|
||||
cp -r build/DEBIAN build/AppDir
|
||||
dpkg-deb --build build/AppDir
|
||||
mv build/AppDir.deb imhex.deb
|
||||
mv build/AppDir.deb imhex-${{env.version}}.deb
|
||||
rm -rf build/AppDir/DEBIAN
|
||||
|
||||
- name: 📦 Bundle AppImage
|
||||
run: |
|
||||
cd build
|
||||
appimage-builder --recipe ../dist/AppImageBuilder.yml
|
||||
mv ImHex-AppImage-x86_64.AppImage ../imhex.AppImage
|
||||
mv ImHex-AppImage-x86_64.AppImage ../imhex-${{env.version}}.AppImage
|
||||
cd ..
|
||||
|
||||
#- name: ⬆️ Upload Flatpak
|
||||
# uses: actions/upload-artifact@v2
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: Linux Flatpak
|
||||
# path: |
|
||||
# imhex.flatpak
|
||||
|
||||
- name: ⬆️ Upload DEB
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
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
|
||||
|
||||
|
||||
92
.github/workflows/release.yml
vendored
Normal file
92
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
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 -cvf 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
|
||||
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
.idea/vcs.xml
generated
3
.idea/vcs.xml
generated
@@ -2,9 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imhex_patterns-src" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imhex_patterns-src/yara/official_rules" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imhex_patterns-src/yara/official_rules/utils/yara-forensics" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/capstone" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/curl" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/fmt" vcs="Git" />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Updating the version here will update it throughout ImHex as well
|
||||
set(IMHEX_VERSION "1.17.0")
|
||||
file(READ "VERSION" IMHEX_VERSION)
|
||||
project(imhex VERSION ${IMHEX_VERSION})
|
||||
message("Project version ${IMHEX_VERSION}")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -36,6 +37,3 @@ add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
|
||||
# Configure packaging
|
||||
createPackage()
|
||||
|
||||
# Download and install all current files from the ImHex-Patterns repo
|
||||
downloadImHexPatternsFiles()
|
||||
91
README.md
91
README.md
@@ -127,103 +127,16 @@ Nightlies are available via GitHub Actions [here](https://github.com/WerWolv/ImH
|
||||
- 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)
|
||||
|
||||
## 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
|
||||
@@ -231,6 +144,8 @@ 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
|
||||
- [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,19 @@ 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}/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 +238,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,7 +288,7 @@ macro(detectBadClone)
|
||||
endmacro()
|
||||
|
||||
|
||||
function(downloadImHexPatternsFiles)
|
||||
function(downloadImHexPatternsFiles dest)
|
||||
FetchContent_Declare(
|
||||
imhex_patterns
|
||||
GIT_REPOSITORY https://github.com/WerWolv/ImHex-Patterns.git
|
||||
@@ -294,7 +299,7 @@ function(downloadImHexPatternsFiles)
|
||||
|
||||
set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic)
|
||||
foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})
|
||||
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION "./")
|
||||
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION ${dest})
|
||||
endforeach()
|
||||
|
||||
endfunction()
|
||||
66
dist/AppImageBuilder.yml
vendored
66
dist/AppImageBuilder.yml
vendored
@@ -1,21 +1,7 @@
|
||||
# appimage-builder recipe see https://appimage-builder.readthedocs.io for details
|
||||
version: 1
|
||||
script:
|
||||
- rm -rf AppDir | true
|
||||
- CC=gcc-11 CXX=g++11 cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- make -j3 install DESTDIR=AppDir
|
||||
- mv AppDir/usr/share/imhex/plugins AppDir/usr/bin/plugins
|
||||
- mv AppDir/usr/constants AppDir/usr/bin/constants
|
||||
- mv AppDir/usr/encodings AppDir/usr/bin/encodings
|
||||
- mv AppDir/usr/includes AppDir/usr/bin/includes
|
||||
- mv AppDir/usr/magic AppDir/usr/bin/magic
|
||||
- mv AppDir/usr/patterns AppDir/usr/bin/patterns
|
||||
- mkdir -p AppDir/usr/share/icons/hicolor/512x512
|
||||
- cp AppDir/usr/share/pixmaps/imhex.png AppDir/usr/share/icons/hicolor/512x512/imhex.png
|
||||
|
||||
|
||||
AppDir:
|
||||
path: ./AppDir
|
||||
path: .AppDir
|
||||
app_info:
|
||||
id: imhex
|
||||
name: ImHex
|
||||
@@ -28,18 +14,20 @@ AppDir:
|
||||
- amd64
|
||||
allow_unauthenticated: true
|
||||
sources:
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ impish main restricted
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ impish-updates main restricted
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ impish universe
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ impish-updates universe
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ impish multiverse
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ impish-updates multiverse
|
||||
- sourceline: deb http://us.archive.ubuntu.com/ubuntu/ impish-backports main restricted
|
||||
- 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 impish-security main restricted
|
||||
- sourceline: deb http://security.ubuntu.com/ubuntu impish-security universe
|
||||
- sourceline: deb http://security.ubuntu.com/ubuntu impish-security 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:
|
||||
- adwaita-icon-theme-full
|
||||
- librsvg2-common
|
||||
- libbz2-1.0:amd64
|
||||
- libcap2:amd64
|
||||
- libdbus-1-3:amd64
|
||||
@@ -49,12 +37,11 @@ AppDir:
|
||||
- libpcre3:amd64
|
||||
- libselinux1:amd64
|
||||
- libtinfo6:amd64
|
||||
- yaru-theme-icon
|
||||
files:
|
||||
include:
|
||||
- /lib/x86_64-linux-gnu/libGLX.so.0
|
||||
- /lib/x86_64-linux-gnu/libGLdispatch.so.0
|
||||
- /lib/x86_64-linux-gnu/libLLVM-12.so.1
|
||||
- /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
|
||||
@@ -82,7 +69,7 @@ AppDir:
|
||||
- /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.7
|
||||
- /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
|
||||
@@ -94,31 +81,42 @@ AppDir:
|
||||
- /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/libgtk-3.so.0
|
||||
- /lib/x86_64-linux-gnu/libharfbuzz.so.0
|
||||
- /lib/x86_64-linux-gnu/libicudata.so.67
|
||||
- /lib/x86_64-linux-gnu/libicuuc.so.67
|
||||
- /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.3
|
||||
- /lib/x86_64-linux-gnu/libmbedtls.so.12
|
||||
- /lib/x86_64-linux-gnu/libmbedx509.so.0
|
||||
- /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.8.so.1.0
|
||||
- /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
|
||||
|
||||
38
dist/Arch/PKGBUILD
vendored
Normal file
38
dist/Arch/PKGBUILD
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# 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 gtk3)
|
||||
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
|
||||
install -d $pkgdir/usr/share/imhex
|
||||
}
|
||||
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, libgtk-3-0
|
||||
Maintainer: WerWolv <hey@werwolv.net>
|
||||
Description: ImHex Hex Editor
|
||||
A Hex Editor for Reverse Engineers, Programmers and
|
||||
|
||||
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`
|
||||
2
dist/get_deps_archlinux.sh
vendored
2
dist/get_deps_archlinux.sh
vendored
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
pacman -S --needed \
|
||||
pacman -S $@ --needed \
|
||||
cmake \
|
||||
gcc \
|
||||
lld \
|
||||
|
||||
13
dist/get_deps_debian.sh
vendored
13
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-11 \
|
||||
g++-11 \
|
||||
gcc-12 \
|
||||
g++-12 \
|
||||
lld \
|
||||
${PKGCONF:-} \
|
||||
cmake \
|
||||
@@ -25,7 +21,4 @@ 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++-11"
|
||||
libgtk-3-dev
|
||||
5
dist/imhex.desktop
vendored
5
dist/imhex.desktop
vendored
@@ -2,10 +2,9 @@
|
||||
Name=ImHex
|
||||
Comment=ImHex Hex Editor
|
||||
GenericName=Hex Editor
|
||||
Exec=/usr/bin/imhex %U
|
||||
Exec=imhex %U
|
||||
Icon=imhex
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Development;
|
||||
Categories=Development;IDE;
|
||||
StartupWMClass=imhex
|
||||
|
||||
|
||||
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 <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
#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::TextFormatted("{:02X}", 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 != (size_t)-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);
|
||||
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: f14e88a727...12063078f0
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
Submodule lib/external/pattern_language updated: f276631e73...5ab770490e
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)
|
||||
|
||||
@@ -123,14 +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/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)
|
||||
@@ -142,7 +143,9 @@ if (APPLE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES} source/helpers/fs_macos.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}")
|
||||
|
||||
@@ -3,30 +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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -10,12 +10,16 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -152,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)...));
|
||||
}
|
||||
@@ -230,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)...);
|
||||
@@ -322,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;
|
||||
@@ -378,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)...));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -129,6 +129,12 @@ namespace hex {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
struct ImGuiContext;
|
||||
|
||||
namespace hex {
|
||||
@@ -39,7 +45,11 @@ namespace hex {
|
||||
using SetImGuiContextFunc = void (*)(ImGuiContext *);
|
||||
using IsBuiltinPluginFunc = bool (*)();
|
||||
|
||||
void *m_handle = nullptr;
|
||||
#if defined(OS_WINDOWS)
|
||||
HMODULE m_handle = nullptr;
|
||||
#else
|
||||
void *m_handle = nullptr;
|
||||
#endif
|
||||
std::fs::path m_path;
|
||||
|
||||
mutable bool m_initialized = false;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -2,11 +2,22 @@
|
||||
|
||||
#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
|
||||
|
||||
#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 {
|
||||
|
||||
@@ -26,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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,12 @@ namespace hex::fs {
|
||||
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);
|
||||
|
||||
@@ -50,6 +50,12 @@ namespace hex::fs {
|
||||
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;
|
||||
@@ -61,6 +67,8 @@ namespace hex::fs {
|
||||
|
||||
bool isPathWritable(const std::fs::path &path);
|
||||
|
||||
std::fs::path toShortPath(const std::fs::path &path);
|
||||
|
||||
enum class DialogMode
|
||||
{
|
||||
Open,
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
namespace hex {
|
||||
std::string getMacExecutableDirectoryPath();
|
||||
std::string getMacApplicationSupportDirectoryPath();
|
||||
}
|
||||
#endif
|
||||
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
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
struct _object;
|
||||
typedef struct _object PyObject;
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv {
|
||||
class Provider;
|
||||
}
|
||||
|
||||
class LoaderScript {
|
||||
public:
|
||||
LoaderScript() = delete;
|
||||
|
||||
static bool processFile(const std::fs::path &scriptPath);
|
||||
|
||||
static void setFilePath(const std::fs::path &filePath) { LoaderScript::s_filePath = filePath; }
|
||||
static void setDataProvider(prv::Provider *provider) { LoaderScript::s_dataProvider = provider; }
|
||||
|
||||
private:
|
||||
static inline std::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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -49,6 +49,8 @@ namespace hex {
|
||||
|
||||
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 +64,8 @@ namespace hex {
|
||||
std::mutex m_transmissionActive;
|
||||
float m_progress = 0.0F;
|
||||
bool m_shouldCancel = false;
|
||||
|
||||
static std::string s_proxyUrl;
|
||||
};
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace hex {
|
||||
std::string encodeByteString(const std::vector<u8> &bytes);
|
||||
std::vector<u8> decodeByteString(const std::string &string);
|
||||
|
||||
[[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)>;
|
||||
@@ -72,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>
|
||||
@@ -188,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));
|
||||
@@ -234,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;
|
||||
@@ -287,6 +317,13 @@ 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);
|
||||
|
||||
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
|
||||
223
lib/libimhex/include/hex/providers/buffered_reader.hpp
Normal file
223
lib/libimhex/include/hex/providers/buffered_reader.hpp
Normal file
@@ -0,0 +1,223 @@
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
class BufferedReader {
|
||||
public:
|
||||
explicit BufferedReader(Provider *provider, size_t bufferSize = 0xFF'FFFF) : m_provider(provider), m_maxBufferSize(bufferSize), m_buffer(bufferSize) { }
|
||||
|
||||
void seek(u64 address) {
|
||||
this->m_baseAddress = address;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<u8> read(u64 address, size_t size) {
|
||||
if (size > this->m_buffer.size()) {
|
||||
std::vector<u8> result;
|
||||
result.resize(size);
|
||||
|
||||
this->m_provider->read(address, result.data(), result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this->updateBuffer(address, size);
|
||||
|
||||
auto result = &this->m_buffer[address - this->m_baseAddress];
|
||||
|
||||
return { result, result + std::min(size, this->m_buffer.size()) };
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<u8> readReverse(u64 address, size_t size) {
|
||||
if (size > this->m_buffer.size()) {
|
||||
std::vector<u8> result;
|
||||
result.resize(size);
|
||||
|
||||
this->m_provider->read(address, result.data(), result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this->updateBuffer(address - std::min<u64>(address, this->m_buffer.size()), size);
|
||||
|
||||
auto result = &this->m_buffer[address - this->m_baseAddress];
|
||||
|
||||
return { result, result + std::min(size, this->m_buffer.size()) };
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = u8;
|
||||
using pointer = const value_type*;
|
||||
using reference = const value_type&;
|
||||
|
||||
Iterator(BufferedReader *reader, u64 address) : m_reader(reader), m_address(address) {}
|
||||
|
||||
Iterator& operator++() {
|
||||
this->m_address++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) {
|
||||
auto copy = *this;
|
||||
this->m_address++;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
Iterator& operator+=(i64 offset) {
|
||||
this->m_address += offset;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator& operator-=(i64 offset) {
|
||||
this->m_address -= offset;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*() const {
|
||||
return (*this)[0];
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getAddress() const {
|
||||
return this->m_address;
|
||||
}
|
||||
|
||||
difference_type operator-(const Iterator &other) const {
|
||||
return this->m_address - other.m_address;
|
||||
}
|
||||
|
||||
Iterator operator+(i64 offset) const {
|
||||
return { this->m_reader, this->m_address + offset };
|
||||
}
|
||||
|
||||
value_type operator[](i64 offset) const {
|
||||
auto result = this->m_reader->read(this->m_address + offset, 1);
|
||||
if (result.empty())
|
||||
return 0x00;
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
friend bool operator== (const Iterator& left, const Iterator& right) { return left.m_address == right.m_address; };
|
||||
friend bool operator!= (const Iterator& left, const Iterator& right) { return left.m_address != right.m_address; };
|
||||
private:
|
||||
BufferedReader *m_reader;
|
||||
u64 m_address;
|
||||
};
|
||||
|
||||
class ReverseIterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = u8;
|
||||
using pointer = const value_type*;
|
||||
using reference = const value_type&;
|
||||
|
||||
ReverseIterator(BufferedReader *reader, u64 address) : m_reader(reader), m_address(address) {}
|
||||
|
||||
ReverseIterator& operator++() {
|
||||
this->m_address--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReverseIterator operator++(int) {
|
||||
auto copy = *this;
|
||||
this->m_address--;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
ReverseIterator& operator+=(i64 offset) {
|
||||
this->m_address -= offset;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReverseIterator& operator-=(i64 offset) {
|
||||
this->m_address += offset;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*() const {
|
||||
return (*this)[0];
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getAddress() const {
|
||||
return this->m_address;
|
||||
}
|
||||
|
||||
difference_type operator-(const ReverseIterator &other) const {
|
||||
return other.m_address - this->m_address;
|
||||
}
|
||||
|
||||
ReverseIterator operator+(i64 offset) const {
|
||||
return { this->m_reader, this->m_address - offset };
|
||||
}
|
||||
|
||||
value_type operator[](i64 offset) const {
|
||||
auto result = this->m_reader->readReverse(this->m_address + offset, 1);
|
||||
if (result.empty())
|
||||
return 0x00;
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
friend bool operator== (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address == right.m_address; };
|
||||
friend bool operator!= (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address != right.m_address; };
|
||||
|
||||
private:
|
||||
BufferedReader *m_reader;
|
||||
u64 m_address;
|
||||
};
|
||||
|
||||
Iterator begin() {
|
||||
return { this, this->m_baseAddress };
|
||||
}
|
||||
|
||||
Iterator end() {
|
||||
return { this, this->m_baseAddress + this->m_provider->getActualSize() };
|
||||
}
|
||||
|
||||
ReverseIterator rbegin() {
|
||||
return { this, this->m_baseAddress };
|
||||
}
|
||||
|
||||
ReverseIterator rend() {
|
||||
return { this, std::numeric_limits<u64>::max() };
|
||||
}
|
||||
|
||||
private:
|
||||
void updateBuffer(u64 address, size_t size) {
|
||||
if (!this->m_bufferValid || address < this->m_baseAddress || address + size > (this->m_baseAddress + this->m_buffer.size())) {
|
||||
const auto remainingBytes = this->m_provider->getActualSize() - address;
|
||||
if (remainingBytes < this->m_maxBufferSize)
|
||||
this->m_buffer.resize(remainingBytes);
|
||||
|
||||
this->m_provider->read(address, this->m_buffer.data(), this->m_buffer.size());
|
||||
this->m_baseAddress = address;
|
||||
this->m_bufferValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Provider *m_provider;
|
||||
|
||||
size_t m_maxBufferSize;
|
||||
bool m_bufferValid = false;
|
||||
u64 m_baseAddress = 0x00;
|
||||
std::vector<u8> m_buffer;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -35,6 +35,7 @@ namespace hex::prv {
|
||||
|
||||
virtual void resize(size_t newSize);
|
||||
virtual void insert(u64 offset, size_t size);
|
||||
virtual void remove(u64 offset, size_t size);
|
||||
|
||||
virtual void save();
|
||||
virtual void saveAs(const std::fs::path &path);
|
||||
|
||||
@@ -63,7 +63,6 @@ namespace ImGui {
|
||||
|
||||
void UnderlinedText(const char *label, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0));
|
||||
|
||||
void Disabled(const std::function<void()> &widgets, bool disabled);
|
||||
void TextSpinner(const char *label);
|
||||
|
||||
void Header(const char *label, bool firstEntry = false);
|
||||
@@ -75,7 +74,8 @@ namespace ImGui {
|
||||
bool ToolBarButton(const char *symbol, ImVec4 color);
|
||||
bool IconButton(const char *symbol, ImVec4 color, ImVec2 size_arg = ImVec2(0, 0));
|
||||
|
||||
bool InputIntegerPrefix(const char* label, const char *prefix, u64 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
bool InputIntegerPrefix(const char* label, const char *prefix, void *value, ImGuiDataType type, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
bool InputHexadecimal(const char* label, u32 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
bool InputHexadecimal(const char* label, u64 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
|
||||
inline bool HasSecondPassed() {
|
||||
@@ -131,4 +131,10 @@ namespace ImGui {
|
||||
|
||||
bool InputText(const char* label, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
bool InputTextMultiline(const char* label, std::string &buffer, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
|
||||
bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data);
|
||||
|
||||
void HideTooltip();
|
||||
|
||||
bool BitCheckbox(const char* label, bool* v);
|
||||
}
|
||||
@@ -34,11 +34,9 @@ namespace hex {
|
||||
[[nodiscard]] virtual bool isAvailable() const;
|
||||
[[nodiscard]] virtual bool shouldProcess() const { return this->isAvailable() && this->getWindowOpenState(); }
|
||||
|
||||
static void drawCommonInterfaces();
|
||||
|
||||
static void showMessagePopup(const std::string &message);
|
||||
static void showErrorPopup(const std::string &errorMessage);
|
||||
static void showFatalPopup(const std::string &errorMessage);
|
||||
static void showInfoPopup(const std::string &message);
|
||||
static void showErrorPopup(const std::string &message);
|
||||
static void showFatalPopup(const std::string &message);
|
||||
static void showYesNoQuestionPopup(const std::string &message, const std::function<void()> &yesCallback, const std::function<void()> &noCallback);
|
||||
|
||||
static void showFileChooserPopup(const std::vector<std::fs::path> &paths, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback);
|
||||
@@ -71,15 +69,6 @@ namespace hex {
|
||||
bool m_windowOpen = false;
|
||||
std::map<Shortcut, std::function<void()>> m_shortcuts;
|
||||
|
||||
static std::string s_popupMessage;
|
||||
|
||||
static std::function<void()> s_yesCallback, s_noCallback;
|
||||
|
||||
static u32 s_selectableFileIndex;
|
||||
static std::vector<std::fs::path> s_selectableFiles;
|
||||
static std::function<void(std::fs::path)> s_selectableFileOpenCallback;
|
||||
static std::vector<nfdfilteritem_t> s_selectableFilesValidExtensions;
|
||||
|
||||
static ImFontAtlas *s_fontAtlas;
|
||||
static ImFontConfig s_fontConfig;
|
||||
|
||||
|
||||
@@ -536,4 +536,64 @@ namespace hex {
|
||||
|
||||
}
|
||||
|
||||
namespace ContentRegistry::HexEditor {
|
||||
|
||||
const int DataVisualizer::TextInputFlags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll;
|
||||
|
||||
bool DataVisualizer::drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const {
|
||||
struct UserData {
|
||||
u8 *data;
|
||||
i32 maxChars;
|
||||
|
||||
bool editingDone;
|
||||
};
|
||||
|
||||
UserData userData = {
|
||||
.data = data,
|
||||
.maxChars = this->getMaxCharsPerCell(),
|
||||
|
||||
.editingDone = false
|
||||
};
|
||||
|
||||
ImGui::PushID(reinterpret_cast<void*>(address));
|
||||
ImGui::InputScalarCallback("##editing_input", dataType, data, format, flags | TextInputFlags | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int {
|
||||
auto &userData = *reinterpret_cast<UserData*>(data->UserData);
|
||||
|
||||
if (data->BufTextLen >= userData.maxChars)
|
||||
userData.editingDone = true;
|
||||
|
||||
return 0;
|
||||
}, &userData);
|
||||
ImGui::PopID();
|
||||
|
||||
return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Enter);
|
||||
}
|
||||
|
||||
void impl::addDataVisualizer(const std::string &unlocalizedName, DataVisualizer *visualizer) {
|
||||
getVisualizers().insert({ unlocalizedName, visualizer });
|
||||
|
||||
}
|
||||
|
||||
std::map<std::string, DataVisualizer*> &impl::getVisualizers() {
|
||||
static std::map<std::string, DataVisualizer*> visualizers;
|
||||
|
||||
return visualizers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ContentRegistry::Hashes {
|
||||
|
||||
std::vector<Hash *> &impl::getHashes() {
|
||||
static std::vector<Hash *> hashes;
|
||||
|
||||
return hashes;
|
||||
}
|
||||
|
||||
void impl::add(Hash *hash) {
|
||||
getHashes().push_back(hash);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <utility>
|
||||
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace ImHexApi::Common {
|
||||
@@ -29,32 +32,55 @@ namespace hex {
|
||||
|
||||
namespace ImHexApi::HexEditor {
|
||||
|
||||
Highlighting::Highlighting(Region region, color_t color, std::string tooltip)
|
||||
: m_region(region), m_color(color), m_tooltip(std::move(tooltip)) {
|
||||
Highlighting::Highlighting(Region region, color_t color)
|
||||
: m_region(region), m_color(color) {
|
||||
}
|
||||
|
||||
Tooltip::Tooltip(Region region, std::string value, color_t color) : m_region(region), m_value(std::move(value)), m_color(color) {
|
||||
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
static std::map<u32, Highlighting> s_highlights;
|
||||
std::map<u32, Highlighting> &getHighlights() {
|
||||
return s_highlights;
|
||||
static std::map<u32, Highlighting> s_backgroundHighlights;
|
||||
std::map<u32, Highlighting> &getBackgroundHighlights() {
|
||||
return s_backgroundHighlights;
|
||||
}
|
||||
|
||||
static std::map<u32, HighlightingFunction> s_highlightingFunctions;
|
||||
std::map<u32, HighlightingFunction> &getHighlightingFunctions() {
|
||||
return s_highlightingFunctions;
|
||||
static std::map<u32, HighlightingFunction> s_backgroundHighlightingFunctions;
|
||||
std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions() {
|
||||
return s_backgroundHighlightingFunctions;
|
||||
}
|
||||
|
||||
static std::map<u32, Highlighting> s_foregroundHighlights;
|
||||
std::map<u32, Highlighting> &getForegroundHighlights() {
|
||||
return s_foregroundHighlights;
|
||||
}
|
||||
|
||||
static std::map<u32, HighlightingFunction> s_foregroundHighlightingFunctions;
|
||||
std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions() {
|
||||
return s_foregroundHighlightingFunctions;
|
||||
}
|
||||
|
||||
static std::map<u32, Tooltip> s_tooltips;
|
||||
std::map<u32, Tooltip> &getTooltips() {
|
||||
return s_tooltips;
|
||||
}
|
||||
|
||||
static std::map<u32, TooltipFunction> s_tooltipFunctions;
|
||||
std::map<u32, TooltipFunction> &getTooltipFunctions() {
|
||||
return s_tooltipFunctions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 addHighlight(const Region ®ion, color_t color, const std::string &tooltip) {
|
||||
auto &highlights = impl::getHighlights();
|
||||
static u64 id = 0;
|
||||
u32 addBackgroundHighlight(const Region ®ion, color_t color) {
|
||||
static u32 id = 0;
|
||||
|
||||
id++;
|
||||
|
||||
highlights.insert({
|
||||
id, Highlighting {region, color, tooltip}
|
||||
impl::getBackgroundHighlights().insert({
|
||||
id, Highlighting {region, color}
|
||||
});
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
@@ -62,37 +88,101 @@ namespace hex {
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeHighlight(u32 id) {
|
||||
impl::getHighlights().erase(id);
|
||||
void removeBackgroundHighlight(u32 id) {
|
||||
impl::getBackgroundHighlights().erase(id);
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
u32 addHighlightingProvider(const impl::HighlightingFunction &function) {
|
||||
auto &highlightFuncs = impl::getHighlightingFunctions();
|
||||
u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) {
|
||||
static u32 id = 0;
|
||||
|
||||
auto id = highlightFuncs.size();
|
||||
id++;
|
||||
|
||||
highlightFuncs.insert({ id, function });
|
||||
impl::getBackgroundHighlightingFunctions().insert({ id, function });
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeHighlightingProvider(u32 id) {
|
||||
impl::getHighlightingFunctions().erase(id);
|
||||
void removeBackgroundHighlightingProvider(u32 id) {
|
||||
impl::getBackgroundHighlightingFunctions().erase(id);
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
Region getSelection() {
|
||||
static Region selectedRegion;
|
||||
EventManager::subscribe<EventRegionSelected>([](const Region ®ion) {
|
||||
selectedRegion = region;
|
||||
u32 addForegroundHighlight(const Region ®ion, color_t color) {
|
||||
static u32 id = 0;
|
||||
|
||||
id++;
|
||||
|
||||
impl::getForegroundHighlights().insert({
|
||||
id, Highlighting {region, color}
|
||||
});
|
||||
|
||||
return selectedRegion;
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeForegroundHighlight(u32 id) {
|
||||
impl::getForegroundHighlights().erase(id);
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) {
|
||||
static u32 id = 0;
|
||||
|
||||
id++;
|
||||
|
||||
impl::getForegroundHighlightingFunctions().insert({ id, function });
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeForegroundHighlightingProvider(u32 id) {
|
||||
impl::getForegroundHighlightingFunctions().erase(id);
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
static u32 tooltipId = 0;
|
||||
u32 addTooltip(Region region, std::string value, color_t color) {
|
||||
tooltipId++;
|
||||
impl::getTooltips().insert({ tooltipId, { region, std::move(value), color } });
|
||||
|
||||
return tooltipId;
|
||||
}
|
||||
|
||||
void removeTooltip(u32 id) {
|
||||
impl::getTooltips().erase(id);
|
||||
}
|
||||
|
||||
static u32 tooltipFunctionId;
|
||||
u32 addTooltipProvider(TooltipFunction function) {
|
||||
tooltipFunctionId++;
|
||||
impl::getTooltipFunctions().insert({ tooltipFunctionId, std::move(function) });
|
||||
|
||||
return tooltipFunctionId;
|
||||
}
|
||||
|
||||
void removeTooltipProvider(u32 id) {
|
||||
impl::getTooltipFunctions().erase(id);
|
||||
}
|
||||
|
||||
bool isSelectionValid() {
|
||||
return getSelection().has_value();
|
||||
}
|
||||
|
||||
std::optional<Region> getSelection() {
|
||||
std::optional<Region> selection;
|
||||
EventManager::post<QuerySelection>(selection);
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
void setSelection(const Region ®ion) {
|
||||
@@ -144,7 +234,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return !s_providers.empty();
|
||||
return !s_providers.empty() && s_currentProvider < s_providers.size();
|
||||
}
|
||||
|
||||
void add(prv::Provider *provider) {
|
||||
@@ -159,7 +249,7 @@ namespace hex {
|
||||
|
||||
s_providers.erase(it);
|
||||
|
||||
if (it - s_providers.begin() == s_currentProvider)
|
||||
if (it - s_providers.begin() == s_currentProvider && !s_providers.empty())
|
||||
setCurrentProvider(0);
|
||||
|
||||
delete provider;
|
||||
@@ -210,7 +300,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
static float s_globalScale;
|
||||
static float s_globalScale = 1.0;
|
||||
void setGlobalScale(float scale) {
|
||||
s_globalScale = scale;
|
||||
}
|
||||
@@ -228,6 +318,21 @@ namespace hex {
|
||||
s_borderlessWindowMode = enabled;
|
||||
}
|
||||
|
||||
static std::fs::path s_customFontPath;
|
||||
void setCustomFontPath(const std::fs::path &path) {
|
||||
s_customFontPath = path;
|
||||
}
|
||||
|
||||
static float s_fontSize;
|
||||
void setFontSize(float size) {
|
||||
s_fontSize = size;
|
||||
}
|
||||
|
||||
static std::string s_gpuVendor;
|
||||
void setGPUVendor(const std::string &vendor) {
|
||||
s_gpuVendor = vendor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +378,54 @@ namespace hex {
|
||||
|
||||
return initArgs;
|
||||
}
|
||||
|
||||
const std::fs::path &getCustomFontPath() {
|
||||
return impl::s_customFontPath;
|
||||
}
|
||||
|
||||
float getFontSize() {
|
||||
return impl::s_fontSize;
|
||||
}
|
||||
|
||||
|
||||
static Theme s_theme;
|
||||
static bool s_systemThemeDetection;
|
||||
|
||||
void setTheme(Theme theme) {
|
||||
s_theme = theme;
|
||||
|
||||
EventManager::post<EventSettingsChanged>();
|
||||
}
|
||||
|
||||
Theme getTheme() {
|
||||
return s_theme;
|
||||
}
|
||||
|
||||
|
||||
void enableSystemThemeDetection(bool enabled) {
|
||||
s_systemThemeDetection = enabled;
|
||||
|
||||
EventManager::post<EventSettingsChanged>();
|
||||
}
|
||||
|
||||
bool usesSystemThemeDetection() {
|
||||
return s_systemThemeDetection;
|
||||
}
|
||||
|
||||
|
||||
static std::vector<std::fs::path> s_additionalFolderPaths;
|
||||
const std::vector<std::fs::path> &getAdditionalFolderPaths() {
|
||||
return s_additionalFolderPaths;
|
||||
}
|
||||
|
||||
void setAdditionalFolderPaths(const std::vector<std::fs::path> &paths) {
|
||||
s_additionalFolderPaths = paths;
|
||||
}
|
||||
|
||||
|
||||
const std::string &getGPUVendor() {
|
||||
return impl::s_gpuVendor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,17 +3,26 @@
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <dlfcn.h>
|
||||
#include <system_error>
|
||||
|
||||
namespace hex {
|
||||
|
||||
Plugin::Plugin(const std::fs::path &path) : m_path(path) {
|
||||
this->m_handle = dlopen(path.string().c_str(), RTLD_LAZY);
|
||||
#if defined(OS_WINDOWS)
|
||||
this->m_handle = LoadLibraryW(path.c_str());
|
||||
|
||||
if (this->m_handle == nullptr) {
|
||||
log::error("dlopen failed: {}", dlerror());
|
||||
return;
|
||||
}
|
||||
if (this->m_handle == nullptr) {
|
||||
log::error("LoadLibraryW failed: {}!", std::system_category().message(::GetLastError()));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
this->m_handle = dlopen(path.string().c_str(), RTLD_LAZY);
|
||||
|
||||
if (this->m_handle == nullptr) {
|
||||
log::error("dlopen failed: {}!", dlerror());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto pluginName = std::fs::path(path).stem().string();
|
||||
|
||||
@@ -49,8 +58,13 @@ namespace hex {
|
||||
}
|
||||
|
||||
Plugin::~Plugin() {
|
||||
if (this->m_handle != nullptr)
|
||||
dlclose(this->m_handle);
|
||||
#if defined(OS_WINDOWS)
|
||||
if (this->m_handle != nullptr)
|
||||
FreeLibrary(this->m_handle);
|
||||
#else
|
||||
if (this->m_handle != nullptr)
|
||||
dlclose(this->m_handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plugin::initializePlugin() const {
|
||||
@@ -120,7 +134,11 @@ namespace hex {
|
||||
|
||||
|
||||
void *Plugin::getPluginFunction(const std::string &symbol) {
|
||||
return dlsym(this->m_handle, symbol.c_str());
|
||||
#if defined(OS_WINDOWS)
|
||||
return reinterpret_cast<void *>(GetProcAddress(this->m_handle, symbol.c_str()));
|
||||
#else
|
||||
return dlsym(this->m_handle, symbol.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +153,7 @@ namespace hex {
|
||||
|
||||
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
|
||||
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug")
|
||||
PluginManager::s_plugins.emplace_back(pluginPath.path().string());
|
||||
PluginManager::s_plugins.emplace_back(pluginPath.path());
|
||||
}
|
||||
|
||||
if (PluginManager::s_plugins.empty())
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
#include <mbedtls/version.h>
|
||||
#include <mbedtls/base64.h>
|
||||
@@ -15,7 +16,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace hex {
|
||||
|
||||
EncodingFile::EncodingFile(Type type, const std::fs::path &path) {
|
||||
std::ifstream encodingFile(path.c_str());
|
||||
|
||||
auto file = fs::File(path, fs::File::Mode::Read);
|
||||
switch (type) {
|
||||
case Type::Thingy:
|
||||
parseThingyFile(encodingFile);
|
||||
parseThingyFile(file);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -34,22 +31,21 @@ namespace hex {
|
||||
return { ".", 1 };
|
||||
}
|
||||
|
||||
void EncodingFile::parseThingyFile(std::ifstream &content) {
|
||||
for (std::string line; std::getline(content, line);) {
|
||||
void EncodingFile::parseThingyFile(fs::File &file) {
|
||||
for (const auto &line : splitString(file.readString(), "\n")) {
|
||||
|
||||
std::string from, to;
|
||||
{
|
||||
auto delimiterPos = line.find('=', 0);
|
||||
auto delimiterPos = line.find('=');
|
||||
|
||||
if (delimiterPos == std::string::npos)
|
||||
continue;
|
||||
if (delimiterPos >= line.length())
|
||||
continue;
|
||||
|
||||
from = line.substr(0, delimiterPos);
|
||||
to = line.substr(delimiterPos + 1);
|
||||
|
||||
hex::trim(from);
|
||||
hex::trim(to);
|
||||
|
||||
if (from.empty()) continue;
|
||||
if (to.empty()) to = " ";
|
||||
}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
#include <hex/helpers/file.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace hex::fs {
|
||||
|
||||
File::File(const std::fs::path &path, Mode mode) noexcept : m_path(path) {
|
||||
if (mode == File::Mode::Read)
|
||||
this->m_file = fopen64(path.string().c_str(), "rb");
|
||||
else if (mode == File::Mode::Write)
|
||||
this->m_file = fopen64(path.string().c_str(), "r+b");
|
||||
#if defined(OS_WINDOWS)
|
||||
if (mode == File::Mode::Read)
|
||||
this->m_file = _wfopen(path.c_str(), L"rb");
|
||||
else if (mode == File::Mode::Write)
|
||||
this->m_file = _wfopen(path.c_str(), L"r+b");
|
||||
|
||||
if (mode == File::Mode::Create || (mode == File::Mode::Write && this->m_file == nullptr))
|
||||
this->m_file = fopen64(path.string().c_str(), "w+b");
|
||||
if (mode == File::Mode::Create || (mode == File::Mode::Write && this->m_file == nullptr))
|
||||
this->m_file = _wfopen(path.c_str(), L"w+b");
|
||||
#else
|
||||
if (mode == File::Mode::Read)
|
||||
this->m_file = fopen64(path.string().c_str(), "rb");
|
||||
else if (mode == File::Mode::Write)
|
||||
this->m_file = fopen64(path.string().c_str(), "r+b");
|
||||
|
||||
if (mode == File::Mode::Create || (mode == File::Mode::Write && this->m_file == nullptr))
|
||||
this->m_file = fopen64(path.string().c_str(), "w+b");
|
||||
#endif
|
||||
}
|
||||
|
||||
File::File() noexcept {
|
||||
@@ -77,7 +89,22 @@ namespace hex::fs {
|
||||
if (bytes.empty())
|
||||
return "";
|
||||
|
||||
return { reinterpret_cast<char *>(bytes.data()), bytes.size() };
|
||||
auto cString = reinterpret_cast<const char *>(bytes.data());
|
||||
return { cString, std::min(bytes.size(), std::strlen(cString)) };
|
||||
}
|
||||
|
||||
std::u8string File::readU8String(size_t numBytes) {
|
||||
if (!isValid()) return {};
|
||||
|
||||
if (getSize() == 0) return {};
|
||||
|
||||
auto bytes = readBytes(numBytes);
|
||||
|
||||
if (bytes.empty())
|
||||
return u8"";
|
||||
|
||||
auto cString = reinterpret_cast<const char8_t *>(bytes.data());
|
||||
return { cString, std::min(bytes.size(), std::strlen(reinterpret_cast<const char*>(bytes.data()))) };
|
||||
}
|
||||
|
||||
void File::write(const u8 *buffer, size_t size) {
|
||||
@@ -98,6 +125,12 @@ namespace hex::fs {
|
||||
std::fwrite(string.data(), string.size(), 1, this->m_file);
|
||||
}
|
||||
|
||||
void File::write(const std::u8string &string) {
|
||||
if (!isValid()) return;
|
||||
|
||||
std::fwrite(string.data(), string.size(), 1, this->m_file);
|
||||
}
|
||||
|
||||
size_t File::getSize() const {
|
||||
if (!isValid()) return 0;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/fs_macos.h>
|
||||
#include <hex/helpers/fs_macos.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace hex::fs {
|
||||
|
||||
std::optional<std::fs::path> getExecutablePath() {
|
||||
#if defined(OS_WINDOWS)
|
||||
std::string exePath(MAX_PATH, '\0');
|
||||
if (GetModuleFileName(nullptr, exePath.data(), exePath.length()) == 0)
|
||||
std::wstring exePath(MAX_PATH, '\0');
|
||||
if (GetModuleFileNameW(nullptr, exePath.data(), exePath.length()) == 0)
|
||||
return std::nullopt;
|
||||
|
||||
return exePath;
|
||||
@@ -34,7 +34,15 @@ namespace hex::fs {
|
||||
|
||||
return exePath;
|
||||
#elif defined(OS_MACOS)
|
||||
return getMacExecutableDirectoryPath();
|
||||
std::string result;
|
||||
|
||||
{
|
||||
auto string = getMacExecutableDirectoryPath();
|
||||
result = string;
|
||||
macFree(string);
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
@@ -92,8 +100,7 @@ namespace hex::fs {
|
||||
std::vector<std::fs::path> getDefaultPaths(ImHexPath path, bool listNonExisting) {
|
||||
std::vector<std::fs::path> result;
|
||||
const auto exePath = getExecutablePath();
|
||||
const std::string settingName { "hex.builtin.setting.folders" };
|
||||
auto userDirs = ContentRegistry::Settings::read(settingName, settingName, std::vector<std::string> {});
|
||||
auto userDirs = ImHexApi::System::getAdditionalFolderPaths();
|
||||
|
||||
[[maybe_unused]]
|
||||
auto addUserDirs = [&userDirs](auto &paths) {
|
||||
@@ -105,11 +112,11 @@ namespace hex::fs {
|
||||
#if defined(OS_WINDOWS)
|
||||
std::fs::path appDataDir;
|
||||
{
|
||||
LPWSTR wAppDataPath = nullptr;
|
||||
PWSTR wAppDataPath = nullptr;
|
||||
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath)))
|
||||
throw std::runtime_error("Failed to get APPDATA folder path");
|
||||
|
||||
appDataDir = wAppDataPath;
|
||||
appDataDir = std::wstring(wAppDataPath);
|
||||
CoTaskMemFree(wAppDataPath);
|
||||
}
|
||||
|
||||
@@ -122,60 +129,60 @@ namespace hex::fs {
|
||||
case ImHexPath::Patterns:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "patterns").string();
|
||||
return path / "patterns";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::PatternsInclude:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "includes").string();
|
||||
return path / "includes";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Magic:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "magic").string();
|
||||
return path / "magic";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Python:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "python").string();
|
||||
return path / "python";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Plugins:
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "plugins").string();
|
||||
return path / "plugins";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Yara:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "yara").string();
|
||||
return path / "yara";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Config:
|
||||
return { (appDataDir / "imhex" / "config").string() };
|
||||
return { appDataDir / "imhex" / "config" };
|
||||
case ImHexPath::Resources:
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "resources").string();
|
||||
return path / "resources";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Constants:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "constants").string();
|
||||
return path / "constants";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Encodings:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "encodings").string();
|
||||
return path / "encodings";
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Logs:
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "logs").string();
|
||||
return path / "logs";
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@@ -183,25 +190,31 @@ namespace hex::fs {
|
||||
}
|
||||
#elif defined(OS_MACOS)
|
||||
// Get path to special directories
|
||||
const std::fs::path applicationSupportDir(getMacApplicationSupportDirectoryPath());
|
||||
std::string applicationSupportDir;
|
||||
{
|
||||
auto string = getMacApplicationSupportDirectoryPath();
|
||||
applicationSupportDir = string;
|
||||
macFree(string);
|
||||
}
|
||||
const std::fs::path applicationSupportDirPath(applicationSupportDir);
|
||||
|
||||
std::vector<std::fs::path> paths = { applicationSupportDir };
|
||||
std::vector<std::fs::path> paths = { applicationSupportDirPath };
|
||||
|
||||
if (exePath)
|
||||
paths.push_back(exePath->parent_path());
|
||||
if (exePath.has_value())
|
||||
paths.push_back(exePath.value());
|
||||
|
||||
switch (path) {
|
||||
case ImHexPath::Patterns:
|
||||
result.push_back((applicationSupportDir / "patterns").string());
|
||||
result.push_back((applicationSupportDirPath / "patterns").string());
|
||||
break;
|
||||
case ImHexPath::PatternsInclude:
|
||||
result.push_back((applicationSupportDir / "includes").string());
|
||||
result.push_back((applicationSupportDirPath / "includes").string());
|
||||
break;
|
||||
case ImHexPath::Magic:
|
||||
result.push_back((applicationSupportDir / "magic").string());
|
||||
result.push_back((applicationSupportDirPath / "magic").string());
|
||||
break;
|
||||
case ImHexPath::Python:
|
||||
result.push_back((applicationSupportDir / "python").string());
|
||||
result.push_back((applicationSupportDirPath / "python").string());
|
||||
break;
|
||||
case ImHexPath::Plugins:
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
@@ -209,22 +222,22 @@ namespace hex::fs {
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Yara:
|
||||
result.push_back((applicationSupportDir / "yara").string());
|
||||
result.push_back((applicationSupportDirPath / "yara").string());
|
||||
break;
|
||||
case ImHexPath::Config:
|
||||
result.push_back((applicationSupportDir / "config").string());
|
||||
result.push_back((applicationSupportDirPath / "config").string());
|
||||
break;
|
||||
case ImHexPath::Resources:
|
||||
result.push_back((applicationSupportDir / "resources").string());
|
||||
result.push_back((applicationSupportDirPath / "resources").string());
|
||||
break;
|
||||
case ImHexPath::Constants:
|
||||
result.push_back((applicationSupportDir / "constants").string());
|
||||
result.push_back((applicationSupportDirPath / "constants").string());
|
||||
break;
|
||||
case ImHexPath::Encodings:
|
||||
result.push_back((applicationSupportDir / "encodings").string());
|
||||
result.push_back((applicationSupportDirPath / "encodings").string());
|
||||
break;
|
||||
case ImHexPath::Logs:
|
||||
result.push_back((applicationSupportDir / "logs").string());
|
||||
result.push_back((applicationSupportDirPath / "logs").string());
|
||||
break;
|
||||
default:
|
||||
hex::unreachable();
|
||||
@@ -299,4 +312,20 @@ namespace hex::fs {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::fs::path toShortPath(const std::fs::path &path) {
|
||||
#if defined(OS_WINDOWS)
|
||||
size_t size = GetShortPathNameW(path.c_str(), nullptr, 0) * sizeof(TCHAR);
|
||||
if (size == 0)
|
||||
return path;
|
||||
|
||||
std::wstring newPath(size, 0x00);
|
||||
GetShortPathNameW(path.c_str(), newPath.data(), newPath.size());
|
||||
|
||||
return newPath;
|
||||
#else
|
||||
return path;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
44
lib/libimhex/source/helpers/fs_macos.m
Normal file
44
lib/libimhex/source/helpers/fs_macos.m
Normal file
@@ -0,0 +1,44 @@
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
char* getMacExecutableDirectoryPath() {
|
||||
@autoreleasepool {
|
||||
const char *pathString = [[[[[NSBundle mainBundle] executableURL] URLByDeletingLastPathComponent] path] UTF8String];
|
||||
|
||||
char *result = malloc(strlen(pathString) + 1);
|
||||
strcpy(result, pathString);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
char* getMacApplicationSupportDirectoryPath() {
|
||||
@autoreleasepool {
|
||||
NSError* error = nil;
|
||||
NSURL* dirUrl = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
||||
inDomain:NSUserDomainMask
|
||||
appropriateForURL:nil
|
||||
create:YES
|
||||
error:&error];
|
||||
|
||||
if (error != nil) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *pathString = [[[dirUrl URLByAppendingPathComponent:(@"imhex")] path] UTF8String];
|
||||
|
||||
char *result = malloc(strlen(pathString) + 1);
|
||||
strcpy(result, pathString);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void macFree(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,30 +0,0 @@
|
||||
#if defined(OS_MACOS)
|
||||
#include <hex/helpers/fs_macos.h>
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
namespace hex {
|
||||
std::string getMacExecutableDirectoryPath() {
|
||||
@autoreleasepool {
|
||||
return {[[[[[NSBundle mainBundle] executableURL] URLByDeletingLastPathComponent] path] UTF8String]};
|
||||
}
|
||||
}
|
||||
|
||||
std::string getMacApplicationSupportDirectoryPath() {
|
||||
@autoreleasepool {
|
||||
NSError* error = nil;
|
||||
NSURL* dirUrl = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
|
||||
inDomain:NSUserDomainMask
|
||||
appropriateForURL:nil
|
||||
create:YES
|
||||
error:&error];
|
||||
|
||||
if (error != nil) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
return {[[[dirUrl URLByAppendingPathComponent:(@"imhex")] path] UTF8String]};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,240 +0,0 @@
|
||||
#include <hex/helpers/loader_script_handler.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
namespace hex {
|
||||
|
||||
PyObject *LoaderScript::Py_getFilePath(PyObject *self, PyObject *args) {
|
||||
hex::unused(self, args);
|
||||
|
||||
return PyUnicode_FromString(LoaderScript::s_filePath.string().c_str());
|
||||
}
|
||||
|
||||
PyObject *LoaderScript::Py_addPatch(PyObject *self, PyObject *args) {
|
||||
hex::unused(self);
|
||||
|
||||
u64 address;
|
||||
u8 *patches;
|
||||
Py_ssize_t count;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "K|y#", &address, &patches, &count)) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (patches == nullptr || count == 0) {
|
||||
PyErr_SetString(PyExc_TypeError, "Invalid patch provided");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (address >= LoaderScript::s_dataProvider->getActualSize()) {
|
||||
PyErr_SetString(PyExc_IndexError, "address out of range");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LoaderScript::s_dataProvider->write(address, patches, count);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *LoaderScript::Py_addBookmark(PyObject *self, PyObject *args) {
|
||||
hex::unused(self);
|
||||
|
||||
u64 address;
|
||||
size_t size;
|
||||
|
||||
char *name = nullptr;
|
||||
char *comment = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "K|n|s|s", &address, &size, &name, &comment)) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (name == nullptr || comment == nullptr) {
|
||||
PyErr_SetString(PyExc_IndexError, "address out of range");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImHexApi::Bookmarks::add(address, size, name, comment);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *createStructureType(const std::string &keyword, PyObject *args) {
|
||||
auto type = PyTuple_GetItem(args, 0);
|
||||
if (type == nullptr) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto instance = PyObject_CallObject(type, nullptr);
|
||||
if (instance == nullptr) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ON_SCOPE_EXIT { Py_DECREF(instance); };
|
||||
|
||||
if (instance->ob_type->tp_base == nullptr || instance->ob_type->tp_base->tp_name != "ImHexType"s) {
|
||||
PyErr_SetString(PyExc_TypeError, "class type must extend from ImHexType");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto dict = instance->ob_type->tp_dict;
|
||||
if (dict == nullptr) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto annotations = PyDict_GetItemString(dict, "__annotations__");
|
||||
if (annotations == nullptr) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto list = PyDict_Items(annotations);
|
||||
if (list == nullptr) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ON_SCOPE_EXIT { Py_DECREF(list); };
|
||||
|
||||
std::string code = keyword + " " + instance->ob_type->tp_name + " {\n";
|
||||
|
||||
for (Py_ssize_t i = 0; i < PyList_Size(list); i++) {
|
||||
auto item = PyList_GetItem(list, i);
|
||||
|
||||
auto memberName = PyUnicode_AsUTF8(PyTuple_GetItem(item, 0));
|
||||
auto memberType = PyTuple_GetItem(item, 1);
|
||||
if (memberType == nullptr) {
|
||||
PyErr_SetString(PyExc_TypeError, "member needs to have a annotation extending from ImHexType");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Array already is an object
|
||||
if (memberType->ob_type->tp_name == "array"s) {
|
||||
|
||||
auto arrayType = PyObject_GetAttrString(memberType, "array_type");
|
||||
if (arrayType == nullptr) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
code += " "s + arrayType->ob_type->tp_name + " " + memberName;
|
||||
|
||||
auto arraySize = PyObject_GetAttrString(memberType, "size");
|
||||
if (arraySize == nullptr) {
|
||||
PyErr_BadArgument();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PyUnicode_Check(arraySize))
|
||||
code += "["s + PyUnicode_AsUTF8(arraySize) + "];\n";
|
||||
else if (PyLong_Check(arraySize))
|
||||
code += "["s + std::to_string(PyLong_AsLong(arraySize)) + "];\n";
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError, "invalid array size type. Expected string or int");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
auto memberTypeInstance = PyObject_CallObject(memberType, nullptr);
|
||||
if (memberTypeInstance == nullptr || memberTypeInstance->ob_type->tp_base == nullptr || memberTypeInstance->ob_type->tp_base->tp_name != "ImHexType"s) {
|
||||
PyErr_SetString(PyExc_TypeError, "member needs to have a annotation extending from ImHexType");
|
||||
if (memberTypeInstance != nullptr)
|
||||
Py_DECREF(memberTypeInstance);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
code += " "s + memberTypeInstance->ob_type->tp_name + " "s + memberName + ";\n";
|
||||
|
||||
Py_DECREF(memberTypeInstance);
|
||||
}
|
||||
}
|
||||
|
||||
code += "};\n";
|
||||
|
||||
EventManager::post<RequestSetPatternLanguageCode>(code);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *LoaderScript::Py_addStruct(PyObject *self, PyObject *args) {
|
||||
hex::unused(self);
|
||||
|
||||
return createStructureType("struct", args);
|
||||
}
|
||||
|
||||
PyObject *LoaderScript::Py_addUnion(PyObject *self, PyObject *args) {
|
||||
hex::unused(self);
|
||||
|
||||
return createStructureType("union", args);
|
||||
}
|
||||
|
||||
bool LoaderScript::processFile(const std::fs::path &scriptPath) {
|
||||
Py_SetProgramName(Py_DecodeLocale("ImHex", nullptr));
|
||||
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Python)) {
|
||||
if (fs::exists(std::fs::path(dir / "lib" / "python" PYTHON_VERSION_MAJOR_MINOR))) {
|
||||
Py_SetPythonHome(Py_DecodeLocale(dir.string().c_str(), nullptr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PyImport_AppendInittab("_imhex", []() -> PyObject * {
|
||||
static PyMethodDef ImHexMethods[] = {
|
||||
{"get_file_path", &LoaderScript::Py_getFilePath, METH_NOARGS, "Returns the path of the file being loaded."},
|
||||
{ "patch", &LoaderScript::Py_addPatch, METH_VARARGS, "Patches a region of memory" },
|
||||
{ "add_bookmark", &LoaderScript::Py_addBookmark, METH_VARARGS, "Adds a bookmark" },
|
||||
{ "add_struct", &LoaderScript::Py_addStruct, METH_VARARGS, "Adds a struct" },
|
||||
{ "add_union", &LoaderScript::Py_addUnion, METH_VARARGS, "Adds a union" },
|
||||
{ nullptr, nullptr, 0, nullptr }
|
||||
};
|
||||
|
||||
static PyModuleDef ImHexModule = {
|
||||
PyModuleDef_HEAD_INIT, "imhex", nullptr, -1, ImHexMethods, nullptr, nullptr, nullptr, nullptr
|
||||
};
|
||||
|
||||
auto module = PyModule_Create(&ImHexModule);
|
||||
if (module == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return module;
|
||||
});
|
||||
|
||||
Py_Initialize();
|
||||
|
||||
{
|
||||
auto sysPath = PySys_GetObject("path");
|
||||
auto path = PyUnicode_FromString("lib");
|
||||
|
||||
PyList_Insert(sysPath, 0, path);
|
||||
}
|
||||
|
||||
fs::File scriptFile(scriptPath, fs::File::Mode::Read);
|
||||
PyRun_SimpleFile(scriptFile.getHandle(), scriptFile.getPath().string().c_str());
|
||||
|
||||
Py_Finalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,8 +26,9 @@ namespace hex::magic {
|
||||
std::error_code error;
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Magic)) {
|
||||
for (const auto &entry : std::fs::directory_iterator(dir, error)) {
|
||||
if (entry.is_regular_file() && ((sourceFiles && entry.path().extension().empty()) || (!sourceFiles && entry.path().extension() == ".mgc")))
|
||||
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
|
||||
if (entry.is_regular_file() && ((sourceFiles && entry.path().extension().empty()) || (!sourceFiles && entry.path().extension() == ".mgc"))) {
|
||||
magicFiles += fs::toShortPath(entry.path()).string() + MAGIC_PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <cstdio>
|
||||
|
||||
@@ -113,6 +115,8 @@ namespace hex {
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_SSLCERTTYPE, "PEM");
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunction);
|
||||
#endif
|
||||
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_PROXY, Net::s_proxyUrl.c_str());
|
||||
}
|
||||
|
||||
std::optional<i32> Net::execute() {
|
||||
@@ -241,4 +245,10 @@ namespace hex {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string Net::s_proxyUrl;
|
||||
|
||||
void Net::setProxy(const std::string &url) {
|
||||
Net::s_proxyUrl = url;
|
||||
}
|
||||
|
||||
}
|
||||
99
lib/libimhex/source/helpers/tar.cpp
Normal file
99
lib/libimhex/source/helpers/tar.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <hex/helpers/tar.hpp>
|
||||
#include <hex/helpers/literals.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
using namespace hex::literals;
|
||||
|
||||
Tar::Tar(const std::fs::path &path, Mode mode) {
|
||||
if (mode == Tar::Mode::Read)
|
||||
mtar_open(&this->m_ctx, path.string().c_str(), "r");
|
||||
else if (mode == Tar::Mode::Write)
|
||||
mtar_open(&this->m_ctx, path.string().c_str(), "a");
|
||||
else if (mode == Tar::Mode::Create)
|
||||
mtar_open(&this->m_ctx, path.string().c_str(), "w");
|
||||
}
|
||||
|
||||
Tar::~Tar() {
|
||||
mtar_finalize(&this->m_ctx);
|
||||
mtar_close(&this->m_ctx);
|
||||
}
|
||||
|
||||
std::vector<std::fs::path> Tar::listEntries() {
|
||||
std::vector<std::fs::path> result;
|
||||
|
||||
const std::string PaxHeaderName = "@PaxHeader";
|
||||
mtar_header_t header;
|
||||
while (mtar_read_header(&this->m_ctx, &header) != MTAR_ENULLRECORD) {
|
||||
if (header.name != PaxHeaderName) {
|
||||
result.emplace_back(header.name);
|
||||
}
|
||||
|
||||
mtar_next(&this->m_ctx);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> Tar::read(const std::fs::path &path) {
|
||||
mtar_header_t header;
|
||||
mtar_find(&this->m_ctx, path.string().c_str(), &header);
|
||||
|
||||
std::vector<u8> result(header.size, 0x00);
|
||||
mtar_read_data(&this->m_ctx, result.data(), result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Tar::write(const std::fs::path &path, const std::vector<u8> &data) {
|
||||
if (path.has_parent_path()) {
|
||||
std::fs::path pathPart;
|
||||
for (const auto &part : path.parent_path()) {
|
||||
pathPart /= part;
|
||||
mtar_write_dir_header(&this->m_ctx, pathPart.string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
mtar_write_file_header(&this->m_ctx, path.string().c_str(), data.size());
|
||||
mtar_write_data(&this->m_ctx, data.data(), data.size());
|
||||
}
|
||||
|
||||
static void writeFile(mtar_t *ctx, mtar_header_t *header, const std::fs::path &path) {
|
||||
constexpr static u64 BufferSize = 1_MiB;
|
||||
|
||||
fs::File outputFile(path, fs::File::Mode::Create);
|
||||
|
||||
std::vector<u8> buffer;
|
||||
for (u64 offset = 0; offset < header->size; offset += BufferSize) {
|
||||
buffer.resize(std::min<u64>(BufferSize, header->size - offset));
|
||||
|
||||
mtar_read_data(ctx, buffer.data(), buffer.size());
|
||||
outputFile.write(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void Tar::extract(const std::fs::path &path, const std::fs::path &outputPath) {
|
||||
mtar_header_t header;
|
||||
mtar_find(&this->m_ctx, path.string().c_str(), &header);
|
||||
|
||||
writeFile(&this->m_ctx, &header, outputPath);
|
||||
}
|
||||
|
||||
void Tar::extractAll(const std::fs::path &outputPath) {
|
||||
mtar_header_t header;
|
||||
while (mtar_read_header(&this->m_ctx, &header) != MTAR_ENULLRECORD) {
|
||||
const auto filePath = std::fs::absolute(outputPath / std::fs::path(header.name));
|
||||
|
||||
if (filePath.filename() != "@PaxHeader") {
|
||||
|
||||
std::fs::create_directories(filePath.parent_path());
|
||||
|
||||
writeFile(&this->m_ctx, &header, filePath);
|
||||
}
|
||||
|
||||
mtar_next(&this->m_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <filesystem>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
@@ -18,13 +16,10 @@
|
||||
#elif defined(OS_LINUX)
|
||||
#include <unistd.h>
|
||||
#elif defined(OS_MACOS)
|
||||
#include <CoreFoundation/CFBundle.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <hex/helpers/utils_macos.hpp>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
long double operator""_scaled(long double value) {
|
||||
@@ -83,29 +78,34 @@ namespace hex {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string result = hex::format("{0:.2f}", value);
|
||||
std::string result;
|
||||
|
||||
if (unitIndex == 0)
|
||||
result = hex::format("{0:}", value);
|
||||
else
|
||||
result = hex::format("{0:.2f}", value);
|
||||
|
||||
switch (unitIndex) {
|
||||
case 0:
|
||||
result += " Bytes";
|
||||
result += ((value == 1) ? " Byte" : " Bytes");
|
||||
break;
|
||||
case 1:
|
||||
result += " kB";
|
||||
result += " kiB";
|
||||
break;
|
||||
case 2:
|
||||
result += " MB";
|
||||
result += " MiB";
|
||||
break;
|
||||
case 3:
|
||||
result += " GB";
|
||||
result += " GiB";
|
||||
break;
|
||||
case 4:
|
||||
result += " TB";
|
||||
result += " TiB";
|
||||
break;
|
||||
case 5:
|
||||
result += " PB";
|
||||
result += " PiB";
|
||||
break;
|
||||
case 6:
|
||||
result += " EB";
|
||||
result += " EiB";
|
||||
break;
|
||||
default:
|
||||
result = "A lot!";
|
||||
@@ -204,11 +204,15 @@ namespace hex {
|
||||
}
|
||||
|
||||
std::vector<std::string> splitString(const std::string &string, const std::string &delimiter) {
|
||||
size_t start = 0, end;
|
||||
size_t start = 0, end = 0;
|
||||
std::string token;
|
||||
std::vector<std::string> res;
|
||||
|
||||
while ((end = string.find(delimiter, start)) != std::string::npos) {
|
||||
size_t size = end - start;
|
||||
if (start + size > string.length())
|
||||
break;
|
||||
|
||||
token = string.substr(start, end - start);
|
||||
start = end + delimiter.length();
|
||||
res.push_back(token);
|
||||
@@ -248,13 +252,14 @@ namespace hex {
|
||||
|
||||
void runCommand(const std::string &command) {
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
auto result = system(hex::format("start {0}", command).c_str());
|
||||
#elif defined(OS_MACOS)
|
||||
auto result = system(hex::format("open {0}", command).c_str());
|
||||
#elif defined(OS_LINUX)
|
||||
auto result = system(hex::format("xdg-open {0}", command).c_str());
|
||||
#endif
|
||||
#if defined(OS_WINDOWS)
|
||||
auto result = system(hex::format("start {0}", command).c_str());
|
||||
#elif defined(OS_MACOS)
|
||||
auto result = system(hex::format("open {0}", command).c_str());
|
||||
#elif defined(OS_LINUX)
|
||||
auto result = system(hex::format("xdg-open {0}", command).c_str());
|
||||
#endif
|
||||
|
||||
hex::unused(result);
|
||||
}
|
||||
|
||||
@@ -263,18 +268,16 @@ namespace hex {
|
||||
if (url.find("://") == std::string::npos)
|
||||
url = "https://" + url;
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
ShellExecute(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
#elif defined(OS_MACOS)
|
||||
CFURLRef urlRef = CFURLCreateWithBytes(nullptr, reinterpret_cast<u8 *>(url.data()), url.length(), kCFStringEncodingASCII, nullptr);
|
||||
LSOpenCFURLRef(urlRef, nullptr);
|
||||
CFRelease(urlRef);
|
||||
#elif defined(OS_LINUX)
|
||||
auto result = system(hex::format("xdg-open {0}", url).c_str());
|
||||
hex::unused(result);
|
||||
#else
|
||||
#warning "Unknown OS, can't open webpages"
|
||||
#endif
|
||||
#if defined(OS_WINDOWS)
|
||||
ShellExecute(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
#elif defined(OS_MACOS)
|
||||
openWebpageMacos(url.c_str());
|
||||
#elif defined(OS_LINUX)
|
||||
auto result = system(hex::format("xdg-open {0}", url).c_str());
|
||||
hex::unused(result);
|
||||
#else
|
||||
#warning "Unknown OS, can't open webpages"
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string encodeByteString(const std::vector<u8> &bytes) {
|
||||
|
||||
28
lib/libimhex/source/helpers/utils_macos.m
Normal file
28
lib/libimhex/source/helpers/utils_macos.m
Normal file
@@ -0,0 +1,28 @@
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
#include <CoreFoundation/CFBundle.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Foundation/NSUserDefaults.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void openWebpageMacos(const char *url) {
|
||||
CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL);
|
||||
LSOpenCFURLRef(urlRef, NULL);
|
||||
CFRelease(urlRef);
|
||||
}
|
||||
|
||||
bool isMacosSystemDarkModeEnabled() {
|
||||
NSString * appleInterfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
|
||||
|
||||
if (appleInterfaceStyle && [appleInterfaceStyle length] > 0) {
|
||||
return [[appleInterfaceStyle lowercaseString] containsString:@"dark"];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,25 +2,17 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <pl/pattern_language.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
Provider::Provider() {
|
||||
this->m_patches.emplace_back();
|
||||
this->m_patternLanguageRuntime = ContentRegistry::PatternLanguage::createDefaultRuntime(this);
|
||||
|
||||
if (this->hasLoadInterface())
|
||||
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
|
||||
}
|
||||
|
||||
Provider::~Provider() {
|
||||
@@ -63,6 +55,22 @@ namespace hex::prv {
|
||||
patches.insert({ address + size, value });
|
||||
}
|
||||
|
||||
void Provider::remove(u64 offset, size_t size) {
|
||||
auto &patches = getPatches();
|
||||
|
||||
std::vector<std::pair<u64, u8>> patchesToMove;
|
||||
|
||||
for (auto &[address, value] : patches) {
|
||||
if (address > offset)
|
||||
patchesToMove.emplace_back(address, value);
|
||||
}
|
||||
|
||||
for (const auto &[address, value] : patchesToMove)
|
||||
patches.erase(address);
|
||||
for (const auto &[address, value] : patchesToMove)
|
||||
patches.insert({ address - size, value });
|
||||
}
|
||||
|
||||
void Provider::applyOverlays(u64 offset, void *buffer, size_t size) {
|
||||
for (auto &overlay : this->m_overlays) {
|
||||
auto overlayOffset = overlay->getAddress();
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ImGui {
|
||||
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
|
||||
auto &string = *static_cast<std::string *>(data->UserData);
|
||||
|
||||
string.resize(data->BufSize);
|
||||
string.resize(data->BufTextLen);
|
||||
data->Buf = string.data();
|
||||
}
|
||||
|
||||
@@ -191,16 +191,6 @@ namespace ImGui {
|
||||
PopStyleColor();
|
||||
}
|
||||
|
||||
void Disabled(const std::function<void()> &widgets, bool disabled) {
|
||||
if (disabled) {
|
||||
BeginDisabled();
|
||||
widgets();
|
||||
EndDisabled();
|
||||
} else {
|
||||
widgets();
|
||||
}
|
||||
}
|
||||
|
||||
void TextSpinner(const char *label) {
|
||||
ImGui::Text("[%c] %s", "|/-\\"[ImU32(ImGui::GetTime() * 20) % 4], label);
|
||||
}
|
||||
@@ -497,7 +487,7 @@ namespace ImGui {
|
||||
return pressed;
|
||||
}
|
||||
|
||||
bool InputIntegerPrefix(const char *label, const char *prefix, u64 *value, ImGuiInputTextFlags flags) {
|
||||
bool InputIntegerPrefix(const char *label, const char *prefix, void *value, ImGuiDataType type, ImGuiInputTextFlags flags) {
|
||||
auto window = ImGui::GetCurrentWindow();
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const ImGuiStyle &style = GImGui->Style;
|
||||
@@ -510,7 +500,7 @@ namespace ImGui {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + frame_size.x);
|
||||
|
||||
char buf[64];
|
||||
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), ImGuiDataType_U64, value, "%llX");
|
||||
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), type, value, "%llX");
|
||||
|
||||
bool value_changed = false;
|
||||
if (InputTextEx(label, nullptr, buf, IM_ARRAYSIZE(buf), ImVec2(CalcItemWidth() - frame_size.x, label_size.y + style.FramePadding.y * 2.0f), flags))
|
||||
@@ -529,8 +519,12 @@ namespace ImGui {
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
bool InputHexadecimal(const char *label, u32 *value, ImGuiInputTextFlags flags) {
|
||||
return InputIntegerPrefix(label, "0x", value, ImGuiDataType_U32, flags | ImGuiInputTextFlags_CharsHexadecimal);
|
||||
}
|
||||
|
||||
bool InputHexadecimal(const char *label, u64 *value, ImGuiInputTextFlags flags) {
|
||||
return InputIntegerPrefix(label, "0x", value, flags | ImGuiInputTextFlags_CharsHexadecimal);
|
||||
return InputIntegerPrefix(label, "0x", value, ImGuiDataType_U64, flags | ImGuiInputTextFlags_CharsHexadecimal);
|
||||
}
|
||||
|
||||
void SmallProgressBar(float fraction, float yOffset) {
|
||||
@@ -563,4 +557,84 @@ namespace ImGui {
|
||||
return ImGui::InputTextMultiline(label, buffer.data(), buffer.size() + 1, size, ImGuiInputTextFlags_CallbackResize | flags, ImGui::UpdateStringSizeCallback, &buffer);
|
||||
}
|
||||
|
||||
bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) {
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
|
||||
if (format == NULL)
|
||||
format = DataTypeGetInfo(data_type)->PrintFmt;
|
||||
|
||||
char buf[64];
|
||||
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
|
||||
|
||||
bool value_changed = false;
|
||||
if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
|
||||
flags |= ImGuiInputTextFlags_CharsDecimal;
|
||||
flags |= ImGuiInputTextFlags_AutoSelectAll;
|
||||
flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
|
||||
|
||||
if (InputText(label, buf, IM_ARRAYSIZE(buf), flags, callback, user_data))
|
||||
value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format);
|
||||
|
||||
if (value_changed)
|
||||
MarkItemEdited(g.LastItemData.ID);
|
||||
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
void HideTooltip() {
|
||||
char window_name[16];
|
||||
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", GImGui->TooltipOverrideCount);
|
||||
if (ImGuiWindow* window = FindWindowByName(window_name); window != nullptr) {
|
||||
if (window->Active)
|
||||
window->Hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BitCheckbox(const char* label, bool* v) {
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
|
||||
const ImVec2 size = ImVec2(CalcTextSize("0").x + style.FramePadding.x * 2, GetFrameHeight());
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect total_bb(pos, pos + size);
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ItemAdd(total_bb, id))
|
||||
{
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hovered, held;
|
||||
bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
|
||||
if (pressed)
|
||||
{
|
||||
*v = !(*v);
|
||||
MarkItemEdited(id);
|
||||
}
|
||||
|
||||
const ImRect check_bb(pos, pos + size);
|
||||
RenderNavHighlight(total_bb, id);
|
||||
RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
|
||||
|
||||
RenderText(check_bb.Min + style.FramePadding, *v ? "1" : "0");
|
||||
|
||||
ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(label_pos, label);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return pressed;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,15 +8,6 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
std::string View::s_popupMessage;
|
||||
|
||||
std::function<void()> View::s_yesCallback, View::s_noCallback;
|
||||
|
||||
u32 View::s_selectableFileIndex;
|
||||
std::vector<std::fs::path> View::s_selectableFiles;
|
||||
std::function<void(std::fs::path)> View::s_selectableFileOpenCallback;
|
||||
std::vector<nfdfilteritem_t> View::s_selectableFilesValidExtensions;
|
||||
|
||||
ImFontAtlas *View::s_fontAtlas;
|
||||
ImFontConfig View::s_fontConfig;
|
||||
|
||||
@@ -26,123 +17,21 @@ namespace hex {
|
||||
return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable();
|
||||
}
|
||||
|
||||
void View::drawCommonInterfaces() {
|
||||
auto windowSize = ImHexApi::System::getMainWindowSize();
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.info"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.error"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.fatal"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape)) {
|
||||
ImHexApi::Common::closeImHex();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.question"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
View::confirmButtons(
|
||||
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] {
|
||||
s_yesCallback();
|
||||
ImGui::CloseCurrentPopup(); }, [] {
|
||||
s_noCallback();
|
||||
ImGui::CloseCurrentPopup(); });
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
bool opened = true;
|
||||
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.choose_file"_lang, &opened, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
|
||||
if (ImGui::BeginListBox("##files", ImVec2(300_scaled, 0))) {
|
||||
|
||||
u32 index = 0;
|
||||
for (auto &path : View::s_selectableFiles) {
|
||||
if (ImGui::Selectable(path.filename().string().c_str(), index == View::s_selectableFileIndex))
|
||||
View::s_selectableFileIndex = index;
|
||||
index++;
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
if (ImGui::Button("hex.builtin.common.open"_lang)) {
|
||||
View::s_selectableFileOpenCallback(View::s_selectableFiles[View::s_selectableFileIndex]);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("hex.builtin.common.browse"_lang)) {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, View::s_selectableFilesValidExtensions, [](const auto &path) {
|
||||
View::s_selectableFileOpenCallback(path);
|
||||
ImGui::CloseCurrentPopup();
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
void View::showInfoPopup(const std::string &message) {
|
||||
EventManager::post<RequestShowInfoPopup>(message);
|
||||
}
|
||||
|
||||
void View::showMessagePopup(const std::string &message) {
|
||||
s_popupMessage = message;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.info"_lang); });
|
||||
void View::showErrorPopup(const std::string &message) {
|
||||
EventManager::post<RequestShowErrorPopup>(message);
|
||||
}
|
||||
|
||||
void View::showErrorPopup(const std::string &errorMessage) {
|
||||
s_popupMessage = errorMessage;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.error"_lang); });
|
||||
}
|
||||
|
||||
void View::showFatalPopup(const std::string &errorMessage) {
|
||||
s_popupMessage = errorMessage;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.fatal"_lang); });
|
||||
void View::showFatalPopup(const std::string &message) {
|
||||
EventManager::post<RequestShowFatalErrorPopup>(message);
|
||||
}
|
||||
|
||||
void View::showYesNoQuestionPopup(const std::string &message, const std::function<void()> &yesCallback, const std::function<void()> &noCallback) {
|
||||
s_popupMessage = message;
|
||||
EventManager::post<RequestShowYesNoQuestionPopup>(message, yesCallback, noCallback);
|
||||
|
||||
s_yesCallback = yesCallback;
|
||||
s_noCallback = noCallback;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.question"_lang); });
|
||||
}
|
||||
|
||||
void View::showFileChooserPopup(const std::vector<std::fs::path> &paths, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback) {
|
||||
@@ -151,12 +40,7 @@ namespace hex {
|
||||
callback(path);
|
||||
});
|
||||
} else {
|
||||
View::s_selectableFileIndex = 0;
|
||||
View::s_selectableFiles = paths;
|
||||
View::s_selectableFilesValidExtensions = validExtensions;
|
||||
View::s_selectableFileOpenCallback = callback;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.choose_file"_lang); });
|
||||
EventManager::post<RequestShowFileChooserPopup>(paths, validExtensions, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ namespace hex::init {
|
||||
this->m_tasks.emplace_back(taskName, task);
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getGPUVendor() const { return this->m_gpuVendor; }
|
||||
|
||||
private:
|
||||
GLFWwindow *m_window;
|
||||
std::mutex m_progressMutex;
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace hex {
|
||||
std::vector<int> m_pressedKeys;
|
||||
|
||||
std::fs::path m_imguiSettingsPath;
|
||||
|
||||
bool m_mouseButtonDown = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace hex::init {
|
||||
this->initGLFW();
|
||||
this->initImGui();
|
||||
|
||||
this->m_gpuVendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
|
||||
ImHexApi::System::impl::setGPUVendor(reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
|
||||
}
|
||||
|
||||
WindowSplash::~WindowSplash() {
|
||||
@@ -173,18 +173,20 @@ namespace hex::init {
|
||||
|
||||
auto meanScale = std::midpoint(xScale, yScale);
|
||||
|
||||
// On Macs with a retina display (basically all modern ones we care about), the OS reports twice
|
||||
// the actual monitor scale for some obscure reason. Get rid of this here so ImHex doesn't look
|
||||
// extremely huge with native scaling on macOS.
|
||||
#if defined(OS_MACOS)
|
||||
meanScale /= 2;
|
||||
#endif
|
||||
// On Macs with a retina display (basically all modern ones we care about), the OS reports twice
|
||||
// the actual monitor scale for some obscure reason. Get rid of this here so ImHex doesn't look
|
||||
// extremely huge with native scaling on macOS.
|
||||
#if defined(OS_MACOS)
|
||||
meanScale /= 2;
|
||||
#endif
|
||||
|
||||
if (meanScale <= 0) {
|
||||
if (meanScale <= 0.0) {
|
||||
meanScale = 1.0;
|
||||
}
|
||||
|
||||
ImHexApi::System::impl::setGlobalScale(meanScale);
|
||||
} else {
|
||||
ImHexApi::System::impl::setGlobalScale(1.0);
|
||||
}
|
||||
|
||||
this->m_window = glfwCreateWindow(640_scaled, 400_scaled, "Starting ImHex...", nullptr, nullptr);
|
||||
|
||||
@@ -93,23 +93,6 @@ namespace hex::init {
|
||||
auto fonts = IM_NEW(ImFontAtlas)();
|
||||
ImFontConfig cfg = {};
|
||||
|
||||
std::fs::path fontFile = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "");
|
||||
if (!fs::exists(fontFile))
|
||||
fontFile.clear();
|
||||
|
||||
// If no custom font has been specified, search for a file called "font.ttf" in one of the resource folders
|
||||
if (fontFile.empty()) {
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Resources)) {
|
||||
auto path = dir / "font.ttf";
|
||||
if (fs::exists(path)) {
|
||||
log::info("Loading custom front from {}", path.string());
|
||||
|
||||
fontFile = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImVector<ImWchar> ranges;
|
||||
{
|
||||
ImFontGlyphRangesBuilder glyphRangesBuilder;
|
||||
@@ -135,7 +118,8 @@ namespace hex::init {
|
||||
0x0100, 0xFFF0, 0
|
||||
};
|
||||
|
||||
float fontSize = 13.0F * ImHexApi::System::getGlobalScale();
|
||||
auto fontFile = ImHexApi::System::getCustomFontPath();
|
||||
float fontSize = ImHexApi::System::getFontSize();
|
||||
if (fontFile.empty()) {
|
||||
// Load default font if no custom one has been specified
|
||||
|
||||
@@ -147,8 +131,6 @@ namespace hex::init {
|
||||
} else {
|
||||
// Load custom font
|
||||
|
||||
fontSize = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13) * ImHexApi::System::getGlobalScale();
|
||||
|
||||
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
|
||||
cfg.SizePixels = fontSize;
|
||||
|
||||
@@ -170,24 +152,34 @@ namespace hex::init {
|
||||
}
|
||||
|
||||
bool deleteSharedData() {
|
||||
ImHexApi::System::getInitArguments().clear();
|
||||
ImHexApi::Tasks::getDeferredCalls().clear();
|
||||
ImHexApi::HexEditor::impl::getHighlights().clear();
|
||||
ImHexApi::HexEditor::impl::getHighlightingFunctions().clear();
|
||||
|
||||
while (ImHexApi::Provider::isValid())
|
||||
ImHexApi::Provider::remove(ImHexApi::Provider::get());
|
||||
ContentRegistry::Provider::getEntries().clear();
|
||||
|
||||
ImHexApi::System::getInitArguments().clear();
|
||||
ImHexApi::Tasks::getDeferredCalls().clear();
|
||||
ImHexApi::HexEditor::impl::getBackgroundHighlights().clear();
|
||||
ImHexApi::HexEditor::impl::getForegroundHighlights().clear();
|
||||
ImHexApi::HexEditor::impl::getBackgroundHighlightingFunctions().clear();
|
||||
ImHexApi::HexEditor::impl::getForegroundHighlightingFunctions().clear();
|
||||
ImHexApi::HexEditor::impl::getTooltips().clear();
|
||||
ImHexApi::HexEditor::impl::getTooltipFunctions().clear();
|
||||
|
||||
ContentRegistry::Settings::getEntries().clear();
|
||||
ContentRegistry::Settings::getSettingsData().clear();
|
||||
|
||||
ContentRegistry::CommandPaletteCommands::getEntries().clear();
|
||||
ContentRegistry::PatternLanguage::getFunctions().clear();
|
||||
|
||||
for (auto &[name, view] : ContentRegistry::Views::getEntries())
|
||||
delete view;
|
||||
ContentRegistry::Views::getEntries().clear();
|
||||
ContentRegistry::PatternLanguage::getFunctions().clear();
|
||||
ContentRegistry::PatternLanguage::getPragmas().clear();
|
||||
|
||||
{
|
||||
auto &views = ContentRegistry::Views::getEntries();
|
||||
for (auto &[name, view] : views)
|
||||
delete view;
|
||||
views.clear();
|
||||
}
|
||||
|
||||
|
||||
ContentRegistry::Tools::getEntries().clear();
|
||||
ContentRegistry::DataInspector::getEntries().clear();
|
||||
@@ -214,6 +206,13 @@ namespace hex::init {
|
||||
ContentRegistry::DataFormatter::getEntries().clear();
|
||||
ContentRegistry::FileHandler::getEntries().clear();
|
||||
|
||||
{
|
||||
auto &visualizers = ContentRegistry::HexEditor::impl::getVisualizers();
|
||||
for (auto &[name, visualizer] : visualizers)
|
||||
delete visualizer;
|
||||
visualizers.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -286,28 +285,6 @@ namespace hex::init {
|
||||
return false;
|
||||
}
|
||||
|
||||
float interfaceScaling = 1.0F;
|
||||
switch (ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling", 0)) {
|
||||
default:
|
||||
case 0:
|
||||
// Native scaling
|
||||
break;
|
||||
case 1:
|
||||
interfaceScaling = 0.5F;
|
||||
break;
|
||||
case 2:
|
||||
interfaceScaling = 1.0F;
|
||||
break;
|
||||
case 3:
|
||||
interfaceScaling = 1.5F;
|
||||
break;
|
||||
case 4:
|
||||
interfaceScaling = 2.0F;
|
||||
break;
|
||||
}
|
||||
|
||||
ImHexApi::System::impl::setGlobalScale(interfaceScaling);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,17 +25,6 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
init::WindowSplash splashWindow;
|
||||
|
||||
// Intel's OpenGL driver has weird bugs that cause the drawn window to be offset to the bottom right.
|
||||
// This can be fixed by either using Mesa3D's OpenGL Software renderer or by simply disabling it.
|
||||
// If you want to try if it works anyways on your GPU, set the hex.builtin.setting.interface.force_borderless_window_mode setting to 1
|
||||
if (ImHexApi::System::isBorderlessWindowModeEnabled()) {
|
||||
bool isIntelGPU = hex::containsIgnoreCase(splashWindow.getGPUVendor(), "Intel");
|
||||
ImHexApi::System::impl::setBorderlessWindowMode(!isIntelGPU);
|
||||
|
||||
if (isIntelGPU)
|
||||
log::warn("Intel GPU detected! Intel's OpenGL driver has bugs that can cause issues when using ImHex. If you experience any rendering bugs, please try the Mesa3D Software Renderer");
|
||||
}
|
||||
|
||||
for (const auto &[name, task] : init::getInitTasks())
|
||||
splashWindow.addStartupTask(name, task);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
|
||||
@@ -21,7 +22,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void Window::setupNativeWindow() {
|
||||
bool themeFollowSystem = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color") == 0;
|
||||
bool themeFollowSystem = ImHexApi::System::usesSystemThemeDetection();
|
||||
EventManager::subscribe<EventOSThemeChanged>(this, [themeFollowSystem] {
|
||||
if (!themeFollowSystem) return;
|
||||
|
||||
@@ -38,7 +39,7 @@ namespace hex {
|
||||
auto exitCode = WEXITSTATUS(pclose(pipe));
|
||||
if (exitCode != 0) return;
|
||||
|
||||
EventManager::post<RequestChangeTheme>(hex::containsIgnoreCase(result, "dark") ? 1 : 2);
|
||||
EventManager::post<RequestChangeTheme>(hex::containsIgnoreCase(result, "light") ? 2 : 1);
|
||||
});
|
||||
|
||||
if (themeFollowSystem)
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
|
||||
#include <hex/helpers/utils_macos.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -19,12 +21,14 @@ namespace hex {
|
||||
}
|
||||
|
||||
void Window::setupNativeWindow() {
|
||||
bool themeFollowSystem = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color") == 0;
|
||||
bool themeFollowSystem = ImHexApi::System::usesSystemThemeDetection();
|
||||
EventManager::subscribe<EventOSThemeChanged>(this, [themeFollowSystem] {
|
||||
if (!themeFollowSystem) return;
|
||||
|
||||
// TODO: Implement this when MacOS build is working again
|
||||
EventManager::post<RequestChangeTheme>(1);
|
||||
if (!isMacosSystemDarkModeEnabled())
|
||||
EventManager::post<RequestChangeTheme>(2);
|
||||
else
|
||||
EventManager::post<RequestChangeTheme>(1);
|
||||
});
|
||||
|
||||
if (themeFollowSystem)
|
||||
|
||||
@@ -177,9 +177,10 @@ namespace hex {
|
||||
glfwWaitEvents();
|
||||
|
||||
} else {
|
||||
double timeout = (1.0 / 5.0) - (glfwGetTime() - this->m_lastFrameTime);
|
||||
timeout = timeout > 0 ? timeout : 0;
|
||||
glfwWaitEventsTimeout(ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId) || Task::getRunningTaskCount() > 0 ? 0 : timeout);
|
||||
const bool frameRateThrottled = !(ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId) || Task::getRunningTaskCount() > 0 || this->m_mouseButtonDown);
|
||||
const double timeout = std::max(0.0, (1.0 / 5.0) - (glfwGetTime() - this->m_lastFrameTime));
|
||||
|
||||
glfwWaitEventsTimeout(frameRateThrottled ? timeout : 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -347,11 +348,12 @@ namespace hex {
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Plugins, true)) {
|
||||
const auto filePath = path / "builtin.hexplug";
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(path.string().c_str());
|
||||
ImGui::TextUnformatted(filePath.string().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(fs::exists(path) ? ICON_VS_CHECK : ICON_VS_CLOSE);
|
||||
ImGui::TextUnformatted(fs::exists(filePath) ? ICON_VS_CHECK : ICON_VS_CLOSE);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
@@ -419,8 +421,6 @@ namespace hex {
|
||||
calls.clear();
|
||||
}
|
||||
|
||||
View::drawCommonInterfaces();
|
||||
|
||||
for (auto &[name, view] : ContentRegistry::Views::getEntries()) {
|
||||
ImGui::GetCurrentContext()->NextWindowData.ClearFlags();
|
||||
|
||||
@@ -568,23 +568,32 @@ namespace hex {
|
||||
win->frameEnd();
|
||||
});
|
||||
|
||||
glfwSetMouseButtonCallback(this->m_window, [](GLFWwindow *window, int button, int action, int mods) {
|
||||
hex::unused(button, mods);
|
||||
|
||||
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
||||
|
||||
if (action == GLFW_PRESS)
|
||||
win->m_mouseButtonDown = true;
|
||||
else if (action == GLFW_RELEASE)
|
||||
win->m_mouseButtonDown = false;
|
||||
});
|
||||
|
||||
glfwSetKeyCallback(this->m_window, [](GLFWwindow *window, int key, int scancode, int action, int mods) {
|
||||
auto keyName = glfwGetKeyName(key, scancode);
|
||||
if (keyName != nullptr)
|
||||
key = std::toupper(keyName[0]);
|
||||
|
||||
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
if (action == GLFW_PRESS) {
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||
win->m_pressedKeys.push_back(key);
|
||||
io.KeysDown[key] = true;
|
||||
io.KeyCtrl = (mods & GLFW_MOD_CONTROL) != 0;
|
||||
io.KeyShift = (mods & GLFW_MOD_SHIFT) != 0;
|
||||
io.KeyAlt = (mods & GLFW_MOD_ALT) != 0;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
auto &io = ImGui::GetIO();
|
||||
io.KeysDown[key] = false;
|
||||
io.KeyCtrl = (mods & GLFW_MOD_CONTROL) != 0;
|
||||
io.KeyShift = (mods & GLFW_MOD_SHIFT) != 0;
|
||||
@@ -643,8 +652,9 @@ namespace hex {
|
||||
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
{
|
||||
if (glfwGetPrimaryMonitor() != nullptr) {
|
||||
auto sessionType = hex::getEnvironmentVariable("XDG_SESSION_TYPE");
|
||||
|
||||
if (!sessionType || !hex::containsIgnoreCase(*sessionType, "wayland"))
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ add_library(${PROJECT_NAME} SHARED
|
||||
source/content/layouts.cpp
|
||||
source/content/main_menu_items.cpp
|
||||
source/content/welcome_screen.cpp
|
||||
source/content/data_visualizers.cpp
|
||||
source/content/events.cpp
|
||||
source/content/hashes.cpp
|
||||
|
||||
source/content/providers/file_provider.cpp
|
||||
source/content/providers/gdb_provider.cpp
|
||||
@@ -55,6 +58,7 @@ add_library(${PROJECT_NAME} SHARED
|
||||
source/lang/it_IT.cpp
|
||||
source/lang/zh_CN.cpp
|
||||
source/lang/ja_JP.cpp
|
||||
source/lang/pt_BR.cpp
|
||||
)
|
||||
|
||||
# Add additional include directories here #
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace hex::plugin::builtin::prv {
|
||||
|
||||
void resize(size_t newSize) override;
|
||||
void insert(u64 offset, size_t size) override;
|
||||
void remove(u64 offset, size_t size) override;
|
||||
|
||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 offset, const void *buffer, size_t size) override;
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
std::endian m_endian = std::endian::native;
|
||||
ContentRegistry::DataInspector::NumberDisplayStyle m_numberDisplayStyle = ContentRegistry::DataInspector::NumberDisplayStyle::Decimal;
|
||||
bool m_invert = false;
|
||||
|
||||
u64 m_startAddress = 0;
|
||||
size_t m_validBytes = 0;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
#include <array>
|
||||
@@ -16,35 +18,10 @@ namespace hex::plugin::builtin {
|
||||
void drawContent() override;
|
||||
|
||||
private:
|
||||
enum class HashFunctions
|
||||
{
|
||||
Crc8,
|
||||
Crc16,
|
||||
Crc32,
|
||||
Md5,
|
||||
Sha1,
|
||||
Sha224,
|
||||
Sha256,
|
||||
Sha384,
|
||||
Sha512
|
||||
};
|
||||
ContentRegistry::Hashes::Hash *m_selectedHash = nullptr;
|
||||
std::string m_newHashName;
|
||||
|
||||
bool m_shouldInvalidate = true;
|
||||
int m_currHashFunction = 0;
|
||||
u64 m_hashRegion[2] = { 0 };
|
||||
bool m_shouldMatchSelection = false;
|
||||
|
||||
static constexpr std::array hashFunctionNames {
|
||||
std::pair {HashFunctions::Crc8, "CRC8" },
|
||||
std::pair { HashFunctions::Crc16, "CRC16" },
|
||||
std::pair { HashFunctions::Crc32, "CRC32" },
|
||||
std::pair { HashFunctions::Md5, "MD5" },
|
||||
std::pair { HashFunctions::Sha1, "SHA-1" },
|
||||
std::pair { HashFunctions::Sha224, "SHA-224"},
|
||||
std::pair { HashFunctions::Sha256, "SHA-256"},
|
||||
std::pair { HashFunctions::Sha384, "SHA-384"},
|
||||
std::pair { HashFunctions::Sha512, "SHA-512"},
|
||||
};
|
||||
std::vector<ContentRegistry::Hashes::Hash::Function> m_hashFunctions;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,96 +1,144 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/helpers/encoding_file.hpp>
|
||||
|
||||
#include <imgui_memory_editor.h>
|
||||
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::prv {
|
||||
class Provider;
|
||||
}
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
using SearchFunction = std::vector<std::pair<u64, u64>> (*)(prv::Provider *&provider, std::string string);
|
||||
|
||||
struct HighlightBlock {
|
||||
struct Highlight {
|
||||
color_t color;
|
||||
std::vector<std::string> tooltips;
|
||||
};
|
||||
|
||||
constexpr static size_t Size = 0x2000;
|
||||
|
||||
u64 base = 0x00;
|
||||
std::array<Highlight, Size> highlight;
|
||||
};
|
||||
|
||||
class ViewHexEditor : public View {
|
||||
public:
|
||||
ViewHexEditor();
|
||||
~ViewHexEditor() override;
|
||||
|
||||
void drawContent() override;
|
||||
void drawAlwaysVisible() override;
|
||||
|
||||
private:
|
||||
MemoryEditor m_memoryEditor;
|
||||
constexpr static auto InvalidSelection = std::numeric_limits<u64>::max();
|
||||
|
||||
std::vector<char> m_searchStringBuffer;
|
||||
std::vector<char> m_searchHexBuffer;
|
||||
SearchFunction m_searchFunction = nullptr;
|
||||
std::vector<std::pair<u64, u64>> *m_lastSearchBuffer = nullptr;
|
||||
bool m_searchRequested = false;
|
||||
|
||||
i64 m_lastSearchIndex = 0;
|
||||
std::vector<std::pair<u64, u64>> m_lastStringSearch;
|
||||
std::vector<std::pair<u64, u64>> m_lastHexSearch;
|
||||
|
||||
std::string m_gotoAddressInput;
|
||||
bool m_gotoRequested = false;
|
||||
bool m_evaluateGoto = false;
|
||||
|
||||
u64 m_baseAddress = 0;
|
||||
u64 m_resizeSize = 0;
|
||||
|
||||
std::vector<u8> m_dataToSave;
|
||||
std::set<pl::Pattern *> m_highlightedPatterns;
|
||||
|
||||
std::string m_loaderScriptScriptPath;
|
||||
std::string m_loaderScriptFilePath;
|
||||
|
||||
hex::EncodingFile m_currEncodingFile;
|
||||
u8 m_highlightAlpha = 0x80;
|
||||
|
||||
std::list<HighlightBlock> m_highlights;
|
||||
|
||||
bool m_processingImportExport = false;
|
||||
bool m_advancedDecodingEnabled = false;
|
||||
|
||||
void drawSearchPopup();
|
||||
void drawSearchInput(std::vector<char> *currBuffer, ImGuiInputTextFlags flags);
|
||||
void performSearch(const char *buffer);
|
||||
void performSearchNext();
|
||||
void performSearchPrevious();
|
||||
static int inputCallback(ImGuiInputTextCallbackData *data);
|
||||
|
||||
void drawGotoPopup();
|
||||
void drawEditPopup();
|
||||
|
||||
void openFile(const std::fs::path &path);
|
||||
|
||||
void copyBytes() const;
|
||||
void pasteBytes() const;
|
||||
void copyString() const;
|
||||
|
||||
void registerEvents();
|
||||
void registerShortcuts();
|
||||
void registerEvents();
|
||||
void registerMenuItems();
|
||||
|
||||
void drawCell(u64 address, u8 *data, size_t size, bool hovered);
|
||||
void drawPopup();
|
||||
void drawSelectionFrame(u32 x, u32 y, u64 byteAddress, u16 bytesPerCell, const ImVec2 &cellPos, const ImVec2 &cellSize);
|
||||
|
||||
public:
|
||||
void setSelection(const Region ®ion) { this->setSelection(region.getStartAddress(), region.getEndAddress()); }
|
||||
void setSelection(u128 start, u128 end) {
|
||||
if (!ImHexApi::Provider::isValid())
|
||||
return;
|
||||
if (start == InvalidSelection && end == InvalidSelection)
|
||||
return;
|
||||
|
||||
if (start == InvalidSelection)
|
||||
start = end;
|
||||
if (end == InvalidSelection)
|
||||
end = start;
|
||||
|
||||
const size_t maxAddress = ImHexApi::Provider::get()->getActualSize() - 1;
|
||||
|
||||
this->m_selectionChanged = this->m_selectionStart != start || this->m_selectionEnd != end;
|
||||
|
||||
this->m_selectionStart = std::clamp<u128>(start, 0, maxAddress);
|
||||
this->m_selectionEnd = std::clamp<u128>(end, 0, maxAddress);
|
||||
|
||||
if (this->m_selectionChanged) {
|
||||
EventManager::post<EventRegionSelected>(this->getSelection());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] Region getSelection() const {
|
||||
const auto start = std::min(this->m_selectionStart, this->m_selectionEnd);
|
||||
const auto end = std::max(this->m_selectionStart, this->m_selectionEnd);
|
||||
const size_t size = end - start + 1;
|
||||
|
||||
return { start, size };
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isSelectionValid() const {
|
||||
return this->m_selectionStart != InvalidSelection && this->m_selectionEnd != InvalidSelection;
|
||||
}
|
||||
|
||||
void jumpToSelection() {
|
||||
this->m_shouldJumpToSelection = true;
|
||||
}
|
||||
|
||||
void scrollToSelection() {
|
||||
this->m_shouldScrollToSelection = true;
|
||||
}
|
||||
|
||||
void jumpIfOffScreen() {
|
||||
this->m_shouldJumpWhenOffScreen = true;
|
||||
}
|
||||
|
||||
public:
|
||||
class Popup {
|
||||
public:
|
||||
virtual ~Popup() = default;
|
||||
virtual void draw(ViewHexEditor *editor) = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool isAnyPopupOpen() const {
|
||||
return this->m_currPopup != nullptr;
|
||||
}
|
||||
|
||||
template<std::derived_from<Popup> T>
|
||||
[[nodiscard]] bool isPopupOpen() const {
|
||||
return dynamic_cast<T*>(this->m_currPopup.get()) != nullptr;
|
||||
}
|
||||
|
||||
template<std::derived_from<Popup> T, typename ... Args>
|
||||
void openPopup(Args && ...args) {
|
||||
this->m_currPopup = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
this->m_shouldOpenPopup = true;
|
||||
}
|
||||
|
||||
void closePopup() {
|
||||
this->m_currPopup.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
void drawEditor(const ImVec2 &size);
|
||||
void drawFooter(const ImVec2 &size);
|
||||
|
||||
void handleSelection(u64 address, u32 bytesPerCell, const u8 *data, bool cellHovered);
|
||||
|
||||
private:
|
||||
u16 m_bytesPerRow = 16;
|
||||
|
||||
ContentRegistry::HexEditor::DataVisualizer *m_currDataVisualizer;
|
||||
|
||||
bool m_shouldJumpToSelection = false;
|
||||
bool m_shouldScrollToSelection = false;
|
||||
bool m_shouldJumpWhenOffScreen = false;
|
||||
|
||||
bool m_selectionChanged = false;
|
||||
u64 m_selectionStart = InvalidSelection;
|
||||
u64 m_selectionEnd = InvalidSelection;
|
||||
|
||||
u16 m_visibleRowCount = 0;
|
||||
|
||||
std::optional<u64> m_editingAddress;
|
||||
bool m_shouldModifyValue = false;
|
||||
bool m_enteredEditingMode = false;
|
||||
bool m_shouldUpdateEditingValue = false;
|
||||
std::vector<u8> m_editingBytes;
|
||||
|
||||
color_t m_selectionColor = 0x00;
|
||||
bool m_upperCaseHex = true;
|
||||
bool m_grayOutZero = true;
|
||||
bool m_showAscii = true;
|
||||
|
||||
bool m_shouldOpenPopup = false;
|
||||
std::unique_ptr<Popup> m_currPopup;
|
||||
|
||||
std::optional<EncodingFile> m_currCustomEncoding;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include <TextEditor.h>
|
||||
|
||||
namespace pl { class Pattern; }
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewPatternEditor : public View {
|
||||
@@ -23,7 +25,7 @@ namespace hex::plugin::builtin {
|
||||
void drawContent() override;
|
||||
|
||||
private:
|
||||
pl::PatternLanguage *m_parserRuntime;
|
||||
std::unique_ptr<pl::PatternLanguage> m_parserRuntime;
|
||||
|
||||
std::vector<std::fs::path> m_possiblePatternFiles;
|
||||
u32 m_selectedPatternFile = 0;
|
||||
@@ -91,6 +93,8 @@ namespace hex::plugin::builtin {
|
||||
void drawEnvVars(ImVec2 size);
|
||||
void drawVariableSettings(ImVec2 size);
|
||||
|
||||
void drawPatternTooltip(pl::Pattern *pattern);
|
||||
|
||||
void loadPatternFile(const std::fs::path &path);
|
||||
void clearPatterns();
|
||||
|
||||
|
||||
@@ -21,10 +21,12 @@ namespace hex::plugin::builtin {
|
||||
u64 address;
|
||||
size_t size;
|
||||
bool wholeDataMatch;
|
||||
|
||||
u32 highlightId;
|
||||
u32 tooltipId;
|
||||
};
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> m_rules;
|
||||
std::vector<std::pair<std::fs::path, std::fs::path>> m_rules;
|
||||
std::vector<YaraMatch> m_matches;
|
||||
u32 m_selectedRule = 0;
|
||||
bool m_matching = false;
|
||||
|
||||
@@ -11,83 +11,108 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
enum class TokenType
|
||||
{
|
||||
Number,
|
||||
Variable,
|
||||
Function,
|
||||
Operator,
|
||||
Bracket
|
||||
};
|
||||
|
||||
enum class Operator : u16
|
||||
{
|
||||
Invalid = 0x000,
|
||||
Assign = 0x010,
|
||||
Or = 0x020,
|
||||
Xor = 0x030,
|
||||
And = 0x040,
|
||||
BitwiseOr = 0x050,
|
||||
BitwiseXor = 0x060,
|
||||
BitwiseAnd = 0x070,
|
||||
Equals = 0x080,
|
||||
NotEquals = 0x081,
|
||||
GreaterThan = 0x090,
|
||||
LessThan = 0x091,
|
||||
GreaterThanOrEquals = 0x092,
|
||||
LessThanOrEquals = 0x093,
|
||||
ShiftLeft = 0x0A0,
|
||||
ShiftRight = 0x0A1,
|
||||
Addition = 0x0B0,
|
||||
Subtraction = 0x0B1,
|
||||
Multiplication = 0x0C0,
|
||||
Division = 0x0C1,
|
||||
Modulus = 0x0C2,
|
||||
Exponentiation = 0x1D0,
|
||||
Combine = 0x0E0,
|
||||
BitwiseNot = 0x0F0,
|
||||
Not = 0x0F1
|
||||
};
|
||||
|
||||
enum class BracketType : std::uint8_t
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
|
||||
union {
|
||||
long double number;
|
||||
Operator op;
|
||||
BracketType bracketType;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::vector<long double> arguments;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class MathEvaluator {
|
||||
public:
|
||||
MathEvaluator() = default;
|
||||
|
||||
std::optional<long double> evaluate(const std::string &input);
|
||||
std::optional<T> evaluate(const std::string &input);
|
||||
|
||||
void registerStandardVariables();
|
||||
void registerStandardFunctions();
|
||||
|
||||
void setVariable(const std::string &name, long double value);
|
||||
void setFunction(const std::string &name, const std::function<std::optional<long double>(std::vector<long double>)> &function, size_t minNumArgs, size_t maxNumArgs);
|
||||
void setVariable(const std::string &name, T value);
|
||||
void setFunction(const std::string &name, const std::function<std::optional<T>(std::vector<T>)> &function, size_t minNumArgs, size_t maxNumArgs);
|
||||
|
||||
std::unordered_map<std::string, long double> &getVariables() { return this->m_variables; }
|
||||
std::unordered_map<std::string, T> &getVariables() { return this->m_variables; }
|
||||
|
||||
[[nodiscard]] bool hasError() const {
|
||||
return this->m_lastError.has_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::string> getLastError() const {
|
||||
return this->m_lastError;
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<Token> parseInput(std::string input);
|
||||
std::optional<long double> evaluate(std::queue<Token> postfixTokens);
|
||||
void setError(const std::string &error) {
|
||||
this->m_lastError = error;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, long double> m_variables;
|
||||
std::unordered_map<std::string, std::function<std::optional<long double>(std::vector<long double>)>> m_functions;
|
||||
private:
|
||||
enum class TokenType
|
||||
{
|
||||
Number,
|
||||
Variable,
|
||||
Function,
|
||||
Operator,
|
||||
Bracket
|
||||
};
|
||||
|
||||
enum class Operator : u16
|
||||
{
|
||||
Invalid = 0x000,
|
||||
Assign = 0x010,
|
||||
Or = 0x020,
|
||||
Xor = 0x030,
|
||||
And = 0x040,
|
||||
BitwiseOr = 0x050,
|
||||
BitwiseXor = 0x060,
|
||||
BitwiseAnd = 0x070,
|
||||
Equals = 0x080,
|
||||
NotEquals = 0x081,
|
||||
GreaterThan = 0x090,
|
||||
LessThan = 0x091,
|
||||
GreaterThanOrEquals = 0x092,
|
||||
LessThanOrEquals = 0x093,
|
||||
ShiftLeft = 0x0A0,
|
||||
ShiftRight = 0x0A1,
|
||||
Addition = 0x0B0,
|
||||
Subtraction = 0x0B1,
|
||||
Multiplication = 0x0C0,
|
||||
Division = 0x0C1,
|
||||
Modulus = 0x0C2,
|
||||
Exponentiation = 0x1D0,
|
||||
Combine = 0x0E0,
|
||||
BitwiseNot = 0x0F0,
|
||||
Not = 0x0F1
|
||||
};
|
||||
|
||||
enum class BracketType : std::uint8_t
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
|
||||
union {
|
||||
T number;
|
||||
Operator op;
|
||||
BracketType bracketType;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::vector<T> arguments;
|
||||
};
|
||||
|
||||
static i16 comparePrecedence(const Operator &a, const Operator &b);
|
||||
static bool isLeftAssociative(const Operator op);
|
||||
static std::pair<Operator, size_t> toOperator(const std::string &input);
|
||||
|
||||
private:
|
||||
std::optional<std::queue<Token>> parseInput(std::string input);
|
||||
std::optional<std::queue<Token>> toPostfix(std::queue<Token> inputQueue);
|
||||
std::optional<T> evaluate(std::queue<Token> postfixTokens);
|
||||
|
||||
std::unordered_map<std::string, T> m_variables;
|
||||
std::unordered_map<std::string, std::function<std::optional<T>(std::vector<T>)>> m_functions;
|
||||
|
||||
std::optional<std::string> m_lastError;
|
||||
};
|
||||
|
||||
extern template class MathEvaluator<long double>;
|
||||
extern template class MathEvaluator<i128>;
|
||||
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
namespace hex {
|
||||
|
||||
template<typename T>
|
||||
concept ArrayPattern = requires(T pattern, std::function<void(int, pl::Pattern&)> fn) {
|
||||
{ pattern.forEachArrayEntry(fn) } -> std::same_as<void>;
|
||||
concept ArrayPattern = requires(u64 displayEnd, T pattern, std::function<void(int, pl::Pattern&)> fn) {
|
||||
{ pattern.forEachArrayEntry(displayEnd, fn) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
class PatternDrawer : public pl::PatternVisitor {
|
||||
@@ -48,7 +48,7 @@ namespace hex {
|
||||
|
||||
if (opened) {
|
||||
auto& displayEnd = this->getDisplayEnd(pattern);
|
||||
pattern.forEachArrayEntry([&] (u64 idx, auto &entry){
|
||||
pattern.forEachArrayEntry(displayEnd, [&] (u64 idx, auto &entry){
|
||||
this->drawArrayNode(idx, displayEnd, entry);
|
||||
});
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool drawArrayRoot(pl::Pattern& pattern, size_t entryCount, bool isInlined);
|
||||
void drawArrayNode(u64 idx, u64 displayEnd, pl::Pattern& pattern);
|
||||
void drawArrayNode(u64 idx, u64& displayEnd, pl::Pattern& pattern);
|
||||
void drawArrayEnd(pl::Pattern& pattern, bool opened);
|
||||
|
||||
void drawCommentTooltip(const pl::Pattern &pattern) const;
|
||||
|
||||
@@ -16,19 +16,17 @@ namespace hex::plugin::builtin {
|
||||
"#",
|
||||
"hex.builtin.command.calc.desc",
|
||||
[](auto input) {
|
||||
hex::MathEvaluator evaluator;
|
||||
hex::MathEvaluator<long double> evaluator;
|
||||
evaluator.registerStandardVariables();
|
||||
evaluator.registerStandardFunctions();
|
||||
|
||||
std::optional<long double> result;
|
||||
|
||||
try {
|
||||
result = evaluator.evaluate(input);
|
||||
} catch (std::exception &e) { }
|
||||
|
||||
|
||||
result = evaluator.evaluate(input);
|
||||
if (result.has_value())
|
||||
return hex::format("#{0} = {1}", input.data(), result.value());
|
||||
else if (evaluator.hasError())
|
||||
return hex::format("Error: {}", *evaluator.getLastError());
|
||||
else
|
||||
return std::string("???");
|
||||
});
|
||||
|
||||
@@ -84,22 +84,22 @@ namespace hex::plugin::builtin {
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(endian, style);
|
||||
|
||||
std::string binary = "0b";
|
||||
for (u8 i = 0; i < 8; i++)
|
||||
binary += ((buffer[0] << i) & 0x80) == 0 ? '0' : '1';
|
||||
std::string binary = hex::format("0b{:08b}", buffer[0]);
|
||||
|
||||
return [binary] {
|
||||
ImGui::TextUnformatted(binary.c_str());
|
||||
return binary;
|
||||
};
|
||||
}, [](std::string value, std::endian endian) -> std::vector<u8> {
|
||||
}, [](const std::string &value, std::endian endian) -> std::vector<u8> {
|
||||
hex::unused(endian);
|
||||
if (value.starts_with("0b"))
|
||||
value = value.substr(2);
|
||||
|
||||
std::string copy = value;
|
||||
if (copy.starts_with("0b"))
|
||||
copy = copy.substr(2);
|
||||
|
||||
if (value.size() > 8) return { };
|
||||
u8 byte = 0x00;
|
||||
for (char c : value) {
|
||||
for (char c : copy) {
|
||||
byte <<= 1;
|
||||
|
||||
if (c == '1')
|
||||
@@ -214,17 +214,25 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.float16", sizeof(u16),
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(style);
|
||||
auto value = hex::format("{0:G}", hex::changeEndianess(float16ToFloat32(*reinterpret_cast<u16 *>(buffer.data())), endian));
|
||||
u16 result = 0;
|
||||
std::memcpy(&result, buffer.data(), sizeof(u16));
|
||||
|
||||
const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}";
|
||||
|
||||
auto value = hex::format(formatString, float16ToFloat32(hex::changeEndianess(result, endian)));
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
|
||||
}
|
||||
);
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.float", sizeof(float),
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(style);
|
||||
float result = 0;
|
||||
std::memcpy(&result, buffer.data(), sizeof(float));
|
||||
|
||||
auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast<float *>(buffer.data()), endian));
|
||||
const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}";
|
||||
|
||||
auto value = hex::format(formatString, hex::changeEndianess(result, endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
|
||||
},
|
||||
stringToFloat<float>
|
||||
@@ -232,12 +240,12 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.double", sizeof(double),
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(style);
|
||||
|
||||
double result = 0;
|
||||
std::memcpy(&result, buffer.data(), sizeof(double));
|
||||
|
||||
auto value = hex::format("{0:G}", hex::changeEndianess(result, endian));
|
||||
const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}";
|
||||
|
||||
auto value = hex::format(formatString, hex::changeEndianess(result, endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
|
||||
},
|
||||
stringToFloat<double>
|
||||
@@ -245,14 +253,36 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.long_double", sizeof(long double),
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(style);
|
||||
long double result = 0;
|
||||
std::memcpy(&result, buffer.data(), sizeof(long double));
|
||||
|
||||
auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast<long double *>(buffer.data()), endian));
|
||||
const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}";
|
||||
|
||||
auto value = hex::format(formatString, hex::changeEndianess(result, endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
|
||||
},
|
||||
stringToFloat<long double>
|
||||
);
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.bool", sizeof(bool),
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(endian, style);
|
||||
|
||||
std::string value = [buffer] {
|
||||
switch (buffer[0]) {
|
||||
case false:
|
||||
return "false";
|
||||
case true:
|
||||
return "true";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}();
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
|
||||
}
|
||||
);
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.ascii", sizeof(char8_t),
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(endian, style);
|
||||
@@ -284,7 +314,7 @@ namespace hex::plugin::builtin {
|
||||
return [value] { ImGui::TextFormatted("'{0}'", value.c_str()); return value; };
|
||||
},
|
||||
[](const std::string &value, std::endian endian) -> std::vector<u8> {
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter("");
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter("Invalid");
|
||||
|
||||
std::vector<u8> bytes;
|
||||
auto wideString = converter.from_bytes(value.c_str());
|
||||
@@ -321,19 +351,25 @@ namespace hex::plugin::builtin {
|
||||
[](auto buffer, auto endian, auto style) {
|
||||
hex::unused(endian, style);
|
||||
|
||||
Region currSelection = { 0, 0 };
|
||||
EventManager::post<QuerySelection>(currSelection);
|
||||
auto currSelection = ImHexApi::HexEditor::getSelection();
|
||||
|
||||
constexpr static auto MaxStringLength = 32;
|
||||
|
||||
std::vector<u8> stringBuffer(std::min<size_t>(MaxStringLength, currSelection.size), 0x00);
|
||||
ImHexApi::Provider::get()->read(currSelection.address, stringBuffer.data(), stringBuffer.size());
|
||||
std::string value, copyValue;
|
||||
|
||||
auto value = hex::encodeByteString(stringBuffer);
|
||||
auto copyValue = hex::encodeByteString(buffer);
|
||||
if (currSelection.has_value()) {
|
||||
std::vector<u8> stringBuffer(std::min<size_t>(MaxStringLength, currSelection->size), 0x00);
|
||||
ImHexApi::Provider::get()->read(currSelection->address, stringBuffer.data(), stringBuffer.size());
|
||||
|
||||
if (currSelection.size > MaxStringLength)
|
||||
value += "...";
|
||||
value = hex::encodeByteString(stringBuffer);
|
||||
copyValue = hex::encodeByteString(buffer);
|
||||
|
||||
if (currSelection->size > MaxStringLength)
|
||||
value += "...";
|
||||
} else {
|
||||
value = "";
|
||||
copyValue = "";
|
||||
}
|
||||
|
||||
return [value, copyValue] { ImGui::TextFormatted("\"{0}\"", value.c_str()); return copyValue; };
|
||||
},
|
||||
@@ -395,6 +431,42 @@ namespace hex::plugin::builtin {
|
||||
|
||||
#endif
|
||||
|
||||
struct DOSDate {
|
||||
unsigned day : 5;
|
||||
unsigned month : 4;
|
||||
unsigned year : 7;
|
||||
};
|
||||
|
||||
struct DOSTime {
|
||||
unsigned seconds : 5;
|
||||
unsigned minutes : 6;
|
||||
unsigned hours : 5;
|
||||
};
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.dos_date", sizeof(DOSDate), [](auto buffer, auto endian, auto style) {
|
||||
hex::unused(style);
|
||||
|
||||
DOSDate date = { };
|
||||
std::memcpy(&date, buffer.data(), sizeof(DOSDate));
|
||||
date = hex::changeEndianess(date, endian);
|
||||
|
||||
auto value = hex::format("{}/{}/{}", date.day, date.month, date.year + 1980);
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
|
||||
});
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.dos_time", sizeof(DOSTime), [](auto buffer, auto endian, auto style) {
|
||||
hex::unused(style);
|
||||
|
||||
DOSTime time = { };
|
||||
std::memcpy(&time, buffer.data(), sizeof(DOSTime));
|
||||
time = hex::changeEndianess(time, endian);
|
||||
|
||||
auto value = hex::format("{:02}:{:02}:{:02}", time.hours, time.minutes, time.seconds * 2);
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
|
||||
});
|
||||
|
||||
ContentRegistry::DataInspector::add("hex.builtin.inspector.guid", sizeof(GUID), [](auto buffer, auto endian, auto style) {
|
||||
hex::unused(style);
|
||||
|
||||
|
||||
@@ -777,7 +777,16 @@ namespace hex::plugin::builtin {
|
||||
NodeVisualizerDigram() : Node("hex.builtin.nodes.visualizer.digram.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { }
|
||||
|
||||
void drawNode() override {
|
||||
const auto viewSize = scaled({ 200, 200 });
|
||||
drawDigram(scaled({ 200, 200 }));
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
drawDigram(scaled({ 600, 600 }));
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void drawDigram(const ImVec2 &viewSize) {
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImU32(ImColor(0, 0, 0)));
|
||||
if (ImGui::BeginChild("##visualizer", viewSize, true)) {
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
@@ -860,7 +869,15 @@ namespace hex::plugin::builtin {
|
||||
NodeVisualizerLayeredDistribution() : Node("hex.builtin.nodes.visualizer.layered_dist.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { }
|
||||
|
||||
void drawNode() override {
|
||||
const auto viewSize = scaled({ 200, 200 });
|
||||
drawLayeredDistribution(scaled({ 200, 200 }));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
drawLayeredDistribution(scaled({ 600, 600 }));
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void drawLayeredDistribution(const ImVec2 &viewSize) {
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImU32(ImColor(0, 0, 0)));
|
||||
if (ImGui::BeginChild("##visualizer", viewSize, true)) {
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
@@ -944,6 +961,11 @@ namespace hex::plugin::builtin {
|
||||
|
||||
void drawNode() override {
|
||||
ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.aspectRatio() * 200, 200)));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.aspectRatio() * 600, 600)));
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void process() override {
|
||||
@@ -964,8 +986,18 @@ namespace hex::plugin::builtin {
|
||||
NodeVisualizerByteDistribution() : Node("hex.builtin.nodes.visualizer.byte_distribution.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { }
|
||||
|
||||
void drawNode() override {
|
||||
drawPlot(scaled({ 400, 300 }));
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
drawPlot(scaled({ 700, 550 }));
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void drawPlot(const ImVec2 &viewSize) {
|
||||
ImPlot::SetNextPlotLimits(0, 256, 0.5, float(*std::max_element(this->m_counts.begin(), this->m_counts.end())) * 1.1F, ImGuiCond_Always);
|
||||
if (ImPlot::BeginPlot("##distribution", "Address", "Count", scaled(ImVec2(400, 300)), ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock | ImPlotAxisFlags_LogScale, ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoTickLabels)) {
|
||||
if (ImPlot::BeginPlot("##distribution", "Address", "Count", viewSize, ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock | ImPlotAxisFlags_LogScale, ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoTickLabels)) {
|
||||
static auto x = [] {
|
||||
std::array<ImU64, 256> result { 0 };
|
||||
std::iota(result.begin(), result.end(), 0);
|
||||
|
||||
272
plugins/builtin/source/content/data_visualizers.cpp
Normal file
272
plugins/builtin/source/content/data_visualizers.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
template<typename T>
|
||||
constexpr ImGuiDataType getImGuiDataType() {
|
||||
if constexpr (std::same_as<T, u8>) return ImGuiDataType_U8;
|
||||
else if constexpr (std::same_as<T, u16>) return ImGuiDataType_U16;
|
||||
else if constexpr (std::same_as<T, u32>) return ImGuiDataType_U32;
|
||||
else if constexpr (std::same_as<T, u64>) return ImGuiDataType_U64;
|
||||
else if constexpr (std::same_as<T, i8>) return ImGuiDataType_S8;
|
||||
else if constexpr (std::same_as<T, i16>) return ImGuiDataType_S16;
|
||||
else if constexpr (std::same_as<T, i32>) return ImGuiDataType_S32;
|
||||
else if constexpr (std::same_as<T, i64>) return ImGuiDataType_S64;
|
||||
else if constexpr (std::same_as<T, float>) return ImGuiDataType_Float;
|
||||
else if constexpr (std::same_as<T, double>) return ImGuiDataType_Double;
|
||||
else static_assert(hex::always_false<T>::value, "Invalid data type!");
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
class DataVisualizerHexadecimal : public hex::ContentRegistry::HexEditor::DataVisualizer {
|
||||
public:
|
||||
DataVisualizerHexadecimal() : DataVisualizer(ByteCount, CharCount) { }
|
||||
|
||||
void draw(u64 address, const u8 *data, size_t size, bool upperCase) override {
|
||||
hex::unused(address);
|
||||
|
||||
if (size == ByteCount)
|
||||
ImGui::Text(getFormatString(upperCase), *reinterpret_cast<const T*>(data));
|
||||
else
|
||||
ImGui::TextFormatted("{: {}s}", CharCount);
|
||||
}
|
||||
|
||||
bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override {
|
||||
hex::unused(address, startedEditing);
|
||||
|
||||
if (size == ByteCount) {
|
||||
return drawDefaultEditingTextBox(address, getFormatString(upperCase), getImGuiDataType<T>(), data, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static inline auto ByteCount = sizeof(T);
|
||||
constexpr static inline auto CharCount = ByteCount * 2;
|
||||
|
||||
const static inline auto FormattingUpperCase = hex::format("%0{}X", CharCount);
|
||||
const static inline auto FormattingLowerCase = hex::format("%0{}x", CharCount);
|
||||
|
||||
const char *getFormatString(bool upperCase) {
|
||||
if (upperCase)
|
||||
return FormattingUpperCase.c_str();
|
||||
else
|
||||
return FormattingLowerCase.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class DataVisualizerHexii : public hex::ContentRegistry::HexEditor::DataVisualizer {
|
||||
public:
|
||||
DataVisualizerHexii() : DataVisualizer(ByteCount, CharCount) { }
|
||||
|
||||
void draw(u64 address, const u8 *data, size_t size, bool upperCase) override {
|
||||
hex::unused(address);
|
||||
|
||||
if (size == ByteCount) {
|
||||
const u8 c = data[0];
|
||||
switch (c) {
|
||||
case 0x00:
|
||||
ImGui::Text(" ");
|
||||
break;
|
||||
case 0xFF:
|
||||
ImGui::TextDisabled("##");
|
||||
break;
|
||||
case ' ' ... '~':
|
||||
ImGui::Text(".%c", c);
|
||||
break;
|
||||
default:
|
||||
ImGui::Text(getFormatString(upperCase), c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
ImGui::TextFormatted("{: {}s}", CharCount);
|
||||
}
|
||||
|
||||
bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override {
|
||||
hex::unused(address, startedEditing);
|
||||
|
||||
if (size == ByteCount) {
|
||||
return drawDefaultEditingTextBox(address, getFormatString(upperCase), getImGuiDataType<u8>(), data, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static inline auto ByteCount = 1;
|
||||
constexpr static inline auto CharCount = ByteCount * 2;
|
||||
|
||||
const static inline auto FormattingUpperCase = hex::format("%0{}X", CharCount);
|
||||
const static inline auto FormattingLowerCase = hex::format("%0{}x", CharCount);
|
||||
|
||||
const char *getFormatString(bool upperCase) {
|
||||
if (upperCase)
|
||||
return FormattingUpperCase.c_str();
|
||||
else
|
||||
return FormattingLowerCase.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
template<std::integral T>
|
||||
class DataVisualizerDecimal : public hex::ContentRegistry::HexEditor::DataVisualizer {
|
||||
public:
|
||||
DataVisualizerDecimal() : DataVisualizer(ByteCount, CharCount) { }
|
||||
|
||||
void draw(u64 address, const u8 *data, size_t size, bool upperCase) override {
|
||||
hex::unused(address, upperCase);
|
||||
|
||||
if (size == ByteCount) {
|
||||
if (std::is_signed<T>::value)
|
||||
ImGui::Text(getFormatString(), static_cast<i64>(*reinterpret_cast<const T*>(data)));
|
||||
else
|
||||
ImGui::Text(getFormatString(), static_cast<u64>(*reinterpret_cast<const T*>(data)));
|
||||
}
|
||||
else
|
||||
ImGui::TextFormatted("{: {}s}", CharCount);
|
||||
}
|
||||
|
||||
bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override {
|
||||
hex::unused(address, upperCase, startedEditing);
|
||||
|
||||
if (size == ByteCount) {
|
||||
return ImGui::InputScalar(
|
||||
"##hex_input",
|
||||
getImGuiDataType<T>(),
|
||||
data,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
DataVisualizer::TextInputFlags);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static inline auto ByteCount = sizeof(T);
|
||||
constexpr static inline auto CharCount = std::numeric_limits<T>::digits10 + 2;
|
||||
|
||||
const static inline auto FormatString = hex::format("%{}{}", CharCount, std::is_signed<T>::value ? "lld" : "llu");
|
||||
|
||||
const char *getFormatString() {
|
||||
return FormatString.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
template<std::floating_point T>
|
||||
class DataVisualizerFloatingPoint : public hex::ContentRegistry::HexEditor::DataVisualizer {
|
||||
public:
|
||||
DataVisualizerFloatingPoint() : DataVisualizer(ByteCount, CharCount) { }
|
||||
|
||||
void draw(u64 address, const u8 *data, size_t size, bool upperCase) override {
|
||||
hex::unused(address);
|
||||
|
||||
if (size == ByteCount)
|
||||
ImGui::Text(getFormatString(upperCase), *reinterpret_cast<const T*>(data));
|
||||
else
|
||||
ImGui::TextFormatted("{: {}s}", CharCount);
|
||||
}
|
||||
|
||||
bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override {
|
||||
hex::unused(address, upperCase, startedEditing);
|
||||
|
||||
if (size == ByteCount) {
|
||||
return ImGui::InputScalar(
|
||||
"##hex_input",
|
||||
getImGuiDataType<T>(),
|
||||
data,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
DataVisualizer::TextInputFlags | ImGuiInputTextFlags_CharsScientific);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static inline auto ByteCount = sizeof(T);
|
||||
constexpr static inline auto CharCount = 14;
|
||||
|
||||
const static inline auto FormatStringUpperCase = hex::format("%{}E", CharCount);
|
||||
const static inline auto FormatStringLowerCase = hex::format("%{}e", CharCount);
|
||||
|
||||
const char *getFormatString(bool upperCase) {
|
||||
if (upperCase)
|
||||
return FormatStringUpperCase.c_str();
|
||||
else
|
||||
return FormatStringLowerCase.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class DataVisualizerRGBA8 : public hex::ContentRegistry::HexEditor::DataVisualizer {
|
||||
public:
|
||||
DataVisualizerRGBA8() : DataVisualizer(4, 2) { }
|
||||
|
||||
void draw(u64 address, const u8 *data, size_t size, bool upperCase) override {
|
||||
hex::unused(address, upperCase);
|
||||
|
||||
if (size == 4)
|
||||
ImGui::ColorButton("##color", ImColor(data[0], data[1], data[2], data[3]), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
else
|
||||
ImGui::ColorButton("##color", ImColor(0, 0, 0, 0xFF), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
}
|
||||
|
||||
bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override {
|
||||
hex::unused(address, data, size, upperCase);
|
||||
|
||||
if (startedEditing) {
|
||||
this->m_currColor = { float(data[0]) / 0xFF, float(data[1]) / 0xFF, float(data[2]) / 0xFF, float(data[3]) / 0xFF };
|
||||
ImGui::OpenPopup("##color_popup");
|
||||
}
|
||||
|
||||
ImGui::ColorButton("##color", ImColor(this->m_currColor[0], this->m_currColor[1], this->m_currColor[2], this->m_currColor[3]), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
|
||||
if (ImGui::BeginPopup("##color_popup")) {
|
||||
if (ImGui::ColorPicker4("##picker", this->m_currColor.data(), ImGuiColorEditFlags_AlphaBar)) {
|
||||
for (u8 i = 0; i < 4; i++)
|
||||
data[i] = this->m_currColor[i] * 0xFF;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<float, 4> m_currColor;
|
||||
|
||||
};
|
||||
|
||||
void registerDataVisualizers() {
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerHexadecimal<u8>>("hex.builtin.visualizer.hexadecimal.8bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerHexadecimal<u16>>("hex.builtin.visualizer.hexadecimal.16bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerHexadecimal<u32>>("hex.builtin.visualizer.hexadecimal.32bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerHexadecimal<u64>>("hex.builtin.visualizer.hexadecimal.64bit");
|
||||
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<u8>>("hex.builtin.visualizer.decimal.unsigned.8bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<u16>>("hex.builtin.visualizer.decimal.unsigned.16bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<u32>>("hex.builtin.visualizer.decimal.unsigned.32bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<u64>>("hex.builtin.visualizer.decimal.unsigned.64bit");
|
||||
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<i8>>("hex.builtin.visualizer.decimal.signed.8bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<i16>>("hex.builtin.visualizer.decimal.signed.16bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<i32>>("hex.builtin.visualizer.decimal.signed.32bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerDecimal<i64>>("hex.builtin.visualizer.decimal.signed.64bit");
|
||||
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerFloatingPoint<float>>("hex.builtin.visualizer.floating_point.32bit");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerFloatingPoint<double>>("hex.builtin.visualizer.floating_point.64bit");
|
||||
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerRGBA8>("hex.builtin.visualizer.rgba8");
|
||||
ContentRegistry::HexEditor::addDataVisualizer<DataVisualizerHexii>("hex.builtin.visualizer.hexii");
|
||||
}
|
||||
|
||||
}
|
||||
99
plugins/builtin/source/content/events.cpp
Normal file
99
plugins/builtin/source/content/events.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/helpers/project_file_handler.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "content/providers/file_provider.hpp"
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
static void openFile(const std::fs::path &path) {
|
||||
hex::prv::Provider *provider = nullptr;
|
||||
EventManager::post<RequestCreateProvider>("hex.builtin.provider.file", &provider);
|
||||
|
||||
if (auto fileProvider = dynamic_cast<prv::FileProvider *>(provider)) {
|
||||
fileProvider->setPath(path);
|
||||
if (!fileProvider->open()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
|
||||
ImHexApi::Provider::remove(provider);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!provider->isWritable()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.read_only"_lang);
|
||||
}
|
||||
|
||||
if (!provider->isAvailable()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
|
||||
ImHexApi::Provider::remove(provider);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectFile::setFilePath(path);
|
||||
|
||||
EventManager::post<EventFileLoaded>(path);
|
||||
EventManager::post<EventDataChanged>();
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
void registerEventHandlers() {
|
||||
EventManager::subscribe<EventProjectFileLoad>([]() {
|
||||
EventManager::post<RequestOpenFile>(ProjectFile::getFilePath());
|
||||
});
|
||||
|
||||
EventManager::subscribe<EventWindowClosing>([](GLFWwindow *window) {
|
||||
if (ProjectFile::hasUnsavedChanges()) {
|
||||
glfwSetWindowShouldClose(window, GLFW_FALSE);
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.popup.exit_application.title"_lang); });
|
||||
}
|
||||
});
|
||||
|
||||
EventManager::subscribe<RequestOpenFile>(openFile);
|
||||
|
||||
EventManager::subscribe<RequestOpenWindow>([](const std::string &name) {
|
||||
if (name == "Create File") {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) {
|
||||
fs::File file(path, fs::File::Mode::Create);
|
||||
|
||||
if (!file.isValid()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.create"_lang);
|
||||
return;
|
||||
}
|
||||
|
||||
file.setSize(1);
|
||||
|
||||
EventManager::post<RequestOpenFile>(path);
|
||||
});
|
||||
} else if (name == "Open File") {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
|
||||
EventManager::post<RequestOpenFile>(path);
|
||||
});
|
||||
} else if (name == "Open Project") {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} },
|
||||
[](const auto &path) {
|
||||
ProjectFile::load(path);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
EventManager::subscribe<EventProviderChanged>([](auto, auto) {
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
});
|
||||
|
||||
EventManager::subscribe<EventProviderCreated>([](hex::prv::Provider *provider) {
|
||||
if (provider->hasLoadInterface())
|
||||
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
140
plugins/builtin/source/content/hashes.cpp
Normal file
140
plugins/builtin/source/content/hashes.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
#include <hex/helpers/crypto.hpp>
|
||||
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class HashMD5 : public ContentRegistry::Hashes::Hash {
|
||||
public:
|
||||
HashMD5() : Hash("hex.builtin.hash.md5") {}
|
||||
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto array = crypt::md5(provider, region.address, region.size);
|
||||
|
||||
return { array.begin(), array.end() };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class HashSHA1 : public ContentRegistry::Hashes::Hash {
|
||||
public:
|
||||
HashSHA1() : Hash("hex.builtin.hash.sha1") {}
|
||||
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto array = crypt::sha1(provider, region.address, region.size);
|
||||
|
||||
return { array.begin(), array.end() };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class HashSHA224 : public ContentRegistry::Hashes::Hash {
|
||||
public:
|
||||
HashSHA224() : Hash("hex.builtin.hash.sha224") {}
|
||||
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto array = crypt::sha224(provider, region.address, region.size);
|
||||
|
||||
return { array.begin(), array.end() };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class HashSHA256 : public ContentRegistry::Hashes::Hash {
|
||||
public:
|
||||
HashSHA256() : Hash("hex.builtin.hash.sha256") {}
|
||||
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto array = crypt::sha256(provider, region.address, region.size);
|
||||
|
||||
return { array.begin(), array.end() };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class HashSHA384 : public ContentRegistry::Hashes::Hash {
|
||||
public:
|
||||
HashSHA384() : Hash("hex.builtin.hash.sha384") {}
|
||||
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto array = crypt::sha384(provider, region.address, region.size);
|
||||
|
||||
return { array.begin(), array.end() };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class HashSHA512 : public ContentRegistry::Hashes::Hash {
|
||||
public:
|
||||
HashSHA512() : Hash("hex.builtin.hash.sha512") {}
|
||||
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto array = crypt::sha512(provider, region.address, region.size);
|
||||
|
||||
return { array.begin(), array.end() };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class HashCRC : public ContentRegistry::Hashes::Hash {
|
||||
public:
|
||||
using CRCFunction = T(*)(prv::Provider*&, u64, size_t, u32, u32, u32, bool, bool);
|
||||
HashCRC(const std::string &name, const CRCFunction &crcFunction, u32 polynomial, u32 initialValue, u32 xorOut)
|
||||
: Hash(name), m_crcFunction(crcFunction), m_polynomial(polynomial), m_initialValue(initialValue), m_xorOut(xorOut) {}
|
||||
|
||||
void draw() override {
|
||||
ImGui::InputHexadecimal("hex.builtin.hash.crc.poly"_lang, &this->m_polynomial);
|
||||
ImGui::InputHexadecimal("hex.builtin.hash.crc.iv"_lang, &this->m_initialValue);
|
||||
ImGui::InputHexadecimal("hex.builtin.hash.crc.xor_out"_lang, &this->m_xorOut);
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Checkbox("hex.builtin.hash.crc.refl_in"_lang, &this->m_reflectIn);
|
||||
ImGui::Checkbox("hex.builtin.hash.crc.refl_out"_lang, &this->m_reflectOut);
|
||||
}
|
||||
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [hash = *this](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto result = hash.m_crcFunction(provider, region.address, region.size, hash.m_polynomial, hash.m_initialValue, hash.m_xorOut, hash.m_reflectIn, hash.m_reflectOut);
|
||||
|
||||
std::vector<u8> bytes(sizeof(result), 0x00);
|
||||
std::memcpy(bytes.data(), &result, bytes.size());
|
||||
|
||||
return bytes;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
CRCFunction m_crcFunction;
|
||||
|
||||
u32 m_polynomial;
|
||||
u32 m_initialValue;
|
||||
u32 m_xorOut;
|
||||
bool m_reflectIn = false, m_reflectOut = false;
|
||||
};
|
||||
|
||||
void registerHashes() {
|
||||
ContentRegistry::Hashes::add<HashMD5>();
|
||||
|
||||
ContentRegistry::Hashes::add<HashSHA1>();
|
||||
ContentRegistry::Hashes::add<HashSHA224>();
|
||||
ContentRegistry::Hashes::add<HashSHA256>();
|
||||
ContentRegistry::Hashes::add<HashSHA384>();
|
||||
ContentRegistry::Hashes::add<HashSHA512>();
|
||||
|
||||
ContentRegistry::Hashes::add<HashCRC<u16>>("hex.builtin.hash.crc8", crypt::crc8, 0x07, 0x0000, 0x0000);
|
||||
ContentRegistry::Hashes::add<HashCRC<u16>>("hex.builtin.hash.crc16", crypt::crc16, 0x8005, 0x0000, 0x0000);
|
||||
ContentRegistry::Hashes::add<HashCRC<u32>>("hex.builtin.hash.crc32", crypt::crc32, 0x04C1'1DB7, 0xFFFF'FFFF, 0xFFFF'FFFF);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,18 +4,267 @@
|
||||
#include <implot.h>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/helpers/project_file_handler.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/crypto.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
static bool g_demoWindowOpen = false;
|
||||
|
||||
void registerMainMenuEntries() {
|
||||
|
||||
static void createFileMenu() {
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.file", 1000);
|
||||
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1050, [&] {
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.open_file"_lang, "CTRL + O")) {
|
||||
|
||||
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
|
||||
EventManager::post<RequestOpenFile>(path);
|
||||
});
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("hex.builtin.menu.file.open_other"_lang)) {
|
||||
|
||||
for (const auto &unlocalizedProviderName : ContentRegistry::Provider::getEntries()) {
|
||||
if (ImGui::MenuItem(LangEntry(unlocalizedProviderName))) {
|
||||
EventManager::post<RequestCreateProvider>(unlocalizedProviderName, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
});
|
||||
|
||||
/* File open, quit imhex */
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1150, [&] {
|
||||
bool providerValid = ImHexApi::Provider::isValid();
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.close"_lang, "", false, providerValid)) {
|
||||
EventManager::post<EventFileUnloaded>();
|
||||
ImHexApi::Provider::remove(ImHexApi::Provider::get());
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.quit"_lang, "", false)) {
|
||||
ImHexApi::Common::closeImHex();
|
||||
}
|
||||
});
|
||||
|
||||
/* Project open / save */
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1250, [&] {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
bool providerValid = ImHexApi::Provider::isValid();
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.open_project"_lang, "")) {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"}
|
||||
},
|
||||
[](const auto &path) {
|
||||
ProjectFile::load(path);
|
||||
});
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.save_project"_lang, "", false, providerValid && provider->isWritable())) {
|
||||
if (ProjectFile::getProjectFilePath() == "") {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"}
|
||||
},
|
||||
[](std::fs::path path) {
|
||||
if (path.extension() != ".hexproj") {
|
||||
path.replace_extension(".hexproj");
|
||||
}
|
||||
|
||||
ProjectFile::store(path);
|
||||
});
|
||||
} else
|
||||
ProjectFile::store();
|
||||
}
|
||||
});
|
||||
|
||||
/* Import / Export */
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1300, [&] {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
bool providerValid = ImHexApi::Provider::isValid();
|
||||
|
||||
/* Import */
|
||||
if (ImGui::BeginMenu("hex.builtin.menu.file.import"_lang)) {
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.import.base64"_lang)) {
|
||||
|
||||
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
|
||||
fs::File inputFile(path, fs::File::Mode::Read);
|
||||
if (!inputFile.isValid()) {
|
||||
View::showErrorPopup("hex.builtin.menu.file.import.base64.popup.open_error"_lang);
|
||||
return;
|
||||
}
|
||||
|
||||
auto base64 = inputFile.readBytes();
|
||||
|
||||
if (!base64.empty()) {
|
||||
auto data = crypt::decode64(base64);
|
||||
|
||||
if (data.empty())
|
||||
View::showErrorPopup("hex.builtin.menu.file.import.base64.popup.import_error"_lang);
|
||||
else {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const std::fs::path &path) {
|
||||
fs::File outputFile(path, fs::File::Mode::Create);
|
||||
|
||||
if (!outputFile.isValid())
|
||||
View::showErrorPopup("hex.builtin.menu.file.import.base64.popup.import_error"_lang);
|
||||
|
||||
outputFile.write(data);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
View::showErrorPopup("hex.builtin.popup.file_open_error"_lang);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.import.ips"_lang, nullptr, false)) {
|
||||
|
||||
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
|
||||
std::thread([path] {
|
||||
auto task = ImHexApi::Tasks::createTask("hex.builtin.common.processing", 0);
|
||||
|
||||
auto patchData = fs::File(path, fs::File::Mode::Read).readBytes();
|
||||
auto patch = hex::loadIPSPatch(patchData);
|
||||
|
||||
task.setMaxValue(patch.size());
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
u64 progress = 0;
|
||||
for (auto &[address, value] : patch) {
|
||||
provider->addPatch(address, &value, 1);
|
||||
progress++;
|
||||
task.update(progress);
|
||||
}
|
||||
|
||||
provider->createUndoPoint();
|
||||
}).detach();
|
||||
});
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.import.ips32"_lang, nullptr, false)) {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
|
||||
std::thread([path] {
|
||||
auto task = ImHexApi::Tasks::createTask("hex.builtin.common.processing", 0);
|
||||
|
||||
auto patchData = fs::File(path, fs::File::Mode::Read).readBytes();
|
||||
auto patch = hex::loadIPS32Patch(patchData);
|
||||
|
||||
task.setMaxValue(patch.size());
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
u64 progress = 0;
|
||||
for (auto &[address, value] : patch) {
|
||||
provider->addPatch(address, &value, 1);
|
||||
progress++;
|
||||
task.update(progress);
|
||||
}
|
||||
|
||||
provider->createUndoPoint();
|
||||
}).detach();
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
||||
/* Export */
|
||||
if (ImGui::BeginMenu("hex.builtin.menu.file.export"_lang, providerValid && provider->isWritable())) {
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.export.ips"_lang, nullptr, false)) {
|
||||
Patches patches = provider->getPatches();
|
||||
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
|
||||
u8 value = 0;
|
||||
provider->read(0x00454F45, &value, sizeof(u8));
|
||||
patches[0x00454F45] = value;
|
||||
}
|
||||
|
||||
std::thread([patches] {
|
||||
auto task = ImHexApi::Tasks::createTask("hex.builtin.common.processing", 0);
|
||||
|
||||
auto data = generateIPSPatch(patches);
|
||||
|
||||
ImHexApi::Tasks::doLater([data] {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const auto &path) {
|
||||
auto file = fs::File(path, fs::File::Mode::Create);
|
||||
if (!file.isValid()) {
|
||||
View::showErrorPopup("hex.builtin.menu.file.export.base64.popup.export_error"_lang);
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(data);
|
||||
});
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.export.ips32"_lang, nullptr, false)) {
|
||||
Patches patches = provider->getPatches();
|
||||
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
|
||||
u8 value = 0;
|
||||
provider->read(0x45454F45, &value, sizeof(u8));
|
||||
patches[0x45454F45] = value;
|
||||
}
|
||||
|
||||
std::thread([patches] {
|
||||
auto task = ImHexApi::Tasks::createTask("hex.builtin.common.processing", 0);
|
||||
|
||||
auto data = generateIPS32Patch(patches);
|
||||
|
||||
ImHexApi::Tasks::doLater([data] {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const auto &path) {
|
||||
auto file = fs::File(path, fs::File::Mode::Create);
|
||||
if (!file.isValid()) {
|
||||
View::showErrorPopup("hex.builtin.menu.file.export.popup.create"_lang);
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(data);
|
||||
});
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void createEditMenu() {
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.edit", 2000);
|
||||
|
||||
/* Provider Undo / Redo */
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1000, [&] {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
bool providerValid = ImHexApi::Provider::isValid();
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.edit.undo"_lang, "CTRL + Z", false, providerValid && provider->canUndo()))
|
||||
provider->undo();
|
||||
if (ImGui::MenuItem("hex.builtin.menu.edit.redo"_lang, "CTRL + Y", false, providerValid && provider->canRedo()))
|
||||
provider->redo();
|
||||
});
|
||||
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1050, [&] {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
bool providerValid = ImHexApi::Provider::isValid();
|
||||
auto selection = ImHexApi::HexEditor::getSelection();
|
||||
|
||||
if (ImGui::MenuItem("hex.builtin.menu.edit.bookmark"_lang, nullptr, false, selection.has_value() && providerValid)) {
|
||||
auto base = provider->getBaseAddress();
|
||||
|
||||
ImHexApi::Bookmarks::add(base + selection->getStartAddress(), selection->size, {}, {});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
static void createViewMenu() {
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.view", 3000);
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.layout", 4000);
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.help", 5000);
|
||||
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.view", 1000, [] {
|
||||
for (auto &[name, view] : ContentRegistry::Views::getEntries()) {
|
||||
@@ -24,11 +273,15 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
});
|
||||
|
||||
#if defined(DEBUG)
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.view", 2000, [] {
|
||||
ImGui::MenuItem("hex.builtin.menu.view.demo"_lang, "", &g_demoWindowOpen);
|
||||
});
|
||||
#endif
|
||||
#if defined(DEBUG)
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.view", 2000, [] {
|
||||
ImGui::MenuItem("hex.builtin.menu.view.demo"_lang, "", &g_demoWindowOpen);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
static void createLayoutMenu() {
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.layout", 4000);
|
||||
|
||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.layout", 1000, [] {
|
||||
for (auto &[layoutName, func] : ContentRegistry::Interface::getLayouts()) {
|
||||
@@ -46,7 +299,20 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void createHelpMenu() {
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.help", 5000);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void registerMainMenuEntries() {
|
||||
createFileMenu();
|
||||
createEditMenu();
|
||||
createViewMenu();
|
||||
createLayoutMenu();
|
||||
createHelpMenu();
|
||||
|
||||
(void)EventManager::subscribe<EventFrameEnd>([] {
|
||||
if (g_demoWindowOpen) {
|
||||
|
||||
@@ -1,248 +1,18 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/net.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <pl/token.hpp>
|
||||
#include <pl/log_console.hpp>
|
||||
#include <pl/evaluator.hpp>
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/args.h>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
std::string format(const auto ¶ms) {
|
||||
auto format = pl::Token::literalToString(params[0], true);
|
||||
std::string message;
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> formatArgs;
|
||||
|
||||
for (u32 i = 1; i < params.size(); i++) {
|
||||
auto ¶m = params[i];
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](pl::Pattern *value) {
|
||||
formatArgs.push_back(value->toString());
|
||||
},
|
||||
[&](auto &&value) {
|
||||
formatArgs.push_back(value);
|
||||
} },
|
||||
param);
|
||||
}
|
||||
|
||||
try {
|
||||
return fmt::vformat(format, formatArgs);
|
||||
} catch (fmt::format_error &error) {
|
||||
pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void registerPatternLanguageFunctions() {
|
||||
using namespace pl;
|
||||
using FunctionParameterCount = pl::api::FunctionParameterCount;
|
||||
|
||||
pl::api::Namespace nsStd = { "builtin", "std" };
|
||||
{
|
||||
/* print(format, args...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "print", FunctionParameterCount::moreThan(0), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ctx->getConsole().log(LogConsole::Level::Info, format(params));
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* format(format, args...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "format", FunctionParameterCount::moreThan(0), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return format(params);
|
||||
});
|
||||
|
||||
/* env(name) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "env", FunctionParameterCount::exactly(1), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto name = Token::literalToString(params[0], false);
|
||||
|
||||
auto env = ctx->getEnvVariable(name);
|
||||
if (env)
|
||||
return env;
|
||||
else {
|
||||
ctx->getConsole().log(LogConsole::Level::Warning, hex::format("environment variable '{}' does not exist", name));
|
||||
return "";
|
||||
}
|
||||
});
|
||||
|
||||
/* pack_size(...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "sizeof_pack", FunctionParameterCount::atLeast(0), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return u128(params.size());
|
||||
});
|
||||
|
||||
/* error(message) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "error", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
LogConsole::abortEvaluation(Token::literalToString(params[0], true));
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* warning(message) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "warning", FunctionParameterCount::exactly(1), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ctx->getConsole().log(LogConsole::Level::Warning, Token::literalToString(params[0], true));
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
api::Namespace nsStdMem = { "builtin", "std", "mem" };
|
||||
{
|
||||
|
||||
/* base_address() */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "base_address", FunctionParameterCount::none(), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
hex::unused(params);
|
||||
|
||||
return u128(ctx->getDataBaseAddress());
|
||||
});
|
||||
|
||||
/* size() */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "size", FunctionParameterCount::none(), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
hex::unused(params);
|
||||
|
||||
return u128(ctx->getDataSize());
|
||||
});
|
||||
|
||||
/* find_sequence_in_range(occurrence_index, start_offset, end_offset, bytes...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "find_sequence_in_range", FunctionParameterCount::moreThan(3), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto occurrenceIndex = Token::literalToUnsigned(params[0]);
|
||||
auto offsetFrom = Token::literalToUnsigned(params[1]);
|
||||
auto offsetTo = Token::literalToUnsigned(params[2]);
|
||||
|
||||
std::vector<u8> sequence;
|
||||
for (u32 i = 3; i < params.size(); i++) {
|
||||
auto byte = Token::literalToUnsigned(params[i]);
|
||||
|
||||
if (byte > 0xFF)
|
||||
LogConsole::abortEvaluation(hex::format("byte #{} value out of range: {} > 0xFF", i, u64(byte)));
|
||||
|
||||
sequence.push_back(u8(byte & 0xFF));
|
||||
}
|
||||
|
||||
std::vector<u8> bytes(sequence.size(), 0x00);
|
||||
u32 occurrences = 0;
|
||||
const u64 bufferSize = ctx->getDataSize();
|
||||
const u64 endOffset = offsetTo <= offsetFrom ? bufferSize : std::min(bufferSize, u64(offsetTo));
|
||||
for (u64 offset = offsetFrom; offset < endOffset - sequence.size(); offset++) {
|
||||
ctx->readData(offset, bytes.data(), bytes.size());
|
||||
|
||||
if (bytes == sequence) {
|
||||
if (occurrences < occurrenceIndex) {
|
||||
occurrences++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return u128(offset);
|
||||
}
|
||||
}
|
||||
|
||||
return i128(-1);
|
||||
});
|
||||
|
||||
/* read_unsigned(address, size) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_unsigned", FunctionParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto address = Token::literalToUnsigned(params[0]);
|
||||
auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
if (size > 16)
|
||||
LogConsole::abortEvaluation("read size out of range");
|
||||
|
||||
u128 result = 0;
|
||||
ctx->readData(address, &result, size);
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/* read_signed(address, size) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_signed", FunctionParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto address = Token::literalToUnsigned(params[0]);
|
||||
auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
if (size > 16)
|
||||
LogConsole::abortEvaluation("read size out of range");
|
||||
|
||||
i128 value;
|
||||
ctx->readData(address, &value, size);
|
||||
return hex::signExtend(size * 8, value);
|
||||
});
|
||||
|
||||
/* read_string(address, size) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_string", FunctionParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto address = Token::literalToUnsigned(params[0]);
|
||||
auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
std::string result(size, '\x00');
|
||||
ctx->readData(address, result.data(), size);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
api::Namespace nsStdString = { "builtin", "std", "string" };
|
||||
{
|
||||
/* length(string) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "length", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
|
||||
return u128(string.length());
|
||||
});
|
||||
|
||||
/* at(string, index) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "at", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
auto index = Token::literalToSigned(params[1]);
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
const auto signIndex = index >> (sizeof(index) * 8 - 1);
|
||||
const auto absIndex = (index ^ signIndex) - signIndex;
|
||||
#else
|
||||
const auto absIndex = std::abs(index);
|
||||
#endif
|
||||
|
||||
if (absIndex > string.length())
|
||||
LogConsole::abortEvaluation("character index out of range");
|
||||
|
||||
if (index >= 0)
|
||||
return char(string[index]);
|
||||
else
|
||||
return char(string[string.length() - -index]);
|
||||
});
|
||||
|
||||
/* substr(string, pos, count) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "substr", FunctionParameterCount::exactly(3), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
auto pos = Token::literalToUnsigned(params[1]);
|
||||
auto size = Token::literalToUnsigned(params[2]);
|
||||
|
||||
if (pos > string.length())
|
||||
LogConsole::abortEvaluation("character index out of range");
|
||||
|
||||
return string.substr(pos, size);
|
||||
});
|
||||
|
||||
/* parse_int(string, base) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "parse_int", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
auto base = Token::literalToUnsigned(params[1]);
|
||||
|
||||
return i128(std::strtoll(string.c_str(), nullptr, base));
|
||||
});
|
||||
|
||||
/* parse_float(string) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "parse_float", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
|
||||
return double(std::strtod(string.c_str(), nullptr));
|
||||
});
|
||||
}
|
||||
|
||||
api::Namespace nsStdHttp = { "builtin", "std", "http" };
|
||||
{
|
||||
/* get(url) */
|
||||
@@ -253,267 +23,5 @@ namespace hex::plugin::builtin {
|
||||
return net.getString(url).get().body;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
api::Namespace nsStdFile = { "builtin", "std", "file" };
|
||||
{
|
||||
static u32 fileCounter = 0;
|
||||
static std::map<u32, fs::File> openFiles;
|
||||
|
||||
/* open(path, mode) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "open", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto path = Token::literalToString(params[0], false);
|
||||
const auto modeEnum = Token::literalToUnsigned(params[1]);
|
||||
|
||||
fs::File::Mode mode;
|
||||
switch (modeEnum) {
|
||||
case 1:
|
||||
mode = fs::File::Mode::Read;
|
||||
break;
|
||||
case 2:
|
||||
mode = fs::File::Mode::Write;
|
||||
break;
|
||||
case 3:
|
||||
mode = fs::File::Mode::Create;
|
||||
break;
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid file open mode");
|
||||
}
|
||||
|
||||
fs::File file(path, mode);
|
||||
|
||||
if (!file.isValid())
|
||||
LogConsole::abortEvaluation(hex::format("failed to open file {}", path));
|
||||
|
||||
fileCounter++;
|
||||
openFiles.emplace(std::pair { fileCounter, std::move(file) });
|
||||
|
||||
return u128(fileCounter);
|
||||
});
|
||||
|
||||
/* close(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "close", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
openFiles.erase(file);
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* read(file, size) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "read", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
return openFiles[file].readString(size);
|
||||
});
|
||||
|
||||
/* write(file, data) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "write", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto data = Token::literalToString(params[1], true);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
openFiles[file].write(data);
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* seek(file, offset) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "seek", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto offset = Token::literalToUnsigned(params[1]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
openFiles[file].seek(offset);
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* size(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "size", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
return u128(openFiles[file].getSize());
|
||||
});
|
||||
|
||||
/* resize(file, size) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "resize", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
openFiles[file].setSize(size);
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* flush(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "flush", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
openFiles[file].flush();
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* remove(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "remove", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
LogConsole::abortEvaluation("failed to access invalid file");
|
||||
|
||||
openFiles[file].remove();
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
api::Namespace nsStdMath = { "builtin", "std", "math" };
|
||||
{
|
||||
/* floor(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "floor", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::floor(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* ceil(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "ceil", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::ceil(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* round(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "round", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::round(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* trunc(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "trunc", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::trunc(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
|
||||
/* log10(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "log10", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::log10(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* log2(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "log2", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::log2(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* ln(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "ln", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::log(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
|
||||
/* fmod(x, y) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "fmod", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::fmod(Token::literalToFloatingPoint(params[0]), Token::literalToFloatingPoint(params[1]));
|
||||
});
|
||||
|
||||
/* pow(base, exp) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "pow", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::pow(Token::literalToFloatingPoint(params[0]), Token::literalToFloatingPoint(params[1]));
|
||||
});
|
||||
|
||||
/* sqrt(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sqrt", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::sqrt(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* cbrt(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cbrt", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::cbrt(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
|
||||
/* sin(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sin", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::sin(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* cos(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cos", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::cos(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* tan(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "tan", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::tan(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* asin(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "asin", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::asin(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* acos(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "acos", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::acos(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* atan(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atan", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::atan(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* atan2(y, x) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atan", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::atan2(Token::literalToFloatingPoint(params[0]), Token::literalToFloatingPoint(params[1]));
|
||||
});
|
||||
|
||||
|
||||
/* sinh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sinh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::sinh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* cosh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cosh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::cosh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* tanh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "tanh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::tanh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* asinh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "asinh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::asinh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* acosh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "acosh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::acosh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* atanh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atanh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::atanh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ namespace hex::plugin::builtin {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguage::addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
|
||||
}
|
||||
|
||||
}
|
||||
@@ -123,7 +123,7 @@ namespace hex::plugin::builtin::prv {
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
size_t readSize = (position >= (offset + buffer.size())) ? buffer.size() : (position - offset);
|
||||
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
||||
|
||||
position -= readSize;
|
||||
|
||||
@@ -135,6 +135,28 @@ namespace hex::plugin::builtin::prv {
|
||||
Provider::insert(offset, size);
|
||||
}
|
||||
|
||||
void FileProvider::remove(u64 offset, size_t size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
this->resize(oldSize + size);
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
|
||||
const auto newSize = oldSize - size;
|
||||
auto position = offset;
|
||||
while (position < newSize) {
|
||||
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
|
||||
|
||||
this->readRaw(position + size, buffer.data(), readSize);
|
||||
this->writeRaw(position, buffer.data(), readSize);
|
||||
|
||||
position += readSize;
|
||||
}
|
||||
|
||||
this->resize(newSize);
|
||||
|
||||
Provider::insert(offset, size);
|
||||
}
|
||||
|
||||
size_t FileProvider::getRealTimeSize() {
|
||||
#if defined(OS_LINUX)
|
||||
if (struct stat newStats; (this->m_fileStatsValid = fstat(this->m_file, &newStats) == 0)) {
|
||||
|
||||
@@ -226,12 +226,10 @@ namespace hex::plugin::builtin::prv {
|
||||
}
|
||||
|
||||
std::string GDBProvider::getName() const {
|
||||
std::string address, port;
|
||||
std::string address = "-";
|
||||
std::string port = "-";
|
||||
|
||||
if (!this->isConnected()) {
|
||||
address = "-";
|
||||
port = "-";
|
||||
} else {
|
||||
if (this->isConnected()) {
|
||||
address = this->m_ipAddress;
|
||||
port = std::to_string(this->m_port);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
#include <hex/api/localization.hpp>
|
||||
|
||||
#include <hex/helpers/net.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
#include <fonts/codicons_font.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -52,6 +55,11 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::Combo(name.data(), &selection, themes, IM_ARRAYSIZE(themes))) {
|
||||
setting = selection;
|
||||
|
||||
ImHexApi::System::enableSystemThemeDetection(selection == 0);
|
||||
if (selection != 0)
|
||||
ImHexApi::System::setTheme(static_cast<ImHexApi::System::Theme>(selection));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -124,6 +132,17 @@ namespace hex::plugin::builtin {
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.wiki_explain_language", "en", [](auto name, nlohmann::json &setting) {
|
||||
static auto lang = std::string(setting);
|
||||
|
||||
if (ImGui::InputText(name.data(), lang, ImGuiInputTextFlags_CharsNoBlank)) {
|
||||
setting = std::string(lang.c_str()); // remove following zero bytes
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.fps", 60, [](auto name, nlohmann::json &setting) {
|
||||
static int fps = static_cast<int>(setting);
|
||||
|
||||
@@ -137,18 +156,32 @@ namespace hex::plugin::builtin {
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.highlight_alpha", 0x80, [](auto name, nlohmann::json &setting) {
|
||||
static int alpha = static_cast<int>(setting);
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.highlight_color", 0x60C08080, [](auto name, nlohmann::json &setting) {
|
||||
static auto color = static_cast<color_t>(setting);
|
||||
|
||||
std::array<float, 4> colorArray = {
|
||||
((color >> 0) & 0x000000FF) / float(0xFF),
|
||||
((color >> 8) & 0x000000FF) / float(0xFF),
|
||||
((color >> 16) & 0x000000FF) / float(0xFF),
|
||||
((color >> 24) & 0x000000FF) / float(0xFF)
|
||||
};
|
||||
|
||||
if (ImGui::ColorEdit4(name.data(), colorArray.data(), ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoDragDrop | ImGuiColorEditFlags_NoInputs)) {
|
||||
color =
|
||||
(color_t(colorArray[0] * 0xFF) << 0) |
|
||||
(color_t(colorArray[1] * 0xFF) << 8) |
|
||||
(color_t(colorArray[2] * 0xFF) << 16) |
|
||||
(color_t(colorArray[3] * 0xFF) << 24);
|
||||
|
||||
setting = color;
|
||||
|
||||
if (ImGui::SliderInt(name.data(), &alpha, 0x00, 0xFF)) {
|
||||
setting = alpha;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.column_count", 16, [](auto name, nlohmann::json &setting) {
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row", 16, [](auto name, nlohmann::json &setting) {
|
||||
static int columns = static_cast<int>(setting);
|
||||
|
||||
if (ImGui::SliderInt(name.data(), &columns, 1, 32)) {
|
||||
@@ -159,17 +192,6 @@ namespace hex::plugin::builtin {
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.hexii", 0, [](auto name, nlohmann::json &setting) {
|
||||
static bool hexii = static_cast<int>(setting);
|
||||
|
||||
if (ImGui::Checkbox(name.data(), &hexii)) {
|
||||
setting = static_cast<int>(hexii);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.ascii", 1, [](auto name, nlohmann::json &setting) {
|
||||
static bool ascii = static_cast<int>(setting);
|
||||
|
||||
@@ -214,18 +236,30 @@ namespace hex::plugin::builtin {
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.extra_info", 1, [](auto name, nlohmann::json &setting) {
|
||||
static bool extraInfos = static_cast<int>(setting);
|
||||
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.visualizer", "hex.builtin.visualizer.hexadecimal.8bit", [](auto name, nlohmann::json &setting) {
|
||||
auto &visualizers = ContentRegistry::HexEditor::impl::getVisualizers();
|
||||
|
||||
if (ImGui::Checkbox(name.data(), &extraInfos)) {
|
||||
setting = static_cast<int>(extraInfos);
|
||||
return true;
|
||||
auto selectedVisualizer = setting;
|
||||
|
||||
bool result = false;
|
||||
if (ImGui::BeginCombo(name.data(), LangEntry(selectedVisualizer))) {
|
||||
|
||||
for (const auto &[unlocalizedName, visualizer] : visualizers) {
|
||||
if (ImGui::Selectable(LangEntry(unlocalizedName))) {
|
||||
setting = unlocalizedName;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
/* Fonts */
|
||||
|
||||
static std::string fontPath;
|
||||
ContentRegistry::Settings::add(
|
||||
"hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "", [](auto name, nlohmann::json &setting) {
|
||||
@@ -271,6 +305,8 @@ namespace hex::plugin::builtin {
|
||||
true);
|
||||
|
||||
|
||||
/* Folders */
|
||||
|
||||
static const std::string dirsSetting { "hex.builtin.setting.folders" };
|
||||
|
||||
ContentRegistry::Settings::addCategoryDescription(dirsSetting, "hex.builtin.setting.folders.description");
|
||||
@@ -278,17 +314,34 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::Settings::add(dirsSetting, dirsSetting, std::vector<std::string> {}, [](auto name, nlohmann::json &setting) {
|
||||
hex::unused(name);
|
||||
|
||||
static std::vector<std::string> folders = setting;
|
||||
static size_t currentItemIndex = 0;
|
||||
static size_t currentItemIndex = 0;
|
||||
static std::vector<std::fs::path> folders = [&setting]{
|
||||
std::vector<std::fs::path> result;
|
||||
|
||||
std::vector<std::u8string> paths = setting;
|
||||
for (const auto &path : paths)
|
||||
result.emplace_back(path);
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
bool result = false;
|
||||
|
||||
auto writeSetting = [&setting]{
|
||||
std::vector<std::u8string> folderStrings;
|
||||
for (const auto &folder : folders)
|
||||
folderStrings.push_back(folder.u8string());
|
||||
setting = folderStrings;
|
||||
|
||||
ImHexApi::System::setAdditionalFolderPaths(folders);
|
||||
};
|
||||
|
||||
if (!ImGui::BeginListBox("", ImVec2(-38, -FLT_MIN))) {
|
||||
return false;
|
||||
} else {
|
||||
for (size_t n = 0; n < folders.size(); n++) {
|
||||
const bool isSelected = (currentItemIndex == n);
|
||||
if (ImGui::Selectable(folders.at(n).c_str(), isSelected)) { currentItemIndex = n; }
|
||||
if (ImGui::Selectable(folders.at(n).string().c_str(), isSelected)) { currentItemIndex = n; }
|
||||
if (isSelected) { ImGui::SetItemDefaultFocus(); }
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
@@ -298,11 +351,12 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::IconButton(ICON_VS_NEW_FOLDER, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButton), ImVec2(30, 30))) {
|
||||
fs::openFileBrowser(fs::DialogMode::Folder, {}, [&](const std::fs::path &path) {
|
||||
auto pathStr = path.string();
|
||||
|
||||
if (std::find(folders.begin(), folders.end(), pathStr) == folders.end()) {
|
||||
folders.emplace_back(pathStr);
|
||||
ContentRegistry::Settings::write(dirsSetting, dirsSetting, folders);
|
||||
if (std::find(folders.begin(), folders.end(), path) == folders.end()) {
|
||||
folders.emplace_back(path);
|
||||
|
||||
writeSetting();
|
||||
|
||||
result = true;
|
||||
}
|
||||
});
|
||||
@@ -312,7 +366,9 @@ namespace hex::plugin::builtin {
|
||||
if (ImGui::IconButton(ICON_VS_REMOVE_CLOSE, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButton), ImVec2(30, 30))) {
|
||||
if (!folders.empty()) {
|
||||
folders.erase(std::next(folders.begin(), currentItemIndex));
|
||||
ContentRegistry::Settings::write(dirsSetting, dirsSetting, folders);
|
||||
|
||||
writeSetting();
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
@@ -322,6 +378,112 @@ namespace hex::plugin::builtin {
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/* Proxy */
|
||||
|
||||
static const std::string proxySetting { "hex.builtin.setting.proxy" };
|
||||
|
||||
// init hex::Net proxy url
|
||||
hex::Net::setProxy(ContentRegistry::Settings::read(proxySetting, "hex.builtin.setting.proxy.url", ""));
|
||||
|
||||
ContentRegistry::Settings::addCategoryDescription(proxySetting, "hex.builtin.setting.proxy.description");
|
||||
|
||||
ContentRegistry::Settings::add(
|
||||
proxySetting, "hex.builtin.setting.proxy.url", "", [](auto name, nlohmann::json &setting) {
|
||||
static std::string proxyUrl = static_cast<std::string>(setting);
|
||||
static bool enableProxy = !proxyUrl.empty();
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (ImGui::Checkbox("hex.builtin.setting.proxy.enable"_lang, &enableProxy)) {
|
||||
setting = enableProxy ? proxyUrl : "";
|
||||
hex::Net::setProxy(enableProxy ? proxyUrl : "");
|
||||
result = true;
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(!enableProxy);
|
||||
if (ImGui::InputText("##proxy_url", proxyUrl)) {
|
||||
setting = proxyUrl;
|
||||
hex::Net::setProxy(proxyUrl);
|
||||
result = true;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::InfoTooltip("hex.builtin.setting.proxy.url.tooltip"_lang);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::TextFormatted("{}", name);
|
||||
return result;
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
|
||||
static void loadInterfaceScalingSetting() {
|
||||
float interfaceScaling = 1.0F;
|
||||
switch (ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling", 0)) {
|
||||
default:
|
||||
case 0:
|
||||
// Native scaling
|
||||
break;
|
||||
case 1:
|
||||
interfaceScaling = 0.5F;
|
||||
break;
|
||||
case 2:
|
||||
interfaceScaling = 1.0F;
|
||||
break;
|
||||
case 3:
|
||||
interfaceScaling = 1.5F;
|
||||
break;
|
||||
case 4:
|
||||
interfaceScaling = 2.0F;
|
||||
break;
|
||||
}
|
||||
|
||||
ImHexApi::System::impl::setGlobalScale(interfaceScaling);
|
||||
}
|
||||
|
||||
static void loadFontSettings() {
|
||||
std::fs::path fontFile = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "");
|
||||
if (!fs::exists(fontFile))
|
||||
fontFile.clear();
|
||||
|
||||
// If no custom font has been specified, search for a file called "font.ttf" in one of the resource folders
|
||||
if (fontFile.empty()) {
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Resources)) {
|
||||
auto path = dir / "font.ttf";
|
||||
if (fs::exists(path)) {
|
||||
log::info("Loading custom front from {}", path.string());
|
||||
|
||||
fontFile = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a custom font has been loaded now, also load the font size
|
||||
float fontSize = 13.0F * ImHexApi::System::getGlobalScale();
|
||||
if (!fontFile.empty()) {
|
||||
ImHexApi::System::impl::setCustomFontPath(fontFile);
|
||||
|
||||
fontSize = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13) * ImHexApi::System::getGlobalScale();
|
||||
}
|
||||
|
||||
ImHexApi::System::impl::setFontSize(fontSize);
|
||||
}
|
||||
|
||||
static void loadThemeSettings() {
|
||||
auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", static_cast<i64>(ImHexApi::System::Theme::Dark));
|
||||
|
||||
ImHexApi::System::enableSystemThemeDetection(theme == 0);
|
||||
ImHexApi::System::setTheme(static_cast<ImHexApi::System::Theme>(theme));
|
||||
}
|
||||
|
||||
void loadSettings() {
|
||||
loadInterfaceScalingSetting();
|
||||
loadFontSettings();
|
||||
loadThemeSettings();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,178 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <codicons_font.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
static std::string s_popupMessage;
|
||||
static std::function<void()> s_yesCallback, s_noCallback;
|
||||
static u32 s_selectableFileIndex;
|
||||
static std::vector<std::fs::path> s_selectableFiles;
|
||||
static std::function<void(std::fs::path)> s_selectableFileOpenCallback;
|
||||
static std::vector<nfdfilteritem_t> s_selectableFilesValidExtensions;
|
||||
|
||||
static void drawGlobalPopups() {
|
||||
|
||||
// "Are you sure you want to exit?" Popup
|
||||
if (ImGui::BeginPopupModal("hex.builtin.popup.exit_application.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("hex.builtin.popup.exit_application.desc"_lang);
|
||||
ImGui::NewLine();
|
||||
|
||||
View::confirmButtons(
|
||||
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { ImHexApi::Common::closeImHex(true); }, [] { ImGui::CloseCurrentPopup(); });
|
||||
|
||||
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
auto windowSize = ImHexApi::System::getMainWindowSize();
|
||||
|
||||
// Info popup
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.info"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Error popup
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.error"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Fatal error popup
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.fatal"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("hex.builtin.common.okay"_lang) || ImGui::IsKeyDown(ImGuiKey_Escape)) {
|
||||
ImHexApi::Common::closeImHex();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Yes/No question popup
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.question"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
View::confirmButtons(
|
||||
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] {
|
||||
s_yesCallback();
|
||||
ImGui::CloseCurrentPopup(); }, [] {
|
||||
s_noCallback();
|
||||
ImGui::CloseCurrentPopup(); });
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// File chooser popup
|
||||
bool opened = true;
|
||||
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.choose_file"_lang, &opened, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
|
||||
if (ImGui::BeginListBox("##files", ImVec2(300_scaled, 0))) {
|
||||
|
||||
u32 index = 0;
|
||||
for (auto &path : s_selectableFiles) {
|
||||
if (ImGui::Selectable(path.filename().string().c_str(), index == s_selectableFileIndex))
|
||||
s_selectableFileIndex = index;
|
||||
index++;
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
if (ImGui::Button("hex.builtin.common.open"_lang)) {
|
||||
s_selectableFileOpenCallback(s_selectableFiles[s_selectableFileIndex]);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("hex.builtin.common.browse"_lang)) {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, s_selectableFilesValidExtensions, [](const auto &path) {
|
||||
s_selectableFileOpenCallback(path);
|
||||
ImGui::CloseCurrentPopup();
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void addGlobalUIItems() {
|
||||
EventManager::subscribe<EventFrameEnd>(drawGlobalPopups);
|
||||
|
||||
EventManager::subscribe<RequestShowInfoPopup>([](const std::string &message) {
|
||||
s_popupMessage = message;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.info"_lang); });
|
||||
});
|
||||
|
||||
EventManager::subscribe<RequestShowErrorPopup>([](const std::string &message) {
|
||||
s_popupMessage = message;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.error"_lang); });
|
||||
});
|
||||
|
||||
EventManager::subscribe<RequestShowFatalErrorPopup>([](const std::string &message) {
|
||||
s_popupMessage = message;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.fatal"_lang); });
|
||||
});
|
||||
|
||||
EventManager::subscribe<RequestShowYesNoQuestionPopup>([](const std::string &message, const std::function<void()> &yesCallback, const std::function<void()> &noCallback) {
|
||||
s_popupMessage = message;
|
||||
|
||||
s_yesCallback = yesCallback;
|
||||
s_noCallback = noCallback;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.question"_lang); });
|
||||
});
|
||||
|
||||
EventManager::subscribe<RequestShowFileChooserPopup>([](const std::vector<std::fs::path> &paths, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback) {
|
||||
s_selectableFileIndex = 0;
|
||||
s_selectableFiles = paths;
|
||||
s_selectableFilesValidExtensions = validExtensions;
|
||||
s_selectableFileOpenCallback = callback;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.choose_file"_lang); });
|
||||
});
|
||||
}
|
||||
|
||||
void addFooterItems() {
|
||||
|
||||
if (hex::isProcessElevated()) {
|
||||
@@ -68,19 +226,20 @@ namespace hex::plugin::builtin {
|
||||
bool providerValid = provider != nullptr;
|
||||
|
||||
// Undo
|
||||
ImGui::Disabled([&provider] {
|
||||
ImGui::BeginDisabled(!providerValid || !provider->canUndo());
|
||||
{
|
||||
if (ImGui::ToolBarButton(ICON_VS_DISCARD, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
|
||||
provider->undo();
|
||||
},
|
||||
!providerValid || !provider->canUndo());
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// Redo
|
||||
ImGui::Disabled([&provider] {
|
||||
ImGui::BeginDisabled(!providerValid || !provider->canRedo());
|
||||
{
|
||||
if (ImGui::ToolBarButton(ICON_VS_REDO, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
|
||||
provider->redo();
|
||||
},
|
||||
!providerValid || !provider->canRedo());
|
||||
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
|
||||
@@ -96,42 +255,45 @@ namespace hex::plugin::builtin {
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
|
||||
// Save file
|
||||
ImGui::Disabled([&provider] {
|
||||
ImGui::BeginDisabled(!providerValid || !provider->isWritable() || !provider->isSavable());
|
||||
{
|
||||
if (ImGui::ToolBarButton(ICON_VS_SAVE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
|
||||
provider->save();
|
||||
},
|
||||
!providerValid || !provider->isWritable() || !provider->isSavable());
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// Save file as
|
||||
ImGui::Disabled([&provider] {
|
||||
ImGui::BeginDisabled(!providerValid || !provider->isSavable());
|
||||
{
|
||||
if (ImGui::ToolBarButton(ICON_VS_SAVE_AS, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
|
||||
fs::openFileBrowser(fs::DialogMode::Save, {}, [&provider](auto path) {
|
||||
provider->saveAs(path);
|
||||
});
|
||||
},
|
||||
!providerValid || !provider->isSavable());
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
|
||||
|
||||
// Create bookmark
|
||||
ImGui::Disabled([] {
|
||||
ImGui::BeginDisabled(!providerValid || !provider->isReadable() || !ImHexApi::HexEditor::isSelectionValid());
|
||||
{
|
||||
if (ImGui::ToolBarButton(ICON_VS_BOOKMARK, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) {
|
||||
Region region = { };
|
||||
EventManager::post<QuerySelection>(region);
|
||||
auto region = ImHexApi::HexEditor::getSelection();
|
||||
|
||||
ImHexApi::Bookmarks::add(region.address, region.size, {}, {});
|
||||
if (region.has_value())
|
||||
ImHexApi::Bookmarks::add(region->address, region->size, {}, {});
|
||||
}
|
||||
},
|
||||
!providerValid || !provider->isReadable() || ImHexApi::HexEditor::getSelection().size == 0);
|
||||
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::Spacing();
|
||||
|
||||
// Provider switcher
|
||||
ImGui::Disabled([] {
|
||||
ImGui::BeginDisabled(!providerValid);
|
||||
{
|
||||
auto &providers = ImHexApi::Provider::getProviders();
|
||||
|
||||
std::string preview;
|
||||
@@ -149,9 +311,23 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
},
|
||||
!providerValid);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void handleBorderlessWindowMode() {
|
||||
// Intel's OpenGL driver has weird bugs that cause the drawn window to be offset to the bottom right.
|
||||
// This can be fixed by either using Mesa3D's OpenGL Software renderer or by simply disabling it.
|
||||
// If you want to try if it works anyways on your GPU, set the hex.builtin.setting.interface.force_borderless_window_mode setting to 1
|
||||
if (ImHexApi::System::isBorderlessWindowModeEnabled()) {
|
||||
bool isIntelGPU = hex::containsIgnoreCase(ImHexApi::System::getGPUVendor(), "Intel");
|
||||
ImHexApi::System::impl::setBorderlessWindowMode(!isIntelGPU);
|
||||
|
||||
if (isIntelGPU)
|
||||
log::warn("Intel GPU detected! Intel's OpenGL driver has bugs that can cause issues when using ImHex. If you experience any rendering bugs, please try the Mesa3D Software Renderer");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace hex::plugin::builtin {
|
||||
this->m_logoTexture = ImGui::LoadImageFromMemory(reinterpret_cast<const ImU8 *>(logo.data()), logo.size());
|
||||
}
|
||||
|
||||
ImGui::Image(this->m_logoTexture.textureId, scaled(this->m_logoTexture.size()));
|
||||
ImGui::Image(this->m_logoTexture.textureId, scaled({ 64, 64 }));
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TextFormatted("ImHex Hex Editor v{} by WerWolv - " ICON_FA_CODE_BRANCH, IMHEX_VERSION);
|
||||
|
||||
@@ -23,9 +23,8 @@ namespace hex::plugin::builtin {
|
||||
name,
|
||||
std::move(comment),
|
||||
color,
|
||||
false,
|
||||
|
||||
ImHexApi::HexEditor::addHighlight(region, color, name) });
|
||||
false
|
||||
});
|
||||
|
||||
ProjectFile::markDirty();
|
||||
});
|
||||
@@ -41,6 +40,75 @@ namespace hex::plugin::builtin {
|
||||
EventManager::subscribe<EventFileUnloaded>(this, [this] {
|
||||
this->m_bookmarks.clear();
|
||||
});
|
||||
|
||||
|
||||
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size) -> std::optional<color_t> {
|
||||
hex::unused(data);
|
||||
|
||||
for (const auto &bookmark : this->m_bookmarks) {
|
||||
if (Region { address, size }.isWithin(bookmark.region))
|
||||
return bookmark.color;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
|
||||
hex::unused(data);
|
||||
for (const auto &bookmark : this->m_bookmarks) {
|
||||
if (!Region { address, size }.isWithin(bookmark.region))
|
||||
continue;
|
||||
|
||||
ImGui::BeginTooltip();
|
||||
|
||||
if (ImGui::BeginTable("##tooltips", 1, ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushID(&bookmark);
|
||||
{
|
||||
ImGui::ColorButton("##color", ImColor(bookmark.color));
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::TextUnformatted(bookmark.name.c_str());
|
||||
|
||||
if (ImGui::GetIO().KeyShift) {
|
||||
ImGui::Indent();
|
||||
if (ImGui::BeginTable("##extra_info", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoClip)) {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("Region: ");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("[ 0x{:08X} - 0x{:08X} ]", bookmark.region.getStartAddress(), bookmark.region.getEndAddress());
|
||||
|
||||
if (!bookmark.comment.empty() && bookmark.comment[0] != '\x00') {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("Comment: ");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedWrapped("\"{}\"", bookmark.comment);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::Unindent();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBg, bookmark.color);
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, bookmark.color);
|
||||
ImGui::EndTable();
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ViewBookmarks::~ViewBookmarks() {
|
||||
@@ -63,7 +131,7 @@ namespace hex::plugin::builtin {
|
||||
u32 id = 1;
|
||||
auto bookmarkToRemove = this->m_bookmarks.end();
|
||||
for (auto iter = this->m_bookmarks.begin(); iter != this->m_bookmarks.end(); iter++) {
|
||||
auto &[region, name, comment, color, locked, highlight] = *iter;
|
||||
auto &[region, name, comment, color, locked] = *iter;
|
||||
|
||||
auto headerColor = ImColor(color);
|
||||
auto hoverColor = ImColor(color);
|
||||
@@ -75,7 +143,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, u32(hoverColor));
|
||||
|
||||
bool open = true;
|
||||
if (ImGui::CollapsingHeader(name.c_str(), &open)) {
|
||||
if (ImGui::CollapsingHeader(hex::format("{}###bookmark", name).c_str(), &open)) {
|
||||
ImGui::TextUnformatted("hex.builtin.view.bookmarks.title.info"_lang);
|
||||
ImGui::Separator();
|
||||
ImGui::TextFormatted("hex.builtin.view.bookmarks.address"_lang, region.address, region.address + region.size - 1, region.size);
|
||||
@@ -169,7 +237,6 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
if (bookmarkToRemove != this->m_bookmarks.end()) {
|
||||
ImHexApi::HexEditor::removeHighlight(bookmarkToRemove->highlightId);
|
||||
this->m_bookmarks.erase(bookmarkToRemove);
|
||||
ProjectFile::markDirty();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewCommandPalette::ViewCommandPalette() : View("hex.builtin.view.command_palette.name") {
|
||||
this->m_commandBuffer.resize(1024, 0x00);
|
||||
this->m_commandBuffer = std::vector<char>(1024, 0x00);
|
||||
|
||||
ShortcutManager::addGlobalShortcut(CTRL + SHIFT + Keys::P, [this] {
|
||||
EventManager::post<RequestOpenPopup>("hex.builtin.view.command_palette.name"_lang);
|
||||
@@ -28,15 +28,24 @@ namespace hex::plugin::builtin {
|
||||
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
if (ImGui::InputText(
|
||||
"##command_input", this->m_commandBuffer.data(), this->m_commandBuffer.size(), ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_EnterReturnsTrue, [](ImGuiInputTextCallbackData *callbackData) -> int {
|
||||
auto _this = static_cast<ViewCommandPalette *>(callbackData->UserData);
|
||||
_this->m_lastResults = _this->getCommandResults(callbackData->Buf);
|
||||
if (this->m_focusInputTextBox) {
|
||||
auto textState = ImGui::GetInputTextState(ImGui::GetID("##command_input"));
|
||||
if (textState != nullptr) {
|
||||
textState->Stb.cursor = strlen(this->m_commandBuffer.data());
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
this)) {
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
this->m_focusInputTextBox = false;
|
||||
}
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
if (ImGui::InputText("##command_input", this->m_commandBuffer.data(), this->m_commandBuffer.size(), ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_EnterReturnsTrue,
|
||||
[](ImGuiInputTextCallbackData *callbackData) -> int {
|
||||
auto _this = static_cast<ViewCommandPalette *>(callbackData->UserData);
|
||||
_this->m_lastResults = _this->getCommandResults(callbackData->Buf);
|
||||
|
||||
return 0;
|
||||
}, this)) {
|
||||
if (!this->m_lastResults.empty()) {
|
||||
auto &[displayResult, matchedCommand, callback] = this->m_lastResults.front();
|
||||
callback(matchedCommand);
|
||||
@@ -52,16 +61,6 @@ namespace hex::plugin::builtin {
|
||||
this->m_justOpened = false;
|
||||
}
|
||||
|
||||
if (this->m_focusInputTextBox) {
|
||||
auto textState = ImGui::GetInputTextState(ImGui::GetID("##command_input"));
|
||||
if (textState != nullptr) {
|
||||
textState->Stb.cursor = strlen(this->m_commandBuffer.data());
|
||||
}
|
||||
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
this->m_focusInputTextBox = false;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
for (const auto &[displayResult, matchedCommand, callback] : this->m_lastResults) {
|
||||
|
||||
@@ -42,6 +42,11 @@ namespace hex::plugin::builtin {
|
||||
std::vector<u8> buffer(entry.requiredSize);
|
||||
provider->read(this->m_startAddress, buffer.data(), buffer.size());
|
||||
|
||||
if (this->m_invert) {
|
||||
for (auto &byte : buffer)
|
||||
byte ^= 0xFF;
|
||||
}
|
||||
|
||||
this->m_cachedData.push_back({ entry.unlocalizedName, entry.generatorFunction(buffer, this->m_endian, this->m_numberDisplayStyle), entry.editingFunction, false });
|
||||
}
|
||||
}
|
||||
@@ -154,6 +159,17 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int selection = this->m_invert ? 1 : 0;
|
||||
std::array options = { "hex.builtin.common.no"_lang, "hex.builtin.common.yes"_lang };
|
||||
|
||||
if (ImGui::SliderInt("hex.builtin.view.data_inspector.invert"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
this->m_invert = selection == 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::string text = "hex.builtin.view.data_inspector.no_data"_lang;
|
||||
auto textSize = ImGui::CalcTextSize(text.c_str());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user