mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
Compare commits
133 Commits
releases/v
...
python_scr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16b91eb715 | ||
|
|
8b9d09aa97 | ||
|
|
ccb78246ab | ||
|
|
3144d79605 | ||
|
|
e984fde966 | ||
|
|
5d0b474a7e | ||
|
|
d09f2c4f26 | ||
|
|
dd20a16d3a | ||
|
|
7d22b71e86 | ||
|
|
567ccbfc3a | ||
|
|
ca40775678 | ||
|
|
4916e5542a | ||
|
|
ac8ec2b622 | ||
|
|
9b9f7e2a1d | ||
|
|
28ea91e6c3 | ||
|
|
0d58307e82 | ||
|
|
c8ca84ede9 | ||
|
|
ed2939c39e | ||
|
|
d36bd253e8 | ||
|
|
4615dce0a9 | ||
|
|
7ce8aa3638 | ||
|
|
9236b92dc1 | ||
|
|
05ffcab911 | ||
|
|
61b9c0970b | ||
|
|
3b3701135f | ||
|
|
f5987fde5a | ||
|
|
e56b34f174 | ||
|
|
0fb43ccc2b | ||
|
|
48db4df028 | ||
|
|
86a0693081 | ||
|
|
6295c1d0c3 | ||
|
|
35d29c8e30 | ||
|
|
ca78c4c2fc | ||
|
|
f276409cde | ||
|
|
682f7bee72 | ||
|
|
43bec6a636 | ||
|
|
6eb9c750a7 | ||
|
|
31c93c8c5c | ||
|
|
5aa1046541 | ||
|
|
0f4504476a | ||
|
|
3897245a7e | ||
|
|
373db3de95 | ||
|
|
a1437658af | ||
|
|
f4ec69021d | ||
|
|
6012f20fb3 | ||
|
|
95da957f73 | ||
|
|
642722bdb1 | ||
|
|
cbc31f3c18 | ||
|
|
f2309ba079 | ||
|
|
246ed15d6d | ||
|
|
88756c83c7 | ||
|
|
cf320266df | ||
|
|
47e7e80afe | ||
|
|
0d880babfb | ||
|
|
28ba34f1bf | ||
|
|
e786cb8180 | ||
|
|
458584d778 | ||
|
|
2c711ea206 | ||
|
|
7b25be51a5 | ||
|
|
45b05a4846 | ||
|
|
6972736abf | ||
|
|
3798654f92 | ||
|
|
fdf01dfb50 | ||
|
|
876f091244 | ||
|
|
2988561f01 | ||
|
|
fbfc319ac1 | ||
|
|
9b1417f32d | ||
|
|
c727762940 | ||
|
|
e3565d5bcb | ||
|
|
a3f550c585 | ||
|
|
c610d804b1 | ||
|
|
3d592dbc79 | ||
|
|
0186f2f456 | ||
|
|
d817a813b0 | ||
|
|
1d219ba511 | ||
|
|
285afb6d4b | ||
|
|
ca3708df71 | ||
|
|
c2aafb14c2 | ||
|
|
d4d1acb555 | ||
|
|
d1a59f8c1b | ||
|
|
2fd17f97b6 | ||
|
|
45a3bdffe0 | ||
|
|
f050c69ccd | ||
|
|
c31a2551f1 | ||
|
|
90e93492a7 | ||
|
|
54266bf63b | ||
|
|
ba12f7aec9 | ||
|
|
deafb6fe08 | ||
|
|
091be1440a | ||
|
|
030aee17f5 | ||
|
|
bbbf836374 | ||
|
|
f1b91ef360 | ||
|
|
f6c59b456f | ||
|
|
8f3f941600 | ||
|
|
e561f49e80 | ||
|
|
2ff884fd11 | ||
|
|
296af748ee | ||
|
|
0cb10fcc34 | ||
|
|
4a67ea0b29 | ||
|
|
8e94acc98f | ||
|
|
39cda3764b | ||
|
|
97f5175c84 | ||
|
|
78f8e5055e | ||
|
|
735d896260 | ||
|
|
cb7a6596ba | ||
|
|
fdaa56fd86 | ||
|
|
667b940feb | ||
|
|
bb3de7d510 | ||
|
|
7bdde15796 | ||
|
|
c412ba66d8 | ||
|
|
dd62bee264 | ||
|
|
f886eac7b5 | ||
|
|
623079ca40 | ||
|
|
ce9bd796d6 | ||
|
|
d5f323a2cd | ||
|
|
40592a93ac | ||
|
|
dc1a5a860c | ||
|
|
f7b431902d | ||
|
|
686d47a59e | ||
|
|
eaa4688182 | ||
|
|
72645aa800 | ||
|
|
7044fc8004 | ||
|
|
9e8c780d66 | ||
|
|
e1795d687f | ||
|
|
607f7cba8d | ||
|
|
2572e23928 | ||
|
|
60921031bd | ||
|
|
77550d902c | ||
|
|
41935781fb | ||
|
|
47362559ef | ||
|
|
032ef0722d | ||
|
|
6e32f03a6b | ||
|
|
5731dcf135 |
48
.github/workflows/build.yml
vendored
48
.github/workflows/build.yml
vendored
@@ -2,7 +2,9 @@ name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -211,21 +213,45 @@ jobs:
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_INSTALL_PREFIX="./install" \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
|
||||
-DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64" \
|
||||
..
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: cd build && ninja package
|
||||
run: cd build && ninja install
|
||||
|
||||
- name: ✒️ Fix Signature
|
||||
run: |
|
||||
set -x
|
||||
cd build/install
|
||||
mv imhex.app ImHex.app
|
||||
codesign --remove-signature ImHex.app
|
||||
codesign --force --deep --sign - ImHex.app
|
||||
|
||||
- name: 📁 Fix permissions
|
||||
run: |
|
||||
set -x
|
||||
cd build/install
|
||||
chmod -R 755 ImHex.app/
|
||||
|
||||
- name: 📦 Create DMG
|
||||
run: |
|
||||
set -x
|
||||
mkdir bundle
|
||||
mv build/install/ImHex.app bundle
|
||||
cd bundle
|
||||
ln -s /Applications Applications
|
||||
cd ..
|
||||
hdiutil create -volname "ImHex" -srcfolder bundle -ov -format UDZO imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64.dmg
|
||||
|
||||
- name: ⬆️ Upload DMG
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: macOS DMG${{matrix.suffix}} x86_64
|
||||
path: build/*.dmg
|
||||
path: ./*.dmg
|
||||
|
||||
macos-arm64-build:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -285,15 +311,15 @@ jobs:
|
||||
path: out
|
||||
|
||||
- name: 🗑️ Delete artifact
|
||||
uses: geekyeggo/delete-artifact@v4
|
||||
uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: macos_arm64_intermediate
|
||||
|
||||
- name: ✒️ Fix Signature
|
||||
run: |
|
||||
set -x
|
||||
cd out
|
||||
mv imhex.app ImHex.app
|
||||
codesign --remove-signature ImHex.app
|
||||
codesign --force --deep --sign - ImHex.app
|
||||
|
||||
@@ -625,11 +651,11 @@ jobs:
|
||||
- name: ✒️ Modify spec file
|
||||
run: |
|
||||
sed -i \
|
||||
-e 's/Version: [0-9]*\.[0-9]*\.[0-9]*$/Version: ${{env.IMHEX_VERSION}}/g' \
|
||||
-e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \
|
||||
-e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \
|
||||
-e '/BuildRequires: cmake/a BuildRequires: git-core' \
|
||||
-e '/%files/a %{_datadir}/%{name}/' \
|
||||
-e 's/Version: VERSION$/Version: ${{env.IMHEX_VERSION}}/g' \
|
||||
-e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \
|
||||
-e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \
|
||||
-e '/BuildRequires: cmake/a BuildRequires: git-core' \
|
||||
-e '/%files/a %{_datadir}/%{name}/' \
|
||||
$GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec
|
||||
|
||||
- name: 📜 Fix ccache on EL9
|
||||
|
||||
4
.github/workflows/build_web.yml
vendored
4
.github/workflows/build_web.yml
vendored
@@ -2,7 +2,9 @@ name: Build for the web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -90,7 +90,7 @@ jobs:
|
||||
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
|
||||
|
||||
- name: ⬇️ Download artifacts from latest workflow
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
workflow: build.yml
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
|
||||
|
||||
- name: ⬆️ Upload everything to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
|
||||
with:
|
||||
files: '*'
|
||||
|
||||
|
||||
9
.github/workflows/tests.yml
vendored
9
.github/workflows/tests.yml
vendored
@@ -2,9 +2,13 @@ name: "Unit Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -49,6 +53,7 @@ jobs:
|
||||
cd build
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DIMHEX_ENABLE_UNIT_TESTS=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \
|
||||
|
||||
@@ -19,6 +19,8 @@ option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build
|
||||
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
|
||||
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
|
||||
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
|
||||
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" OFF)
|
||||
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
|
||||
|
||||
# Basic compiler and cmake configurations
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
@@ -38,6 +40,9 @@ project(imhex
|
||||
HOMEPAGE_URL "https://imhex.werwolv.net"
|
||||
)
|
||||
|
||||
# Add ImHex sources
|
||||
add_custom_target(imhex_all ALL)
|
||||
|
||||
# Make sure project is configured correctly
|
||||
setDefaultBuiltTypeIfUnset()
|
||||
detectBadClone()
|
||||
@@ -54,17 +59,19 @@ configurePackingResources()
|
||||
setUninstallTarget()
|
||||
addBundledLibraries()
|
||||
|
||||
# Add ImHex sources
|
||||
add_custom_target(imhex_all ALL)
|
||||
|
||||
add_subdirectory(lib/libimhex)
|
||||
add_subdirectory(main)
|
||||
addPluginDirectories()
|
||||
|
||||
# Add unit tests
|
||||
enable_testing()
|
||||
add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
if (IMHEX_ENABLE_UNIT_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
endif ()
|
||||
|
||||
# Configure more resources that will be added to the install package
|
||||
createPackage()
|
||||
generatePDBs()
|
||||
generateSDKDirectory()
|
||||
generateSDKDirectory()
|
||||
|
||||
# Handle package generation
|
||||
createPackage()
|
||||
|
||||
@@ -46,6 +46,9 @@ function(addDefineToSource SOURCE DEFINE)
|
||||
APPEND
|
||||
PROPERTY COMPILE_DEFINITIONS "${DEFINE}"
|
||||
)
|
||||
|
||||
# Disable precompiled headers for this file
|
||||
set_source_files_properties(${SOURCE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
endfunction()
|
||||
|
||||
# Detect current OS / System
|
||||
@@ -67,6 +70,9 @@ macro(detectOS)
|
||||
add_compile_definitions(OS_WEB)
|
||||
elseif (UNIX AND NOT APPLE)
|
||||
add_compile_definitions(OS_LINUX)
|
||||
if (BSD AND BSD STREQUAL "FreeBSD")
|
||||
add_compile_definitions(OS_FREEBSD)
|
||||
endif()
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(IMHEX_PLUGINS_IN_SHARE)
|
||||
@@ -86,6 +92,8 @@ macro(detectOS)
|
||||
endmacro()
|
||||
|
||||
macro(configurePackingResources)
|
||||
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
if (WIN32)
|
||||
if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
|
||||
set(APPLICATION_TYPE WIN32)
|
||||
@@ -139,9 +147,7 @@ macro(configurePackingResources)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(createPackage)
|
||||
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
macro(addPluginDirectories)
|
||||
file(MAKE_DIRECTORY "plugins")
|
||||
foreach (plugin IN LISTS PLUGINS)
|
||||
add_subdirectory("plugins/${plugin}")
|
||||
@@ -172,9 +178,9 @@ macro(createPackage)
|
||||
add_dependencies(imhex_all ${plugin})
|
||||
endif ()
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
macro(createPackage)
|
||||
if (WIN32)
|
||||
# Install binaries directly in the prefix, usually C:\Program Files\ImHex.
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
@@ -211,7 +217,6 @@ macro(createPackage)
|
||||
endforeach()
|
||||
]])
|
||||
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
|
||||
downloadImHexPatternsFiles("./")
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
|
||||
@@ -222,7 +227,6 @@ macro(createPackage)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
|
||||
downloadImHexPatternsFiles("./share/imhex")
|
||||
|
||||
# install AppStream file
|
||||
@@ -243,17 +247,15 @@ macro(createPackage)
|
||||
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
|
||||
|
||||
# Fix rpath
|
||||
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main> || true)
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:main>)")
|
||||
|
||||
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
|
||||
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
|
||||
|
||||
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS")
|
||||
downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
|
||||
|
||||
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources")
|
||||
install(FILES ${IMHEX_ICON} DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources")
|
||||
install(TARGETS main BUNDLE DESTINATION ".")
|
||||
install(FILES $<TARGET_FILE:main> DESTINATION "${IMHEX_BUNDLE_PATH}")
|
||||
install(FILES $<TARGET_FILE:updater> DESTINATION "${IMHEX_BUNDLE_PATH}")
|
||||
|
||||
# Update library references to make the bundle portable
|
||||
postprocess_bundle(imhex_all main)
|
||||
@@ -262,15 +264,17 @@ macro(createPackage)
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
|
||||
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns")
|
||||
set(CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}/Contents/Info.plist")
|
||||
set(CPACK_BUNDLE_PLIST "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Info.plist")
|
||||
|
||||
if (IMHEX_RESIGN_BUNDLE)
|
||||
message(STATUS "Resigning bundle...")
|
||||
find_program(CODESIGN_PATH codesign)
|
||||
if (CODESIGN_PATH)
|
||||
add_custom_command(TARGET imhex_all POST_BUILD COMMAND "codesign" ARGS "--force" "--deep" "--sign" "-" "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}")
|
||||
install(CODE "message(STATUS \"Signing bundle '${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}'...\")")
|
||||
install(CODE "execute_process(COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME} COMMAND_ERROR_IS_FATAL ANY)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]])
|
||||
endif()
|
||||
else()
|
||||
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@@ -503,7 +507,7 @@ macro(setupCompilerFlags target)
|
||||
set(IMHEX_CXX_FLAGS "-fexceptions -frtti")
|
||||
|
||||
# Disable some warnings
|
||||
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations")
|
||||
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
@@ -611,6 +615,7 @@ macro(addBundledLibraries)
|
||||
endif()
|
||||
|
||||
set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF CACHE BOOL "" FORCE)
|
||||
set(LIBPL_ENABLE_PRECOMPILED_HEADERS ${IMHEX_ENABLE_PRECOMPILED_HEADERS} CACHE BOOL "" FORCE)
|
||||
|
||||
if (WIN32)
|
||||
set(LIBPL_SHARED_LIBRARY ON CACHE BOOL "" FORCE)
|
||||
@@ -653,20 +658,20 @@ macro(addBundledLibraries)
|
||||
if (${Backtrace_FOUND})
|
||||
message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}")
|
||||
|
||||
if (Backtrace_HEADER STREQUAL "execinfo.h")
|
||||
if (Backtrace_HEADER STREQUAL "backtrace.h")
|
||||
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
|
||||
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
|
||||
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
|
||||
add_compile_definitions(HEX_HAS_EXECINFO)
|
||||
elseif (Backtrace_HEADER STREQUAL "backtrace.h")
|
||||
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
|
||||
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
|
||||
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
|
||||
add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
|
||||
add_compile_definitions(HEX_HAS_BACKTRACE)
|
||||
endif ()
|
||||
elseif (Backtrace_HEADER STREQUAL "execinfo.h")
|
||||
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
|
||||
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
|
||||
add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
|
||||
add_compile_definitions(HEX_HAS_EXECINFO)
|
||||
endif()
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
function(enableUnityBuild TARGET)
|
||||
@@ -732,7 +737,7 @@ function(generateSDKDirectory)
|
||||
if (WIN32)
|
||||
set(SDK_PATH "./sdk")
|
||||
elseif (APPLE)
|
||||
set(SDK_PATH "${BUNDLE_NAME}/Contents/Resources/sdk")
|
||||
set(SDK_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk")
|
||||
else()
|
||||
set(SDK_PATH "share/imhex/sdk")
|
||||
endif()
|
||||
@@ -758,4 +763,19 @@ endfunction()
|
||||
function(addIncludesFromLibrary target library)
|
||||
get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(${target} PRIVATE ${library_include_dirs})
|
||||
endfunction()
|
||||
|
||||
function(precompileHeaders target includeFolder)
|
||||
if (NOT IMHEX_ENABLE_PRECOMPILED_HEADERS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE TARGET_INCLUDES "${includeFolder}/**/*.hpp")
|
||||
set(SYSTEM_INCLUDES "<algorithm>;<array>;<atomic>;<chrono>;<cmath>;<cstddef>;<cstdint>;<cstdio>;<cstdlib>;<cstring>;<exception>;<filesystem>;<functional>;<iterator>;<limits>;<list>;<map>;<memory>;<optional>;<ranges>;<set>;<stdexcept>;<string>;<string_view>;<thread>;<tuple>;<type_traits>;<unordered_map>;<unordered_set>;<utility>;<variant>;<vector>")
|
||||
set(INCLUDES "${SYSTEM_INCLUDES};${TARGET_INCLUDES}")
|
||||
string(REPLACE ">" "$<ANGLE-R>" INCLUDES "${INCLUDES}")
|
||||
target_precompile_headers(${target}
|
||||
PUBLIC
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${INCLUDES}>"
|
||||
)
|
||||
endfunction()
|
||||
@@ -1,4 +1,4 @@
|
||||
find_file(libyara.a YARA_LIBRARIES)
|
||||
find_library(YARA_LIBRARIES NAMES yara)
|
||||
find_file(yara.h YARA_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(YARA_LIBRARIES YARA_INCLUDE_DIRS)
|
||||
@@ -36,8 +36,12 @@ macro(add_imhex_plugin)
|
||||
|
||||
# Add include directories and link libraries
|
||||
target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES})
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${IMHEX_PLUGIN_LIBRARIES} ${FMT_LIBRARIES} imgui_all_includes libwolv)
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_LIBRARIES})
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} imgui_all_includes libwolv)
|
||||
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl)
|
||||
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl-gen)
|
||||
|
||||
precompileHeaders(${IMHEX_PLUGIN_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
# Add IMHEX_PROJECT_NAME and IMHEX_VERSION define
|
||||
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}")
|
||||
@@ -88,6 +92,12 @@ macro(add_imhex_plugin)
|
||||
elseif (UNIX)
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH_USE_ORIGIN ON INSTALL_RPATH "$ORIGIN/")
|
||||
endif()
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt AND IMHEX_ENABLE_UNIT_TESTS)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_NAME}_tests)
|
||||
target_compile_definitions(${IMHEX_PLUGIN_NAME}_tests PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}-tests")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(add_romfs_resource input output)
|
||||
|
||||
@@ -14,12 +14,11 @@ if(CMAKE_GENERATOR)
|
||||
# Being called as include(PostprocessBundle), so define a helper function.
|
||||
set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}")
|
||||
function(postprocess_bundle out_target in_target)
|
||||
add_custom_command(TARGET ${out_target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -DBUNDLE_PATH="$<TARGET_FILE_DIR:${in_target}>/../.."
|
||||
-DCODE_SIGN_CERTIFICATE_ID="${CODE_SIGN_CERTIFICATE_ID}"
|
||||
-DEXTRA_BUNDLE_LIBRARY_PATHS="${EXTRA_BUNDLE_LIBRARY_PATHS}"
|
||||
-P "${_POSTPROCESS_BUNDLE_MODULE_LOCATION}"
|
||||
)
|
||||
|
||||
install(CODE "set(BUNDLE_PATH ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME})")
|
||||
install(CODE "set(CODE_SIGN_CERTIFICATE_ID ${CODE_SIGN_CERTIFICATE_ID})")
|
||||
install(CODE "set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS})")
|
||||
install(SCRIPT ${_POSTPROCESS_BUNDLE_MODULE_LOCATION})
|
||||
endfunction()
|
||||
return()
|
||||
endif()
|
||||
@@ -42,7 +41,7 @@ file(GLOB_RECURSE plugins "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexplug")
|
||||
# makes it sometimes break on libraries that do weird things with @rpath. Specify
|
||||
# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625
|
||||
# is fixed and in our minimum CMake version.
|
||||
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins")
|
||||
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins" "${BUNDLE_PATH}/Contents/Frameworks")
|
||||
message(STATUS "Fixing up application bundle: ${extra_dirs}")
|
||||
|
||||
# BundleUtilities is overly verbose, so disable most of its messages
|
||||
|
||||
13
dist/compiling/docker.md
vendored
13
dist/compiling/docker.md
vendored
@@ -18,11 +18,11 @@ docker buildx build . -f <DOCKERFILE_PATH> --progress plain --build-arg 'JOBS=4'
|
||||
|
||||
where `<DOCKERFILE_PATH>` should be replaced by the wanted Dockerfile base d on the build you want to do:
|
||||
|
||||
| Wanted build | Dockerfile path |
|
||||
|--------------|-----------------------------|
|
||||
| MacOS M1 | dist/macOS/arm64.Dockerfile |
|
||||
| AppImage | dist/appimage/Dockerfile |
|
||||
| Web version | dist/web/Dockerfile |
|
||||
| Wanted build | Dockerfile path | Target |
|
||||
|--------------|-----------------------------|--------|
|
||||
| MacOS M1 | dist/macOS/arm64.Dockerfile | - |
|
||||
| AppImage | dist/appimage/Dockerfile | - |
|
||||
| Web version | dist/web/Dockerfile | raw |
|
||||
|
||||
We'll explain this command in the next section
|
||||
|
||||
@@ -43,6 +43,7 @@ In the command saw earlier:
|
||||
- `.` is the base folder that the Dockerfile will be allowed to see
|
||||
- `-f <path>` is to specify the Dockerfile path
|
||||
- `--progress plain` is to allow you to see the output of instructions
|
||||
- `--build-arg <key>=<value>` is to allow to to specify arguments to the build (like -DKEY=VALUE in CMake)
|
||||
- `--build-arg <key>=<value>` is to allow to specify arguments to the build (like -DKEY=VALUE in CMake)
|
||||
- `--build-context key=<folder>` is to specify folders other than the base folder that the Dockerfile is allowed to see
|
||||
- `--output <path>` is the path to write the output package to. If not specified, Docker will create an image as the output (probably not what you want)
|
||||
- `--target <target>` specifies which docker target to build
|
||||
|
||||
8
dist/compiling/linux.md
vendored
8
dist/compiling/linux.md
vendored
@@ -13,14 +13,8 @@ CC=gcc-12 CXX=g++-12 \
|
||||
cmake -G "Ninja" \
|
||||
-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 \
|
||||
..
|
||||
make -j 4 install
|
||||
ninja install
|
||||
```
|
||||
|
||||
All paths follow the XDG Base Directories standard, and can thus be modified
|
||||
|
||||
2
dist/compiling/macos.md
vendored
2
dist/compiling/macos.md
vendored
@@ -15,7 +15,7 @@ OBJC=$(brew --prefix llvm)/bin/clang \
|
||||
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=./install \
|
||||
-DCMAKE_INSTALL_PREFIX="./install" \
|
||||
-DIMHEX_GENERATE_PACKAGE=ON \
|
||||
..
|
||||
ninja install
|
||||
|
||||
2
dist/compiling/windows.md
vendored
2
dist/compiling/windows.md
vendored
@@ -14,7 +14,7 @@ mkdir build
|
||||
cd build
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DCMAKE_INSTALL_PREFIX="./install" \
|
||||
-DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \
|
||||
..
|
||||
ninja install
|
||||
|
||||
2
dist/macOS/arm64.Dockerfile
vendored
2
dist/macOS/arm64.Dockerfile
vendored
@@ -170,4 +170,4 @@ EOF
|
||||
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /mnt/ImHex/build/install/imhex.app ImHex.app
|
||||
COPY --from=build /mnt/ImHex/build/install/imhex.app imhex.app
|
||||
|
||||
6
dist/rpm/imhex.spec
vendored
6
dist/rpm/imhex.spec
vendored
@@ -1,5 +1,5 @@
|
||||
Name: imhex
|
||||
Version: 1.26.2
|
||||
Version: VERSION
|
||||
Release: 0%{?dist}
|
||||
Summary: A hex editor for reverse engineers and programmers
|
||||
|
||||
@@ -95,10 +95,6 @@ CXXFLAGS+=" -std=gnu++2b"
|
||||
%set_build_flags
|
||||
CXXFLAGS+=" -std=gnu++2b"
|
||||
%endif
|
||||
# build binaries required for tests
|
||||
%cmake_build --target unit_tests
|
||||
%ctest --exclude-regex '(Helpers/StoreAPI|Helpers/TipsAPI|Helpers/ContentAPI)'
|
||||
# Helpers/*API exclude tests that require network access
|
||||
|
||||
|
||||
%install
|
||||
|
||||
2
lib/external/libromfs
vendored
2
lib/external/libromfs
vendored
Submodule lib/external/libromfs updated: 61f7e412dd...03adcfdde0
2
lib/external/libwolv
vendored
2
lib/external/libwolv
vendored
Submodule lib/external/libwolv updated: 2ddf596306...6b4a9c7ddd
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
Submodule lib/external/pattern_language updated: 2dae41613b...7aa467c8be
@@ -38,6 +38,8 @@ set(LIBIMHEX_SOURCES
|
||||
source/helpers/tar.cpp
|
||||
source/helpers/debugging.cpp
|
||||
|
||||
source/test/tests.cpp
|
||||
|
||||
source/providers/provider.cpp
|
||||
source/providers/memory_provider.cpp
|
||||
source/providers/undo/stack.cpp
|
||||
@@ -77,37 +79,6 @@ else()
|
||||
target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
|
||||
endif()
|
||||
|
||||
enableUnityBuild(libimhex)
|
||||
setupCompilerFlags(libimhex)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(libimhex)
|
||||
|
||||
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
|
||||
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
# curl is only used in non-emscripten builds
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
if (WIN32)
|
||||
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
|
||||
elseif (APPLE)
|
||||
find_library(FOUNDATION NAMES Foundation)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${JTHREAD_LIBRARIES})
|
||||
target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES})
|
||||
|
||||
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
|
||||
if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_BRANCH)
|
||||
set(GIT_COMMIT_HASH_LONG "${IMHEX_COMMIT_HASH_LONG}")
|
||||
@@ -142,4 +113,41 @@ endif ()
|
||||
|
||||
addDefineToSource(source/api/imhex_api.cpp "IMHEX_VERSION=\"${IMHEX_VERSION_STRING}\"")
|
||||
|
||||
add_dependencies(imhex_all libimhex)
|
||||
enableUnityBuild(libimhex)
|
||||
setupCompilerFlags(libimhex)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(libimhex)
|
||||
|
||||
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
|
||||
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
# curl is only used in non-emscripten builds
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
if (WIN32)
|
||||
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
|
||||
elseif (APPLE)
|
||||
find_library(FOUNDATION NAMES Foundation)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${JTHREAD_LIBRARIES})
|
||||
target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
|
||||
|
||||
precompileHeaders(libimhex "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
endif()
|
||||
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES})
|
||||
|
||||
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
|
||||
add_dependencies(imhex_all libimhex)
|
||||
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
|
||||
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
@@ -931,7 +931,7 @@ namespace hex {
|
||||
|
||||
void addProviderName(const UnlocalizedString &unlocalizedName);
|
||||
|
||||
using ProviderCreationFunction = std::unique_ptr<prv::Provider>(*)();
|
||||
using ProviderCreationFunction = std::function<std::unique_ptr<prv::Provider>()>;
|
||||
void add(const std::string &typeName, ProviderCreationFunction creationFunction);
|
||||
|
||||
const std::vector<std::string>& getEntries();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
@@ -22,7 +23,7 @@
|
||||
static void subscribe(void *token, Event::Callback function) { EventManager::subscribe<event_name>(token, function); } \
|
||||
static void unsubscribe(const EventManager::EventList::iterator &token) noexcept { EventManager::unsubscribe(token); } \
|
||||
static void unsubscribe(void *token) noexcept { EventManager::unsubscribe<event_name>(token); } \
|
||||
static void post(auto &&...args) noexcept { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
|
||||
static void post(auto &&...args) { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
|
||||
};
|
||||
|
||||
#define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__)
|
||||
@@ -71,11 +72,12 @@ namespace hex {
|
||||
|
||||
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
|
||||
|
||||
void operator()(Params... params) const noexcept {
|
||||
void operator()(std::string_view eventName, Params... params) const {
|
||||
try {
|
||||
m_func(params...);
|
||||
} catch (const std::exception &e) {
|
||||
log::error("An exception occurred while handling event: {}", e.what());
|
||||
log::error("An exception occurred while handling event {}: {}", eventName, e.what());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,12 +174,12 @@ namespace hex {
|
||||
* @param args Arguments to pass to the event
|
||||
*/
|
||||
template<impl::EventType E>
|
||||
static void post(auto &&...args) noexcept {
|
||||
static void post(auto && ...args) {
|
||||
std::scoped_lock lock(getEventMutex());
|
||||
|
||||
for (const auto &[id, event] : getEvents()) {
|
||||
if (id == E::Id) {
|
||||
(*static_cast<E *const>(event.get()))(std::forward<decltype(args)>(args)...);
|
||||
(*static_cast<E *const>(event.get()))(wolv::type::getTypeName<E>(), std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,4 +310,9 @@ namespace hex {
|
||||
* The 'from' provider should not have any per provider data after this, and should be immediately deleted
|
||||
*/
|
||||
EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *);
|
||||
|
||||
/**
|
||||
* Called when ImHex managed to catch an error in a general try/catch to prevent/recover from a crash
|
||||
*/
|
||||
EVENT_DEF(EventCrashRecovered, const std::exception &);
|
||||
}
|
||||
@@ -664,6 +664,12 @@ namespace hex {
|
||||
*/
|
||||
std::optional<InitialWindowProperties> getInitialWindowProperties();
|
||||
|
||||
/**
|
||||
* @brief Gets the module handle of libimhex
|
||||
* @return Module handle
|
||||
*/
|
||||
void* getLibImHexModuleHandle();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -698,6 +704,7 @@ namespace hex {
|
||||
std::vector<GlyphRange> glyphRanges;
|
||||
Offset offset;
|
||||
u32 flags;
|
||||
std::optional<u32> defaultSize;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
@@ -716,8 +723,8 @@ namespace hex {
|
||||
GlyphRange range(const char *glyphBegin, const char *glyphEnd);
|
||||
GlyphRange range(u32 codepointBegin, u32 codepointEnd);
|
||||
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
|
||||
|
||||
constexpr static float DefaultFontSize = 13.0;
|
||||
|
||||
|
||||
@@ -46,7 +46,13 @@ namespace hex {
|
||||
* @brief Get a list of all layouts
|
||||
* @return List of all added layouts
|
||||
*/
|
||||
static std::vector<Layout> getLayouts();
|
||||
static const std::vector<Layout> &getLayouts();
|
||||
|
||||
/**
|
||||
* @brief Removes the layout with the given name
|
||||
* @param name Name of the layout
|
||||
*/
|
||||
static void removeLayout(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Handles loading of layouts if needed
|
||||
|
||||
@@ -15,8 +15,10 @@ struct ImGuiContext;
|
||||
namespace hex {
|
||||
|
||||
struct SubCommand {
|
||||
std::string commandKey;
|
||||
std::string commandDesc;
|
||||
std::string commandLong;
|
||||
std::string commandShort;
|
||||
|
||||
std::string commandDescription;
|
||||
std::function<void(const std::vector<std::string>&)> callback;
|
||||
};
|
||||
|
||||
@@ -104,6 +106,10 @@ namespace hex {
|
||||
|
||||
static bool load();
|
||||
static bool load(const std::fs::path &pluginFolder);
|
||||
|
||||
static bool loadLibraries();
|
||||
static bool loadLibraries(const std::fs::path &libraryFolder);
|
||||
|
||||
static void unload();
|
||||
static void reload();
|
||||
static void initializeNewPlugins();
|
||||
@@ -111,6 +117,7 @@ namespace hex {
|
||||
|
||||
static void addPlugin(const std::string &name, PluginFunctions functions);
|
||||
|
||||
static Plugin* getPlugin(const std::string &name);
|
||||
static const std::list<Plugin>& getPlugins();
|
||||
static const std::vector<std::fs::path>& getPluginPaths();
|
||||
static const std::vector<std::fs::path>& getPluginLoadPaths();
|
||||
@@ -121,6 +128,7 @@ namespace hex {
|
||||
static std::list<Plugin>& getPluginsMutable();
|
||||
|
||||
static AutoReset<std::vector<std::fs::path>> s_pluginPaths, s_pluginLoadPaths;
|
||||
static AutoReset<std::vector<uintptr_t>> s_loadedLibraries;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -13,18 +13,22 @@ namespace hex {
|
||||
struct Workspace {
|
||||
std::string layout;
|
||||
std::fs::path path;
|
||||
bool builtin;
|
||||
};
|
||||
|
||||
static void createWorkspace(const std::string &name, const std::string &layout = "");
|
||||
static void switchWorkspace(const std::string &name);
|
||||
|
||||
static void importFromFile(const std::fs::path &path);
|
||||
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {});
|
||||
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}, bool builtin = false);
|
||||
|
||||
static void removeWorkspace(const std::string &name);
|
||||
|
||||
static const auto& getWorkspaces() { return *s_workspaces; }
|
||||
static const auto& getCurrentWorkspace() { return s_currentWorkspace; }
|
||||
|
||||
static void reset();
|
||||
static void reload();
|
||||
|
||||
static void process();
|
||||
|
||||
@@ -32,7 +36,7 @@ namespace hex {
|
||||
WorkspaceManager() = default;
|
||||
|
||||
static AutoReset<std::map<std::string, Workspace>> s_workspaces;
|
||||
static decltype(s_workspaces)::Type::iterator s_currentWorkspace, s_previousWorkspace;
|
||||
static decltype(s_workspaces)::Type::iterator s_currentWorkspace, s_previousWorkspace, s_workspaceToRemove;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,72 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#if defined(OS_WEB)
|
||||
|
||||
#include <emscripten/fetch.h>
|
||||
#include <future>
|
||||
|
||||
namespace hex {
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
#include <emscripten/fetch.h>
|
||||
|
||||
// Execute the request
|
||||
auto result = this->executeImpl<T>(response);
|
||||
namespace hex {
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
|
||||
// Write the result to the file
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
|
||||
// Execute the request
|
||||
auto result = this->executeImpl<T>(response);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
// Write the result to the file
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
hex::unused(path, mimeName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
hex::unused(data, mimeName, fileName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
std::vector<u8> responseData;
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
strcpy(m_attr.requestMethod, m_method.c_str());
|
||||
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
||||
|
||||
if (!m_body.empty()) {
|
||||
m_attr.requestData = m_body.c_str();
|
||||
m_attr.requestDataSize = m_body.size();
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<const char*> headers;
|
||||
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
|
||||
headers.push_back(it->first.c_str());
|
||||
headers.push_back(it->second.c_str());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
hex::unused(path, mimeName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
headers.push_back(nullptr);
|
||||
m_attr.requestHeaders = headers.data();
|
||||
|
||||
// Send request
|
||||
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
hex::unused(data, mimeName, fileName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
data.resize(fetch->numBytes);
|
||||
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
std::vector<u8> responseData;
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
strcpy(m_attr.requestMethod, m_method.c_str());
|
||||
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
||||
|
||||
if (!m_body.empty()) {
|
||||
m_attr.requestData = m_body.c_str();
|
||||
m_attr.requestDataSize = m_body.size();
|
||||
}
|
||||
|
||||
std::vector<const char*> headers;
|
||||
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
|
||||
headers.push_back(it->first.c_str());
|
||||
headers.push_back(it->second.c_str());
|
||||
}
|
||||
headers.push_back(nullptr);
|
||||
m_attr.requestHeaders = headers.data();
|
||||
|
||||
// Send request
|
||||
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
|
||||
|
||||
data.resize(fetch->numBytes);
|
||||
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
|
||||
|
||||
return Result<T>(fetch->status, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
return Result<T>(fetch->status, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,145 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <future>
|
||||
#if !defined(OS_WEB)
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
#include <future>
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
namespace hex {
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
namespace hex {
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
|
||||
return this->executeImpl<T>(response);
|
||||
});
|
||||
}
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
return std::async(std::launch::async, [this, path, mimeName]{
|
||||
auto fileName = wolv::util::toUTF8String(path.filename());
|
||||
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
curl_mime_data_cb(part, file.getSize(),
|
||||
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
return fread(buffer, size, nitems, handle);
|
||||
},
|
||||
[](void *arg, curl_off_t offset, int origin) -> int {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
if (fseek(handle, offset, origin) != 0)
|
||||
return CURL_SEEKFUNC_CANTSEEK;
|
||||
else
|
||||
return CURL_SEEKFUNC_OK;
|
||||
},
|
||||
[](void *arg) {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
fclose(handle);
|
||||
},
|
||||
file.getHandle());
|
||||
curl_mime_filename(part, fileName.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
|
||||
curl_mime_filename(part, fileNameStr.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
|
||||
|
||||
setDefaultConfig();
|
||||
|
||||
if (!m_body.empty()) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
|
||||
return this->executeImpl<T>(response);
|
||||
});
|
||||
}
|
||||
|
||||
curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
return std::async(std::launch::async, [this, path, mimeName]{
|
||||
auto fileName = wolv::util::toUTF8String(path.filename());
|
||||
|
||||
for (auto &[key, value] : m_headers) {
|
||||
std::string header = hex::format("{}: {}", key, value);
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
curl_mime_data_cb(part, file.getSize(),
|
||||
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
return fread(buffer, size, nitems, handle);
|
||||
},
|
||||
[](void *arg, curl_off_t offset, int origin) -> int {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
if (fseek(handle, offset, origin) != 0)
|
||||
return CURL_SEEKFUNC_CANTSEEK;
|
||||
else
|
||||
return CURL_SEEKFUNC_OK;
|
||||
},
|
||||
[](void *arg) {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
fclose(handle);
|
||||
},
|
||||
file.getHandle());
|
||||
curl_mime_filename(part, fileName.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_transmissionMutex);
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
auto result = curl_easy_perform(m_curl);
|
||||
if (result != CURLE_OK){
|
||||
char *url = nullptr;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
|
||||
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
|
||||
checkProxyErrors();
|
||||
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
|
||||
curl_mime_filename(part, fileNameStr.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
return { };
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
|
||||
|
||||
setDefaultConfig();
|
||||
|
||||
if (!m_body.empty()) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
|
||||
}
|
||||
|
||||
curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
|
||||
|
||||
for (auto &[key, value] : m_headers) {
|
||||
std::string header = hex::format("{}: {}", key, value);
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
}
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_transmissionMutex);
|
||||
|
||||
auto result = curl_easy_perform(m_curl);
|
||||
if (result != CURLE_OK){
|
||||
char *url = nullptr;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
|
||||
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
|
||||
checkProxyErrors();
|
||||
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
long statusCode = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
|
||||
|
||||
return Result<T>(statusCode, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
long statusCode = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
|
||||
|
||||
return Result<T>(statusCode, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <fmt/color.h>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
namespace hex::log {
|
||||
|
||||
@@ -21,6 +22,7 @@ namespace hex::log {
|
||||
|
||||
[[nodiscard]] std::recursive_mutex& getLoggerMutex();
|
||||
[[nodiscard]] bool isLoggingSuspended();
|
||||
[[nodiscard]] bool isDebugLoggingEnabled();
|
||||
|
||||
struct LogEntry {
|
||||
std::string project;
|
||||
@@ -40,13 +42,15 @@ namespace hex::log {
|
||||
std::scoped_lock lock(getLoggerMutex());
|
||||
|
||||
auto dest = getDestination();
|
||||
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
|
||||
try {
|
||||
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
|
||||
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
|
||||
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
|
||||
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
namespace color {
|
||||
@@ -63,13 +67,14 @@ namespace hex::log {
|
||||
|
||||
void suspendLogging();
|
||||
void resumeLogging();
|
||||
void enableDebugLogging();
|
||||
|
||||
[[maybe_unused]] void debug(const std::string &fmt, auto && ... args) {
|
||||
#if defined(DEBUG)
|
||||
if (impl::isDebugLoggingEnabled()) [[unlikely]] {
|
||||
hex::log::impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
|
||||
#else
|
||||
} else {
|
||||
impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] void info(const std::string &fmt, auto && ... args) {
|
||||
@@ -88,23 +93,26 @@ namespace hex::log {
|
||||
hex::log::impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
|
||||
}
|
||||
|
||||
|
||||
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::getLoggerMutex());
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}", message);
|
||||
fflush(dest);
|
||||
try {
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}", message);
|
||||
fflush(dest);
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
[[maybe_unused]] void println(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::getLoggerMutex());
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
try {
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -331,4 +331,14 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] std::string formatSystemError(i32 error);
|
||||
|
||||
/**
|
||||
* Gets the shared library handle for a given pointer
|
||||
* @param symbol Pointer to any function or variable in the shared library
|
||||
* @return The module handle
|
||||
* @warning Important! Calling this function on functions defined in other modules will return the handle of the current module!
|
||||
* This is because you're not actually passing a pointer to the function in the other module but rather a pointer to a thunk
|
||||
* that is defined in the current module.
|
||||
*/
|
||||
[[nodiscard]] void* getContainingModule(void* symbol);
|
||||
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ namespace hex::prv {
|
||||
[[nodiscard]] u64 getActualSize() const override { return m_data.size(); }
|
||||
|
||||
void resizeRaw(u64 newSize) override;
|
||||
void insertRaw(u64 offset, u64 size) override;
|
||||
void removeRaw(u64 offset, u64 size) override;
|
||||
|
||||
[[nodiscard]] std::string getName() const override { return m_name; }
|
||||
|
||||
|
||||
@@ -161,13 +161,13 @@ namespace hex::prv {
|
||||
*/
|
||||
[[nodiscard]] virtual std::string getName() const = 0;
|
||||
|
||||
void resize(u64 newSize);
|
||||
bool resize(u64 newSize);
|
||||
void insert(u64 offset, u64 size);
|
||||
void remove(u64 offset, u64 size);
|
||||
|
||||
virtual void resizeRaw(u64 newSize) { hex::unused(newSize); }
|
||||
virtual void insertRaw(u64 offset, u64 size) { hex::unused(offset, size); }
|
||||
virtual void removeRaw(u64 offset, u64 size) { hex::unused(offset, size); }
|
||||
virtual void insertRaw(u64 offset, u64 size);
|
||||
virtual void removeRaw(u64 offset, u64 size);
|
||||
|
||||
virtual void save();
|
||||
virtual void saveAs(const std::fs::path &path);
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace hex {
|
||||
return m_data | std::views::values;
|
||||
}
|
||||
|
||||
void setOnCreateCallback(std::function<void(const prv::Provider *, T&)> callback) {
|
||||
void setOnCreateCallback(std::function<void(prv::Provider *, T&)> callback) {
|
||||
m_onCreateCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace hex {
|
||||
auto [it, inserted] = m_data.emplace(provider, T());
|
||||
auto &[key, value] = *it;
|
||||
if (m_onCreateCallback)
|
||||
m_onCreateCallback(key, value);
|
||||
m_onCreateCallback(provider, value);
|
||||
});
|
||||
|
||||
EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){
|
||||
@@ -121,7 +121,7 @@ namespace hex {
|
||||
|
||||
private:
|
||||
std::map<const prv::Provider *, T> m_data;
|
||||
std::function<void(const prv::Provider *, T&)> m_onCreateCallback;
|
||||
std::function<void(prv::Provider *, T&)> m_onCreateCallback;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@ namespace hex::prv::undo {
|
||||
|
||||
void groupOperations(u32 count, const UnlocalizedString &unlocalizedName);
|
||||
void apply(const Stack &otherStack);
|
||||
void reapply();
|
||||
|
||||
[[nodiscard]] bool canUndo() const;
|
||||
[[nodiscard]] bool canRedo() const;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/api/plugin_manager.hpp>
|
||||
|
||||
#include <wolv/utils/preproc.hpp>
|
||||
|
||||
@@ -28,16 +29,20 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define INIT_PLUGIN(name) \
|
||||
if (!hex::test::initPluginImpl(name)) TEST_FAIL();
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
using Function = int(*)();
|
||||
struct Test {
|
||||
std::function<int()> function;
|
||||
Function function;
|
||||
bool shouldFail;
|
||||
};
|
||||
|
||||
class Tests {
|
||||
public:
|
||||
static auto addTest(const std::string &name, const std::function<int()> &func, bool shouldFail) noexcept {
|
||||
static auto addTest(const std::string &name, Function func, bool shouldFail) noexcept {
|
||||
s_tests.insert({
|
||||
name, {func, shouldFail}
|
||||
});
|
||||
@@ -50,7 +55,7 @@ namespace hex::test {
|
||||
}
|
||||
|
||||
private:
|
||||
static inline std::map<std::string, Test> s_tests;
|
||||
static std::map<std::string, Test> s_tests;
|
||||
};
|
||||
|
||||
template<class F>
|
||||
@@ -86,4 +91,5 @@ namespace hex::test {
|
||||
return TestSequence<F>(executor.getName(), std::forward<F>(f), executor.shouldFail());
|
||||
}
|
||||
|
||||
bool initPluginImpl(std::string name);
|
||||
}
|
||||
@@ -533,6 +533,11 @@ namespace hex {
|
||||
|
||||
pl::PatternLanguage& getRuntime() {
|
||||
static PerProvider<pl::PatternLanguage> runtime;
|
||||
AT_FIRST_TIME {
|
||||
runtime.setOnCreateCallback([](prv::Provider *provider, pl::PatternLanguage &runtime) {
|
||||
configureRuntime(runtime, provider);
|
||||
});
|
||||
};
|
||||
|
||||
return *runtime;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,11 @@ namespace hex {
|
||||
|
||||
static AutoReset<std::optional<ProviderRegion>> s_currentSelection;
|
||||
void setCurrentSelection(const std::optional<ProviderRegion> ®ion) {
|
||||
*s_currentSelection = region;
|
||||
if (region == Region::Invalid()) {
|
||||
clearSelection();
|
||||
} else {
|
||||
*s_currentSelection = region;
|
||||
}
|
||||
}
|
||||
|
||||
static PerProvider<std::optional<Region>> s_hoveredRegion;
|
||||
@@ -599,6 +603,11 @@ namespace hex {
|
||||
return impl::s_initialWindowProperties;
|
||||
}
|
||||
|
||||
void* getLibImHexModuleHandle() {
|
||||
return hex::getContainingModule((void*)&getLibImHexModuleHandle);
|
||||
}
|
||||
|
||||
|
||||
const std::map<std::string, std::string>& getInitArguments() {
|
||||
return *impl::s_initArguments;
|
||||
}
|
||||
@@ -658,7 +667,11 @@ namespace hex {
|
||||
#if defined(OS_WINDOWS)
|
||||
return "Windows";
|
||||
#elif defined(OS_LINUX)
|
||||
return "Linux";
|
||||
#if defined(OS_FREEBSD)
|
||||
return "FreeBSD";
|
||||
#else
|
||||
return "Linux";
|
||||
#endif
|
||||
#elif defined(OS_MACOS)
|
||||
return "macOS";
|
||||
#elif defined(OS_WEB)
|
||||
@@ -915,7 +928,7 @@ namespace hex {
|
||||
};
|
||||
}
|
||||
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
|
||||
wolv::io::File fontFile(path, wolv::io::File::Mode::Read);
|
||||
if (!fontFile.isValid()) {
|
||||
log::error("Failed to load font from file '{}'", wolv::util::toUTF8String(path));
|
||||
@@ -927,17 +940,19 @@ namespace hex {
|
||||
fontFile.readVector(),
|
||||
glyphRanges,
|
||||
offset,
|
||||
flags
|
||||
flags,
|
||||
defaultSize
|
||||
});
|
||||
}
|
||||
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
|
||||
impl::s_fonts->emplace_back(Font {
|
||||
name,
|
||||
{ data.begin(), data.end() },
|
||||
glyphRanges,
|
||||
offset,
|
||||
flags
|
||||
flags,
|
||||
defaultSize
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -64,10 +64,25 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
std::vector<LayoutManager::Layout> LayoutManager::getLayouts() {
|
||||
const std::vector<LayoutManager::Layout>& LayoutManager::getLayouts() {
|
||||
return s_layouts;
|
||||
}
|
||||
|
||||
void LayoutManager::removeLayout(const std::string& name) {
|
||||
for (const auto &layout : *s_layouts) {
|
||||
if (layout.name == name) {
|
||||
if (wolv::io::File(layout.path, wolv::io::File::Mode::Write).remove()) {
|
||||
log::info("Removed layout '{}'", name);
|
||||
LayoutManager::reload();
|
||||
} else {
|
||||
log::error("Failed to remove layout '{}'", name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LayoutManager::closeAllViews() {
|
||||
for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries())
|
||||
view->getWindowOpenState() = false;
|
||||
|
||||
@@ -18,24 +18,50 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
static uintptr_t loadLibrary(const std::fs::path &path) {
|
||||
#if defined(OS_WINDOWS)
|
||||
auto handle = uintptr_t(LoadLibraryW(path.c_str()));
|
||||
|
||||
if (handle == uintptr_t(INVALID_HANDLE_VALUE) || handle == 0) {
|
||||
log::error("Loading library '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return handle;
|
||||
#else
|
||||
auto handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
|
||||
|
||||
if (handle == 0) {
|
||||
log::error("Loading library '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return handle;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void unloadLibrary(uintptr_t handle, const std::fs::path &path) {
|
||||
#if defined(OS_WINDOWS)
|
||||
if (handle != 0) {
|
||||
if (FreeLibrary(HMODULE(handle)) == FALSE) {
|
||||
log::error("Error when unloading library '{}': {}!", wolv::util::toUTF8String(path.filename()), hex::formatSystemError(::GetLastError()));
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (handle != 0) {
|
||||
if (dlclose(reinterpret_cast<void*>(handle)) != 0) {
|
||||
log::error("Error when unloading library '{}': {}!", path.filename().string(), dlerror());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Plugin::Plugin(const std::fs::path &path) : m_path(path) {
|
||||
log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename()));
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
m_handle = uintptr_t(LoadLibraryW(path.c_str()));
|
||||
|
||||
if (m_handle == uintptr_t(INVALID_HANDLE_VALUE) || m_handle == 0) {
|
||||
log::error("Loading plugin '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError()));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
m_handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
|
||||
|
||||
if (m_handle == 0) {
|
||||
log::error("Loading plugin '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
m_handle = loadLibrary(path);
|
||||
if (m_handle == 0)
|
||||
return;
|
||||
|
||||
const auto fileName = path.stem().string();
|
||||
|
||||
@@ -89,15 +115,7 @@ namespace hex {
|
||||
log::info("Trying to unload plugin '{}'", getPluginName());
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
if (m_handle != 0)
|
||||
if (FreeLibrary(HMODULE(m_handle)) == FALSE) {
|
||||
log::error("Error when unloading plugin '{}': {}!", wolv::util::toUTF8String(m_path.filename()), hex::formatSystemError(::GetLastError()));
|
||||
}
|
||||
#else
|
||||
if (m_handle != 0)
|
||||
dlclose(reinterpret_cast<void*>(m_handle));
|
||||
#endif
|
||||
unloadLibrary(m_handle, m_path);
|
||||
}
|
||||
|
||||
bool Plugin::initializePlugin() const {
|
||||
@@ -284,6 +302,35 @@ namespace hex {
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoReset<std::vector<uintptr_t>> PluginManager::s_loadedLibraries;
|
||||
|
||||
bool PluginManager::loadLibraries() {
|
||||
bool success = true;
|
||||
for (const auto &loadPath : fs::getDefaultPaths(fs::ImHexPath::Libraries))
|
||||
success = PluginManager::loadLibraries(loadPath) && success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool PluginManager::loadLibraries(const std::fs::path& libraryFolder) {
|
||||
bool success = true;
|
||||
for (const auto &entry : std::fs::directory_iterator(libraryFolder)) {
|
||||
if (!(entry.path().extension() == ".dll" || entry.path().extension() == ".so" || entry.path().extension() == ".dylib"))
|
||||
continue;
|
||||
|
||||
auto handle = loadLibrary(entry);
|
||||
if (handle == 0) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
PluginManager::s_loadedLibraries->push_back(handle);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PluginManager::initializeNewPlugins() {
|
||||
for (const auto &plugin : getPlugins()) {
|
||||
if (!plugin.isLoaded())
|
||||
@@ -304,6 +351,11 @@ namespace hex {
|
||||
plugins.pop_back();
|
||||
}
|
||||
|
||||
while (!s_loadedLibraries->empty()) {
|
||||
unloadLibrary(s_loadedLibraries->back(), "");
|
||||
s_loadedLibraries->pop_back();
|
||||
}
|
||||
|
||||
getPluginsMutable() = std::move(savedPlugins);
|
||||
}
|
||||
|
||||
@@ -321,6 +373,15 @@ namespace hex {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
Plugin* PluginManager::getPlugin(const std::string &name) {
|
||||
for (auto &plugin : getPluginsMutable()) {
|
||||
if (plugin.getPluginName() == name)
|
||||
return &plugin;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<std::fs::path>& PluginManager::getPluginPaths() {
|
||||
return s_pluginPaths;
|
||||
}
|
||||
@@ -335,5 +396,4 @@ namespace hex {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -412,13 +412,15 @@ namespace hex {
|
||||
void TaskManager::runDeferredCalls() {
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
|
||||
for (const auto &call : s_deferredCalls)
|
||||
call();
|
||||
for (const auto &[location, call] : s_onceDeferredCalls)
|
||||
call();
|
||||
|
||||
s_deferredCalls.clear();
|
||||
s_onceDeferredCalls.clear();
|
||||
while (!s_deferredCalls.empty()) {
|
||||
auto callback = s_deferredCalls.front();
|
||||
s_deferredCalls.pop_front();
|
||||
callback();
|
||||
}
|
||||
while (!s_onceDeferredCalls.empty()) {
|
||||
auto node = s_onceDeferredCalls.extract(s_onceDeferredCalls.begin());
|
||||
node.mapped()();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::runWhenTasksFinished(const std::function<void()> &function) {
|
||||
|
||||
@@ -9,22 +9,28 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
AutoReset<std::map<std::string, WorkspaceManager::Workspace>> WorkspaceManager::s_workspaces;
|
||||
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_currentWorkspace = s_workspaces->end();
|
||||
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_previousWorkspace = s_workspaces->end();
|
||||
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_workspaceToRemove = s_workspaces->end();
|
||||
|
||||
void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) {
|
||||
s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace {
|
||||
.layout = layout.empty() ? LayoutManager::saveToString() : layout,
|
||||
.path = {}
|
||||
.layout = layout.empty() ? LayoutManager::saveToString() : layout,
|
||||
.path = {},
|
||||
.builtin = false
|
||||
}).first;
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
|
||||
if (exportToFile(path / (name + ".hexws")))
|
||||
for (const auto &workspaceFolder : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
|
||||
const auto workspacePath = workspaceFolder / (name + ".hexws");
|
||||
if (exportToFile(workspacePath)) {
|
||||
s_currentWorkspace->second.path = workspacePath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +43,9 @@ namespace hex {
|
||||
}
|
||||
|
||||
void WorkspaceManager::importFromFile(const std::fs::path& path) {
|
||||
if (std::ranges::any_of(*s_workspaces, [path](const auto &pair) { return pair.second.path == path; }))
|
||||
return;
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
if (!file.isValid()) {
|
||||
log::error("Failed to load workspace from file '{}'", path.string());
|
||||
@@ -50,10 +59,12 @@ namespace hex {
|
||||
|
||||
const std::string name = json["name"];
|
||||
std::string layout = json["layout"];
|
||||
const bool builtin = json.value("builtin", false);
|
||||
|
||||
(*s_workspaces)[name] = Workspace {
|
||||
.layout = std::move(layout),
|
||||
.path = path
|
||||
.path = path,
|
||||
.builtin = builtin
|
||||
};
|
||||
} catch (nlohmann::json::exception &e) {
|
||||
log::error("Failed to load workspace from file '{}': {}", path.string(), e.what());
|
||||
@@ -61,7 +72,7 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName) {
|
||||
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName, bool builtin) {
|
||||
if (path.empty()) {
|
||||
if (s_currentWorkspace == s_workspaces->end())
|
||||
return false;
|
||||
@@ -80,22 +91,46 @@ namespace hex {
|
||||
nlohmann::json json;
|
||||
json["name"] = workspaceName;
|
||||
json["layout"] = LayoutManager::saveToString();
|
||||
json["builtin"] = builtin;
|
||||
|
||||
file.writeString(json.dump(4));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorkspaceManager::removeWorkspace(const std::string& name) {
|
||||
for (const auto &[workspaceName, workspace] : *s_workspaces) {
|
||||
if (workspaceName == name) {
|
||||
log::info("{}", wolv::util::toUTF8String(workspace.path));
|
||||
if (wolv::io::File(workspace.path, wolv::io::File::Mode::Write).remove()) {
|
||||
log::info("Removed workspace '{}'", name);
|
||||
|
||||
switchWorkspace(s_workspaces->begin()->first);
|
||||
s_workspaces->erase(workspaceName);
|
||||
} else {
|
||||
log::error("Failed to remove workspace '{}'", name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WorkspaceManager::process() {
|
||||
if (s_previousWorkspace != s_currentWorkspace) {
|
||||
log::info("Updating workspace");
|
||||
if (s_previousWorkspace != s_workspaces->end())
|
||||
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first);
|
||||
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first, s_previousWorkspace->second.builtin);
|
||||
|
||||
LayoutManager::closeAllViews();
|
||||
ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str());
|
||||
|
||||
s_previousWorkspace = s_currentWorkspace;
|
||||
|
||||
if (s_workspaceToRemove != s_workspaces->end()) {
|
||||
s_workspaces->erase(s_workspaceToRemove);
|
||||
s_workspaceToRemove = s_workspaces->end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +141,24 @@ namespace hex {
|
||||
s_previousWorkspace = s_workspaces->end();
|
||||
}
|
||||
|
||||
void WorkspaceManager::reload() {
|
||||
WorkspaceManager::reset();
|
||||
|
||||
for (const auto &defaultPath : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
|
||||
for (const auto &entry : std::fs::directory_iterator(defaultPath)) {
|
||||
if (!entry.is_regular_file())
|
||||
continue;
|
||||
|
||||
const auto &path = entry.path();
|
||||
if (path.extension() != ".hexws")
|
||||
continue;
|
||||
|
||||
WorkspaceManager::importFromFile(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
#include <shellapi.h>
|
||||
#elif defined(OS_LINUX) || defined(OS_WEB)
|
||||
#include <xdg.hpp>
|
||||
# if defined(OS_FREEBSD)
|
||||
#include <sys/syslimits.h>
|
||||
# else
|
||||
#include <limits.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(OS_WEB)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <hex/api/event_manager.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
@@ -20,6 +22,7 @@ namespace hex::log {
|
||||
bool s_colorOutputEnabled = false;
|
||||
std::recursive_mutex s_loggerMutex;
|
||||
bool s_loggingSuspended = false;
|
||||
bool s_debugLoggingEnabled = false;
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +34,10 @@ namespace hex::log {
|
||||
s_loggingSuspended = false;
|
||||
}
|
||||
|
||||
void enableDebugLogging() {
|
||||
s_debugLoggingEnabled = true;
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
std::recursive_mutex& getLoggerMutex() {
|
||||
@@ -41,6 +48,14 @@ namespace hex::log {
|
||||
return s_loggingSuspended;
|
||||
}
|
||||
|
||||
bool isDebugLoggingEnabled() {
|
||||
#if defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
return s_debugLoggingEnabled;
|
||||
#endif
|
||||
}
|
||||
|
||||
FILE *getDestination() {
|
||||
if (s_loggerFile.isValid())
|
||||
return s_loggerFile.getHandle();
|
||||
@@ -65,7 +80,7 @@ namespace hex::log {
|
||||
s_loggerFile.disableBuffering();
|
||||
|
||||
if (s_loggerFile.isValid()) {
|
||||
s_colorOutputEnabled = true;
|
||||
s_colorOutputEnabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
#include <wolv/utils/guards.hpp>
|
||||
#elif defined(OS_LINUX)
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <hex/helpers/utils_linux.hpp>
|
||||
#elif defined(OS_MACOS)
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <hex/helpers/utils_macos.hpp>
|
||||
#elif defined(OS_WEB)
|
||||
#include "emscripten.h"
|
||||
@@ -784,4 +786,24 @@ namespace hex {
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void* getContainingModule(void* symbol) {
|
||||
#if defined(OS_WINDOWS)
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (VirtualQuery(symbol, &mbi, sizeof(mbi)))
|
||||
return mbi.AllocationBase;
|
||||
|
||||
return nullptr;
|
||||
#elif !defined(OS_WEB)
|
||||
Dl_info info = {};
|
||||
if (dladdr(symbol, &info) == 0)
|
||||
return nullptr;
|
||||
|
||||
return dlopen(info.dli_fname, RTLD_LAZY);
|
||||
#else
|
||||
hex::unused(symbol);
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,41 +31,4 @@ namespace hex::prv {
|
||||
m_data.resize(newSize);
|
||||
}
|
||||
|
||||
void MemoryProvider::insertRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
this->resizeRaw(oldSize + size);
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
||||
|
||||
position -= readSize;
|
||||
|
||||
this->readRaw(position, buffer.data(), readSize);
|
||||
this->writeRaw(position, zeroBuffer.data(), readSize);
|
||||
this->writeRaw(position + size, buffer.data(), readSize);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryProvider::removeRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
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->resizeRaw(oldSize - size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,11 @@ namespace hex::prv {
|
||||
}
|
||||
}
|
||||
|
||||
void Provider::resize(u64 newSize) {
|
||||
bool Provider::resize(u64 newSize) {
|
||||
if (newSize >> 63) {
|
||||
log::error("new provider size is very large ({}). Is it a negative number ?", newSize);
|
||||
return false;
|
||||
}
|
||||
i64 difference = newSize - this->getActualSize();
|
||||
|
||||
if (difference > 0)
|
||||
@@ -83,6 +87,7 @@ namespace hex::prv {
|
||||
EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference);
|
||||
|
||||
this->markDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Provider::insert(u64 offset, u64 size) {
|
||||
@@ -97,6 +102,52 @@ namespace hex::prv {
|
||||
this->markDirty();
|
||||
}
|
||||
|
||||
void Provider::insertRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
this->resizeRaw(oldSize + size);
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
||||
|
||||
position -= readSize;
|
||||
|
||||
this->readRaw(position, buffer.data(), readSize);
|
||||
this->writeRaw(position, zeroBuffer.data(), readSize);
|
||||
this->writeRaw(position + size, buffer.data(), readSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Provider::removeRaw(u64 offset, u64 size) {
|
||||
if (offset > this->getActualSize() || size == 0)
|
||||
return;
|
||||
|
||||
if ((offset + size) > this->getActualSize())
|
||||
size = this->getActualSize() - offset;
|
||||
|
||||
auto oldSize = this->getActualSize();
|
||||
|
||||
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->resizeRaw(newSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Provider::applyOverlays(u64 offset, void *buffer, size_t size) const {
|
||||
for (auto &overlay : m_overlays) {
|
||||
auto overlayOffset = overlay->getAddress();
|
||||
|
||||
@@ -93,6 +93,13 @@ namespace hex::prv::undo {
|
||||
}
|
||||
}
|
||||
|
||||
void Stack::reapply() {
|
||||
for (const auto &operation : m_undoStack) {
|
||||
operation->redo(m_provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Stack::add(std::unique_ptr<Operation> &&operation) {
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace hex::subcommands {
|
||||
std::optional<SubCommand> findSubCommand(const std::string &arg) {
|
||||
for (auto &plugin : PluginManager::getPlugins()) {
|
||||
for (auto &subCommand : plugin.getSubCommands()) {
|
||||
if (hex::format("--{}", subCommand.commandKey) == arg) {
|
||||
if (hex::format("--{}", subCommand.commandLong) == arg || hex::format("-{}", subCommand.commandShort) == arg) {
|
||||
return subCommand;
|
||||
}
|
||||
}
|
||||
@@ -112,8 +112,8 @@ namespace hex::subcommands {
|
||||
|
||||
std::vector<std::string> args;
|
||||
|
||||
for (const auto &arg_view : std::views::split(string, char(0x00))) {
|
||||
std::string arg(arg_view.data(), arg_view.size());
|
||||
for (const auto &argument : std::views::split(string, char(0x00))) {
|
||||
std::string arg(argument.data(), argument.size());
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
|
||||
23
lib/libimhex/source/test/tests.cpp
Normal file
23
lib/libimhex/source/test/tests.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <hex/test/tests.hpp>
|
||||
|
||||
namespace hex::test {
|
||||
std::map<std::string, Test> Tests::s_tests;
|
||||
|
||||
bool initPluginImpl(std::string name) {
|
||||
if (name != "Built-in") {
|
||||
if(!initPluginImpl("Built-in")) return false;
|
||||
}
|
||||
|
||||
hex::Plugin *plugin = hex::PluginManager::getPlugin(name);
|
||||
if (plugin == nullptr) {
|
||||
hex::log::fatal("Plugin '{}' was not found !", name);
|
||||
return false;
|
||||
}else if (!plugin->initializePlugin()) {
|
||||
hex::log::fatal("Failed to initialize plugin '{}' !", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
hex::log::info("Initialized plugin '{}' successfully", name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <imgui_internal.h>
|
||||
#include <implot.h>
|
||||
#include <implot_internal.h>
|
||||
#include <cimgui.h>
|
||||
#include <opengl_support.h>
|
||||
|
||||
#undef IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
3
lib/third_party/imgui/CMakeLists.txt
vendored
3
lib/third_party/imgui/CMakeLists.txt
vendored
@@ -6,9 +6,10 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
add_library(imgui_all_includes INTERFACE)
|
||||
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(cimgui)
|
||||
add_subdirectory(implot)
|
||||
add_subdirectory(imnodes)
|
||||
add_subdirectory(custom)
|
||||
add_subdirectory(ColorTextEditor)
|
||||
|
||||
set(IMGUI_LIBRARIES imgui_imgui imgui_implot imgui_imnodes imgui_custom imgui_color_text_editor PARENT_SCOPE)
|
||||
set(IMGUI_LIBRARIES imgui_imgui imgui_cimgui imgui_implot imgui_imnodes imgui_custom imgui_color_text_editor PARENT_SCOPE)
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
bool mCaseSensitive;
|
||||
|
||||
LanguageDefinition()
|
||||
: mGlobalDocComment("/*!"), mDocComment("/**"), mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
|
||||
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -604,7 +604,7 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
|
||||
return mPalette[(int)PaletteIndex::Comment];
|
||||
if (aGlyph.mMultiLineComment)
|
||||
return mPalette[(int)PaletteIndex::MultiLineComment];
|
||||
if (aGlyph.mDeactivated && !aGlyph.mPreprocessor)
|
||||
if (aGlyph.mDeactivated)
|
||||
return mPalette[(int)PaletteIndex::PreprocessorDeactivated];
|
||||
auto const color = mPalette[(int)aGlyph.mColorIndex];
|
||||
if (aGlyph.mPreprocessor) {
|
||||
@@ -619,10 +619,20 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
|
||||
}
|
||||
|
||||
void TextEditor::HandleKeyboardInputs() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
auto shift = io.KeyShift;
|
||||
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
|
||||
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
auto shift = io.KeyShift;
|
||||
auto left = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow));
|
||||
auto right = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow));
|
||||
auto up = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow));
|
||||
auto down = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow));
|
||||
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
|
||||
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
|
||||
auto home = io.ConfigMacOSXBehaviors ? io.KeySuper && left : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
|
||||
auto end = io.ConfigMacOSXBehaviors ? io.KeySuper && right : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
|
||||
auto top = io.ConfigMacOSXBehaviors ? io.KeySuper && up : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
|
||||
auto bottom = io.ConfigMacOSXBehaviors ? io.KeySuper && down : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
|
||||
auto pageUp = io.ConfigMacOSXBehaviors ? ctrl && up : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp));
|
||||
auto pageDown = io.ConfigMacOSXBehaviors ? ctrl && down : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown));
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
if (ImGui::IsWindowHovered())
|
||||
@@ -638,25 +648,25 @@ void TextEditor::HandleKeyboardInputs() {
|
||||
Undo();
|
||||
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
|
||||
Redo();
|
||||
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
|
||||
else if (!ctrl && !alt && up)
|
||||
MoveUp(1, shift);
|
||||
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
|
||||
else if (!ctrl && !alt && down)
|
||||
MoveDown(1, shift);
|
||||
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
|
||||
else if (!alt && left)
|
||||
MoveLeft(1, shift, ctrl);
|
||||
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
|
||||
else if (!alt && right)
|
||||
MoveRight(1, shift, ctrl);
|
||||
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))
|
||||
else if (!alt && pageUp)
|
||||
MoveUp(GetPageSize() - 4, shift);
|
||||
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))
|
||||
else if (!alt && pageDown)
|
||||
MoveDown(GetPageSize() - 4, shift);
|
||||
else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
|
||||
else if (!alt && top)
|
||||
MoveTop(shift);
|
||||
else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
|
||||
else if (!alt && bottom)
|
||||
MoveBottom(shift);
|
||||
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
|
||||
else if (!ctrl && !alt && home)
|
||||
MoveHome(shift);
|
||||
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
|
||||
else if (!ctrl && !alt && end)
|
||||
MoveEnd(shift);
|
||||
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
|
||||
Delete();
|
||||
@@ -722,7 +732,7 @@ void TextEditor::HandleKeyboardInputs() {
|
||||
void TextEditor::HandleMouseInputs() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
auto shift = io.KeyShift;
|
||||
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
|
||||
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
|
||||
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
|
||||
|
||||
if (ImGui::IsWindowHovered()) {
|
||||
@@ -905,7 +915,10 @@ void TextEditor::Render() {
|
||||
}
|
||||
|
||||
if (mState.mCursorPosition.mLine == lineNo && mShowCursor) {
|
||||
auto focused = ImGui::IsWindowFocused();
|
||||
bool focused = false;
|
||||
ImGuiViewport *viewport = ImGui::GetWindowViewport();
|
||||
if (viewport->PlatformUserData != NULL && ImGui::GetPlatformIO().Platform_GetWindowFocus(viewport))
|
||||
focused = ImGui::IsWindowFocused();
|
||||
|
||||
// Highlight the current line (where the cursor is)
|
||||
if (!HasSelection()) {
|
||||
@@ -2505,6 +2518,7 @@ void TextEditor::ColorizeInternal() {
|
||||
auto firstChar = true; // there is no other non-whitespace characters in the line before
|
||||
auto currentLine = 0;
|
||||
auto currentIndex = 0;
|
||||
auto commentLength = 0;
|
||||
auto &startStr = mLanguageDefinition.mCommentStart;
|
||||
auto &singleStartStr = mLanguageDefinition.mSingleLineComment;
|
||||
auto &docStartStr = mLanguageDefinition.mDocComment;
|
||||
@@ -2516,6 +2530,14 @@ void TextEditor::ColorizeInternal() {
|
||||
while (currentLine < endLine || currentIndex < endIndex) {
|
||||
auto &line = mLines[currentLine];
|
||||
|
||||
auto setGlyphFlags = [&](int index) {
|
||||
line[index].mMultiLineComment = withinComment;
|
||||
line[index].mComment = withinSingleLineComment;
|
||||
line[index].mDocComment = withinDocComment;
|
||||
line[index].mGlobalDocComment = withinGlobalDocComment;
|
||||
line[index].mDeactivated = withinNotDef;
|
||||
};
|
||||
|
||||
if (currentIndex == 0) {
|
||||
withinSingleLineComment = false;
|
||||
withinPreproc = false;
|
||||
@@ -2532,24 +2554,12 @@ void TextEditor::ColorizeInternal() {
|
||||
bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
|
||||
|
||||
if (withinString) {
|
||||
line[currentIndex].mMultiLineComment = withinComment;
|
||||
line[currentIndex].mComment = withinSingleLineComment;
|
||||
line[currentIndex].mDocComment = withinDocComment;
|
||||
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
|
||||
line[currentIndex].mDeactivated = withinNotDef;
|
||||
if (c == '\"') {
|
||||
if (currentIndex > 2 && line[currentIndex - 1].mChar == '\\' && line[currentIndex - 2].mChar != '\\') {
|
||||
currentIndex += 1;
|
||||
if (currentIndex < (int)line.size()) {
|
||||
line[currentIndex].mMultiLineComment = withinComment;
|
||||
line[currentIndex].mComment = withinSingleLineComment;
|
||||
line[currentIndex].mDocComment = withinDocComment;
|
||||
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
|
||||
line[currentIndex].mDeactivated = withinNotDef;
|
||||
}
|
||||
} else
|
||||
withinString = false;
|
||||
}
|
||||
setGlyphFlags(currentIndex);
|
||||
if (c == '\\') {
|
||||
currentIndex++;
|
||||
setGlyphFlags(currentIndex);
|
||||
} else if (c == '\"')
|
||||
withinString = false;
|
||||
} else {
|
||||
if (firstChar && c == mLanguageDefinition.mPreprocChar) {
|
||||
withinPreproc = true;
|
||||
@@ -2594,7 +2604,6 @@ void TextEditor::ColorizeInternal() {
|
||||
}
|
||||
if (!withinNotDef) {
|
||||
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) != mDefines.end();
|
||||
withinNotDef = !isConditionMet;
|
||||
ifDefs.push_back(isConditionMet);
|
||||
} else
|
||||
ifDefs.push_back(false);
|
||||
@@ -2608,7 +2617,6 @@ void TextEditor::ColorizeInternal() {
|
||||
}
|
||||
if (!withinNotDef) {
|
||||
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) == mDefines.end();
|
||||
withinNotDef = !isConditionMet;
|
||||
ifDefs.push_back(isConditionMet);
|
||||
} else
|
||||
ifDefs.push_back(false);
|
||||
@@ -2616,20 +2624,17 @@ void TextEditor::ColorizeInternal() {
|
||||
}
|
||||
} else {
|
||||
if (directive == "endif") {
|
||||
if (ifDefs.size() > 1)
|
||||
if (ifDefs.size() > 1) {
|
||||
ifDefs.pop_back();
|
||||
withinNotDef = !ifDefs.back();
|
||||
withinNotDef = !ifDefs.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\"') {
|
||||
withinString = true;
|
||||
line[currentIndex].mMultiLineComment = withinComment;
|
||||
line[currentIndex].mComment = withinSingleLineComment;
|
||||
line[currentIndex].mDocComment = withinDocComment;
|
||||
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
|
||||
line[currentIndex].mDeactivated = withinNotDef;
|
||||
setGlyphFlags(currentIndex);
|
||||
} else {
|
||||
auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; };
|
||||
|
||||
@@ -2653,29 +2658,30 @@ void TextEditor::ColorizeInternal() {
|
||||
if (isGlobalDocComment || isDocComment || isComment) {
|
||||
commentStartLine = currentLine;
|
||||
commentStartIndex = currentIndex;
|
||||
if (isGlobalDocComment)
|
||||
if (isGlobalDocComment) {
|
||||
withinGlobalDocComment = true;
|
||||
else if (isDocComment)
|
||||
commentLength = 3;
|
||||
} else if (isDocComment) {
|
||||
withinDocComment = true;
|
||||
else
|
||||
commentLength = 3;
|
||||
} else {
|
||||
withinComment = true;
|
||||
commentLength = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
|
||||
}
|
||||
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
|
||||
line[currentIndex].mDocComment = withinDocComment;
|
||||
line[currentIndex].mMultiLineComment = withinComment;
|
||||
line[currentIndex].mComment = withinSingleLineComment;
|
||||
line[currentIndex].mDeactivated = withinNotDef;
|
||||
setGlyphFlags(currentIndex);
|
||||
|
||||
auto &endStr = mLanguageDefinition.mCommentEnd;
|
||||
if (compareBack(endStr, line)) {
|
||||
withinComment = false;
|
||||
withinDocComment = false;
|
||||
withinGlobalDocComment = false;
|
||||
commentStartLine = endLine;
|
||||
commentStartIndex = endIndex;
|
||||
if (compareBack(endStr, line) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) {
|
||||
withinComment = false;
|
||||
withinDocComment = false;
|
||||
withinGlobalDocComment = false;
|
||||
commentStartLine = endLine;
|
||||
commentStartIndex = endIndex;
|
||||
commentLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2684,6 +2690,7 @@ void TextEditor::ColorizeInternal() {
|
||||
|
||||
currentIndex += UTF8CharLength(c);
|
||||
if (currentIndex >= (int)line.size()) {
|
||||
withinNotDef = !ifDefs.back();
|
||||
currentIndex = 0;
|
||||
++currentLine;
|
||||
}
|
||||
|
||||
19
lib/third_party/imgui/cimgui/CMakeLists.txt
vendored
Normal file
19
lib/third_party/imgui/cimgui/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(imgui_cimgui)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
add_library(imgui_cimgui OBJECT
|
||||
source/cimgui.cpp
|
||||
)
|
||||
|
||||
target_include_directories(imgui_cimgui PUBLIC
|
||||
include
|
||||
)
|
||||
|
||||
target_link_libraries(imgui_cimgui PRIVATE imgui_includes)
|
||||
add_dependencies(imhex_all imgui_cimgui)
|
||||
endif()
|
||||
|
||||
4788
lib/third_party/imgui/cimgui/include/cimgui.h
vendored
Normal file
4788
lib/third_party/imgui/cimgui/include/cimgui.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5760
lib/third_party/imgui/cimgui/source/cimgui.cpp
vendored
Normal file
5760
lib/third_party/imgui/cimgui/source/cimgui.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -63,6 +63,8 @@ else ()
|
||||
target_link_libraries(main PRIVATE pthread)
|
||||
endif ()
|
||||
|
||||
precompileHeaders(main ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
if (APPLE)
|
||||
add_compile_definitions(GL_SILENCE_DEPRECATION)
|
||||
endif ()
|
||||
@@ -65,7 +65,7 @@ namespace hex::crash {
|
||||
static void printStackTrace() {
|
||||
for (const auto &stackFrame : stacktrace::getStackTrace()) {
|
||||
if (stackFrame.line == 0)
|
||||
log::fatal(" {}", stackFrame.function);
|
||||
log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function);
|
||||
else
|
||||
log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace hex::init {
|
||||
|
||||
|
||||
// Load all plugins but don't initialize them
|
||||
PluginManager::loadLibraries();
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
|
||||
PluginManager::load(dir);
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ namespace hex::init {
|
||||
|
||||
|
||||
FrameResult WindowSplash::fullFrame() {
|
||||
glfwSetWindowSize(m_window, 640_scaled, 400_scaled);
|
||||
glfwSetWindowSize(m_window, 640, 400);
|
||||
centerWindow(m_window);
|
||||
|
||||
glfwPollEvents();
|
||||
@@ -248,23 +248,21 @@ namespace hex::init {
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
auto scale = ImHexApi::System::getGlobalScale();
|
||||
|
||||
// Draw the splash screen background
|
||||
auto drawList = ImGui::GetBackgroundDrawList();
|
||||
{
|
||||
|
||||
// Draw the splash screen background
|
||||
drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize() * scale);
|
||||
drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize());
|
||||
|
||||
{
|
||||
|
||||
// Function to highlight a given number of bytes at a position in the splash screen
|
||||
const auto highlightBytes = [&](ImVec2 start, size_t count, ImColor color, float opacity) {
|
||||
// Dimensions and number of bytes that are drawn. Taken from the splash screen image
|
||||
const auto hexSize = ImVec2(29, 18) * scale;
|
||||
const auto hexSpacing = ImVec2(17.4, 15) * scale;
|
||||
const auto hexStart = ImVec2(27, 127) * scale;
|
||||
const auto hexSize = ImVec2(29, 18);
|
||||
const auto hexSpacing = ImVec2(17.4, 15);
|
||||
const auto hexStart = ImVec2(27, 127);
|
||||
|
||||
constexpr auto HexCount = ImVec2(13, 7);
|
||||
|
||||
@@ -312,10 +310,10 @@ namespace hex::init {
|
||||
this->progressLerp += (m_progress - this->progressLerp) * 0.1F;
|
||||
|
||||
// Draw the splash screen foreground
|
||||
drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize() * scale);
|
||||
drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize());
|
||||
|
||||
// Draw the "copyright" notice
|
||||
drawList->AddText(ImVec2(35, 85) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str());
|
||||
drawList->AddText(ImVec2(35, 85), ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str());
|
||||
|
||||
// Draw version information
|
||||
// In debug builds, also display the current commit hash and branch
|
||||
@@ -325,18 +323,18 @@ namespace hex::init {
|
||||
const static auto VersionInfo = hex::format("{0}", ImHexApi::System::getImHexVersion());
|
||||
#endif
|
||||
|
||||
drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x * scale - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105 * scale), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str());
|
||||
drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str());
|
||||
}
|
||||
|
||||
// Draw the task progress bar
|
||||
{
|
||||
std::lock_guard guard(m_progressMutex);
|
||||
|
||||
const auto progressBackgroundStart = ImVec2(99, 357) * scale;
|
||||
const auto progressBackgroundSize = ImVec2(442, 30) * scale;
|
||||
const auto progressBackgroundStart = ImVec2(99, 357);
|
||||
const auto progressBackgroundSize = ImVec2(442, 30);
|
||||
|
||||
const auto progressStart = progressBackgroundStart + ImVec2(0, 20) * scale;
|
||||
const auto progressSize = ImVec2(progressBackgroundSize.x * m_progress, 10 * scale);
|
||||
const auto progressStart = progressBackgroundStart + ImVec2(0, 20);
|
||||
const auto progressSize = ImVec2(progressBackgroundSize.x * m_progress, 10);
|
||||
|
||||
// Draw progress bar
|
||||
drawList->AddRectFilled(progressStart, progressStart + progressSize, 0xD0FFFFFF);
|
||||
@@ -344,7 +342,7 @@ namespace hex::init {
|
||||
// Draw task names separated by | characters
|
||||
if (!m_currTaskNames.empty()) {
|
||||
drawList->PushClipRect(progressBackgroundStart, progressBackgroundStart + progressBackgroundSize, true);
|
||||
drawList->AddText(progressStart + ImVec2(5, -20) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(m_currTaskNames, " | ")).c_str());
|
||||
drawList->AddText(progressStart + ImVec2(5, -20), ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(m_currTaskNames, " | ")).c_str());
|
||||
drawList->PopClipRect();
|
||||
}
|
||||
}
|
||||
@@ -492,7 +490,7 @@ namespace hex::init {
|
||||
|
||||
ImFontConfig cfg;
|
||||
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
|
||||
cfg.SizePixels = 13.0_scaled;
|
||||
cfg.SizePixels = ImHexApi::Fonts::DefaultFontSize;
|
||||
io.Fonts->AddFontDefault(&cfg);
|
||||
|
||||
std::uint8_t *px;
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace hex::init {
|
||||
PluginManager::addLoadPath(dir);
|
||||
}
|
||||
|
||||
PluginManager::loadLibraries();
|
||||
PluginManager::load();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -2,12 +2,26 @@
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
namespace {
|
||||
|
||||
[[maybe_unused]] std::string tryDemangle(const std::string &symbolName) {
|
||||
if (auto variant1 = llvm::demangle(symbolName); variant1 != symbolName)
|
||||
return variant1;
|
||||
|
||||
if (auto variant2 = llvm::demangle(std::string("_") + symbolName); variant2 != std::string("_") + symbolName)
|
||||
return variant2;
|
||||
|
||||
return symbolName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
namespace hex::stacktrace {
|
||||
|
||||
@@ -80,14 +94,7 @@
|
||||
fileName = "??";
|
||||
}
|
||||
|
||||
std::string demangledName;
|
||||
if (auto variant1 = llvm::demangle(symbolName); variant1 != symbolName)
|
||||
demangledName = variant1;
|
||||
else if (auto variant2 = llvm::demangle(std::string("_") + symbolName); variant2 != std::string("_") + symbolName)
|
||||
demangledName = variant2;
|
||||
else
|
||||
demangledName = symbolName;
|
||||
|
||||
auto demangledName = tryDemangle(symbolName);
|
||||
stackTrace.push_back(StackFrame { fileName, demangledName, lineNumber });
|
||||
}
|
||||
|
||||
@@ -103,9 +110,8 @@
|
||||
#if __has_include(BACKTRACE_HEADER)
|
||||
|
||||
#include BACKTRACE_HEADER
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace hex::stacktrace {
|
||||
|
||||
@@ -116,17 +122,24 @@
|
||||
std::vector<StackFrame> getStackTrace() {
|
||||
static std::vector<StackFrame> result;
|
||||
|
||||
std::array<void*, 128> addresses;
|
||||
size_t count = backtrace(addresses.data(), addresses.size());
|
||||
auto functions = backtrace_symbols(addresses.data(), count);
|
||||
std::array<void*, 128> addresses = {};
|
||||
const size_t count = backtrace(addresses.data(), addresses.size());
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
result.push_back(StackFrame { "", functions[i], 0 });
|
||||
Dl_info info;
|
||||
for (size_t i = 0; i < count; i += 1) {
|
||||
dladdr(addresses[i], &info);
|
||||
|
||||
auto fileName = info.dli_fname != nullptr ? std::fs::path(info.dli_fname).filename().string() : "??";
|
||||
auto demangledName = info.dli_sname != nullptr ? tryDemangle(info.dli_sname) : "??";
|
||||
|
||||
result.push_back(StackFrame { std::move(fileName), std::move(demangledName), 0 });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(HEX_HAS_BACKTRACE)
|
||||
@@ -134,7 +147,6 @@
|
||||
#if __has_include(BACKTRACE_HEADER)
|
||||
|
||||
#include BACKTRACE_HEADER
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
@@ -161,7 +173,7 @@
|
||||
if (function == nullptr)
|
||||
function = "??";
|
||||
|
||||
result.push_back(StackFrame { std::fs::path(fileName).filename().string(), llvm::demangle(function), u32(lineNumber) });
|
||||
result.push_back(StackFrame { std::fs::path(fileName).filename().string(), tryDemangle(function), u32(lineNumber) });
|
||||
|
||||
return 0;
|
||||
}, nullptr, nullptr);
|
||||
|
||||
@@ -126,6 +126,7 @@ namespace hex {
|
||||
throw;
|
||||
} catch (const std::exception &e) {
|
||||
log::fatal("Unhandled exception: {}", e.what());
|
||||
EventCrashRecovered::post(e);
|
||||
} catch (...) {
|
||||
log::fatal("Unhandled exception: Unknown exception");
|
||||
}
|
||||
@@ -133,26 +134,47 @@ namespace hex {
|
||||
|
||||
void errorRecoverLogCallback(void*, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
std::string message;
|
||||
message.resize(std::vsnprintf(nullptr, 0, fmt, args));
|
||||
std::vsnprintf(message.data(), message.size(), fmt, args);
|
||||
message.resize(message.size() - 1);
|
||||
|
||||
va_start(args, fmt);
|
||||
message.resize(std::vsnprintf(nullptr, 0, fmt, args));
|
||||
va_end(args);
|
||||
|
||||
va_start(args, fmt);
|
||||
std::vsnprintf(message.data(), message.size(), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
message.resize(message.size() - 1);
|
||||
|
||||
log::error("{}", message);
|
||||
}
|
||||
|
||||
void Window::fullFrame() {
|
||||
static u32 crashWatchdog = 0;
|
||||
|
||||
try {
|
||||
this->frameBegin();
|
||||
this->frame();
|
||||
this->frameEnd();
|
||||
|
||||
// Feed the watchdog
|
||||
crashWatchdog = 0;
|
||||
} catch (...) {
|
||||
// If an exception keeps being thrown, abort the application after 10 frames
|
||||
// This is done to avoid the application getting stuck in an infinite loop of exceptions
|
||||
crashWatchdog += 1;
|
||||
if (crashWatchdog > 10) {
|
||||
log::fatal("Crash watchdog triggered, aborting");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
// Try to recover from the exception by bringing ImGui back into a working state
|
||||
ImGui::ErrorCheckEndFrameRecover(errorRecoverLogCallback, nullptr);
|
||||
ImGui::EndFrame();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
|
||||
// Handle the exception
|
||||
handleException();
|
||||
}
|
||||
}
|
||||
@@ -164,13 +186,11 @@ namespace hex {
|
||||
// Determine if the application should be in long sleep mode
|
||||
bool shouldLongSleep = !m_unlockFrameRate;
|
||||
|
||||
// Wait 5 frames before actually enabling the long sleep mode to make animations not stutter
|
||||
constexpr static auto LongSleepTimeout = 5;
|
||||
static i32 lockTimeout = 0;
|
||||
static double lockTimeout = 0;
|
||||
if (!shouldLongSleep) {
|
||||
lockTimeout = LongSleepTimeout;
|
||||
lockTimeout = 0.05;
|
||||
} else if (lockTimeout > 0) {
|
||||
lockTimeout -= 1;
|
||||
lockTimeout -= m_lastFrameTime;
|
||||
}
|
||||
|
||||
if (shouldLongSleep && lockTimeout > 0)
|
||||
@@ -192,6 +212,7 @@ namespace hex {
|
||||
constexpr static auto LongSleepFPS = 5.0;
|
||||
const double timeout = std::max(0.0, (1.0 / LongSleepFPS) - (glfwGetTime() - m_lastStartFrameTime));
|
||||
|
||||
glfwPollEvents();
|
||||
glfwWaitEventsTimeout(timeout);
|
||||
} else {
|
||||
glfwPollEvents();
|
||||
@@ -214,14 +235,28 @@ namespace hex {
|
||||
|
||||
// Limit frame rate
|
||||
// If the target FPS are below 15, use the monitor refresh rate, if it's above 200, don't limit the frame rate
|
||||
const auto targetFPS = ImHexApi::System::getTargetFPS();
|
||||
if (targetFPS < 15) {
|
||||
glfwSwapInterval(1);
|
||||
} else if (targetFPS > 200) {
|
||||
glfwSwapInterval(0);
|
||||
auto targetFPS = ImHexApi::System::getTargetFPS();
|
||||
if (targetFPS >= 200) {
|
||||
// Let it rip
|
||||
} else {
|
||||
// If the target frame rate is below 15, use the current monitor's refresh rate
|
||||
if (targetFPS < 15) {
|
||||
// Fall back to 60 FPS if the monitor refresh rate cannot be determined
|
||||
targetFPS = 60;
|
||||
|
||||
if (auto monitor = glfwGetWindowMonitor(m_window); monitor != nullptr) {
|
||||
if (auto videoMode = glfwGetVideoMode(monitor); videoMode != nullptr) {
|
||||
targetFPS = videoMode->refreshRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep if we're not in long sleep mode
|
||||
if (!shouldLongSleep) {
|
||||
glfwSwapInterval(0);
|
||||
// If anything goes wrong with these checks, make sure that we're sleeping for at least 1ms
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
// Sleep for the remaining time if the frame rate is above the target frame rate
|
||||
const auto frameTime = glfwGetTime() - m_lastStartFrameTime;
|
||||
const auto targetFrameTime = 1.0 / targetFPS;
|
||||
if (frameTime < targetFrameTime) {
|
||||
@@ -567,29 +602,54 @@ namespace hex {
|
||||
// Finalize ImGui frame
|
||||
ImGui::Render();
|
||||
|
||||
// Hash the draw data to determine if anything changed on the screen
|
||||
// Compare the previous frame buffer to the current one to determine if the window content has changed
|
||||
// If not, there's no point in sending the draw data off to the GPU and swapping buffers
|
||||
// NOTE: For anybody looking at this code and thinking "why not just hash the buffer and compare the hashes",
|
||||
// the reason is that hashing the buffer is significantly slower than just comparing the buffers directly.
|
||||
// The buffer might become quite large if there's a lot of vertices on the screen but it's still usually less than
|
||||
// 10MB (out of which only the active portion needs to actually be compared) which is worth the ~60x speedup.
|
||||
bool shouldRender = false;
|
||||
{
|
||||
u32 drawDataHash = 0;
|
||||
static u32 previousDrawDataHash = 0;
|
||||
static std::vector<u8> previousVtxData;
|
||||
static size_t previousVtxDataSize = 0;
|
||||
|
||||
size_t offset = 0;
|
||||
size_t vtxDataSize = 0;
|
||||
|
||||
for (const auto viewPort : ImGui::GetPlatformIO().Viewports) {
|
||||
auto drawData = viewPort->DrawData;
|
||||
for (int n = 0; n < drawData->CmdListsCount; n++) {
|
||||
const ImDrawList *cmdList = drawData->CmdLists[n];
|
||||
drawDataHash = ImHashData(cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert), drawDataHash);
|
||||
vtxDataSize += drawData->CmdLists[n]->VtxBuffer.size() * sizeof(ImDrawVert);
|
||||
}
|
||||
}
|
||||
for (const auto viewPort : ImGui::GetPlatformIO().Viewports) {
|
||||
auto drawData = viewPort->DrawData;
|
||||
for (int n = 0; n < drawData->CmdListsCount; n++) {
|
||||
const ImDrawList *cmdList = drawData->CmdLists[n];
|
||||
drawDataHash = ImHashData(cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx), drawDataHash);
|
||||
|
||||
if (vtxDataSize == previousVtxDataSize) {
|
||||
shouldRender = shouldRender || std::memcmp(previousVtxData.data() + offset, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.size() * sizeof(ImDrawVert)) != 0;
|
||||
} else {
|
||||
shouldRender = true;
|
||||
}
|
||||
|
||||
if (previousVtxData.size() < offset + cmdList->VtxBuffer.size() * sizeof(ImDrawVert)) {
|
||||
previousVtxData.resize(offset + cmdList->VtxBuffer.size() * sizeof(ImDrawVert));
|
||||
}
|
||||
|
||||
std::memcpy(previousVtxData.data() + offset, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.size() * sizeof(ImDrawVert));
|
||||
offset += cmdList->VtxBuffer.size() * sizeof(ImDrawVert);
|
||||
}
|
||||
}
|
||||
|
||||
shouldRender = drawDataHash != previousDrawDataHash;
|
||||
previousDrawDataHash = drawDataHash;
|
||||
previousVtxDataSize = vtxDataSize;
|
||||
}
|
||||
|
||||
GLFWwindow *backupContext = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backupContext);
|
||||
|
||||
if (shouldRender) {
|
||||
int displayWidth, displayHeight;
|
||||
glfwGetFramebufferSize(m_window, &displayWidth, &displayHeight);
|
||||
@@ -603,11 +663,6 @@ namespace hex {
|
||||
m_unlockFrameRate = true;
|
||||
}
|
||||
|
||||
GLFWwindow *backupContext = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backupContext);
|
||||
|
||||
// Process layout load requests
|
||||
// NOTE: This needs to be done before a new frame is started, otherwise ImGui won't handle docking correctly
|
||||
LayoutManager::process();
|
||||
@@ -675,7 +730,9 @@ namespace hex {
|
||||
glfwSetWindowOpacity(m_window, 1.0F);
|
||||
|
||||
glfwMakeContextCurrent(m_window);
|
||||
glfwSwapInterval(1);
|
||||
|
||||
// Disable VSync. Not like any graphics driver actually cares
|
||||
glfwSwapInterval(0);
|
||||
|
||||
// Center window
|
||||
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
||||
@@ -927,6 +984,8 @@ namespace hex {
|
||||
void Window::exitImGui() {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
|
||||
ImNodes::DestroyContext();
|
||||
ImPlot::DestroyContext();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ std::optional<std::fs::path> downloadUpdate(const std::string &url) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
hex::log::info("Writing update to file: {}", file.getPath().string());
|
||||
|
||||
// Write the downloaded update data to the file
|
||||
file.writeVector(data);
|
||||
|
||||
@@ -97,7 +99,7 @@ int installUpdate(const std::string &type, std::fs::path updatePath) {
|
||||
};
|
||||
|
||||
constexpr static auto UpdateHandlers = {
|
||||
UpdateHandler { "win-msi", ".msi", "msiexec /fa {} /passive" },
|
||||
UpdateHandler { "win-msi", ".msi", "msiexec /passive /package {}" },
|
||||
UpdateHandler { "macos-dmg", ".dmg", "hdiutil attach {}" },
|
||||
UpdateHandler { "linux-deb-22.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" },
|
||||
UpdateHandler { "linux-deb-23.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" },
|
||||
@@ -108,10 +110,14 @@ int installUpdate(const std::string &type, std::fs::path updatePath) {
|
||||
// Rename the update file to the correct extension
|
||||
const auto originalPath = updatePath;
|
||||
updatePath.replace_extension(handler.extension);
|
||||
|
||||
hex::log::info("Moving update package from {} to {}", originalPath.string(), updatePath.string());
|
||||
std::fs::rename(originalPath, updatePath);
|
||||
|
||||
// Install the update using the correct command
|
||||
hex::startProgram(hex::format(handler.command, updatePath.string()));
|
||||
const auto command = hex::format(handler.command, updatePath.string());
|
||||
hex::log::info("Starting update process with command: '{}'", command);
|
||||
hex::startProgram(command);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -163,6 +169,8 @@ int main(int argc, char **argv) {
|
||||
if (!updatePath.has_value())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
hex::log::info("Downloaded update successfully");
|
||||
|
||||
// Install the update
|
||||
return installUpdate(updateType, *updatePath);
|
||||
}
|
||||
@@ -2,11 +2,6 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include(ImHexPlugin)
|
||||
|
||||
find_file(DEFAULT_MAGIC_FILE_PATH magic.mgc HINTS ${LIBMAGIC_INCLUDE_DIR}/../share/misc)
|
||||
if (DEFAULT_MAGIC_FILE_PATH)
|
||||
add_romfs_resource(${DEFAULT_MAGIC_FILE_PATH} always_auto_extract/magic/magic.mgc)
|
||||
endif ()
|
||||
|
||||
add_imhex_plugin(
|
||||
NAME
|
||||
builtin
|
||||
@@ -114,8 +109,6 @@ add_imhex_plugin(
|
||||
source/content/views/view_achievements.cpp
|
||||
source/content/views/view_highlight_rules.cpp
|
||||
source/content/views/view_tutorials.cpp
|
||||
|
||||
source/content/helpers/notification.cpp
|
||||
INCLUDES
|
||||
include
|
||||
|
||||
@@ -128,6 +121,11 @@ add_imhex_plugin(
|
||||
LLVMDemangle
|
||||
)
|
||||
|
||||
find_file(DEFAULT_MAGIC_FILE_PATH magic.mgc HINTS ${LIBMAGIC_INCLUDE_DIR}/../share/misc)
|
||||
if (DEFAULT_MAGIC_FILE_PATH)
|
||||
add_romfs_resource(${DEFAULT_MAGIC_FILE_PATH} always_auto_extract/magic/magic.mgc)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(builtin PRIVATE setupapi)
|
||||
endif ()
|
||||
@@ -9,6 +9,7 @@ namespace hex::plugin::builtin {
|
||||
void handleHelpCommand(const std::vector<std::string> &args);
|
||||
void handlePluginsCommand(const std::vector<std::string> &args);
|
||||
void handleLanguageCommand(const std::vector<std::string> &args);
|
||||
void handleVerboseCommand(const std::vector<std::string> &args);
|
||||
|
||||
void handleOpenCommand(const std::vector<std::string> &args);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <implot_internal.h>
|
||||
#include <random>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
@@ -747,6 +748,20 @@ namespace hex {
|
||||
};
|
||||
|
||||
class DiagramByteTypesDistribution {
|
||||
private:
|
||||
struct AnnotationRegion {
|
||||
UnlocalizedString unlocalizedName;
|
||||
Region region;
|
||||
ImColor color;
|
||||
};
|
||||
|
||||
struct Tag {
|
||||
UnlocalizedString unlocalizedName;
|
||||
ImU64 value;
|
||||
ImAxis axis;
|
||||
ImGuiCol color;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit DiagramByteTypesDistribution(u64 blockSize = 256, size_t sampleSize = 0x1000) : m_blockSize(blockSize), m_sampleSize(sampleSize){ }
|
||||
|
||||
@@ -771,10 +786,44 @@ namespace hex {
|
||||
"isupper", "islower", "isdigit", "isxdigit"
|
||||
};
|
||||
|
||||
|
||||
for (u32 i = 0; i < Names.size(); i++) {
|
||||
ImPlot::PlotLine(Names[i], m_xBlockTypeDistributions.data(), m_yBlockTypeDistributionsSampled[i].data(), m_xBlockTypeDistributions.size());
|
||||
}
|
||||
|
||||
if (m_showAnnotations) {
|
||||
u32 id = 1;
|
||||
for (const auto &annotation : m_annotationRegions) {
|
||||
const auto ®ion = annotation.region;
|
||||
double xMin = region.getStartAddress();
|
||||
double xMax = region.getEndAddress();
|
||||
double yMin = 0.0F;
|
||||
double yMax = 100.0F;
|
||||
|
||||
ImPlot::DragRect(id, &xMin, &yMin, &xMax, &yMax, annotation.color, ImPlotDragToolFlags_NoFit | ImPlotDragToolFlags_NoInputs);
|
||||
|
||||
const auto min = ImPlot::PlotToPixels(xMin, yMax);
|
||||
const auto max = ImPlot::PlotToPixels(xMax, yMin);
|
||||
const auto mousePos = ImPlot::PixelsToPlot(ImGui::GetMousePos());
|
||||
if (ImGui::IsMouseHoveringRect(min, max)) {
|
||||
ImPlot::Annotation(xMin + (xMax - xMin) / 2, mousePos.y, annotation.color, ImVec2(), false, "%s", Lang(annotation.unlocalizedName).get().c_str());
|
||||
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
ImHexApi::HexEditor::setSelection(annotation.region);
|
||||
}
|
||||
}
|
||||
|
||||
id += 1;
|
||||
}
|
||||
|
||||
for (const auto &tag : m_tags) {
|
||||
if (tag.axis == ImAxis_X1)
|
||||
ImPlot::TagX(tag.value, ImGui::GetStyleColorVec4(tag.color), "%s", Lang(tag.unlocalizedName).get().c_str());
|
||||
else if (tag.axis == ImAxis_Y1)
|
||||
ImPlot::TagY(tag.value, ImGui::GetStyleColorVec4(tag.color), "%s", Lang(tag.unlocalizedName).get().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// The parameter updateHandle is used when using the pattern language since we don't have a provider
|
||||
// but just a set of bytes, we won't be able to use the drag bar correctly.
|
||||
if (updateHandle) {
|
||||
@@ -871,6 +920,12 @@ namespace hex {
|
||||
for (size_t i = 0; i < typeDist.size(); i++)
|
||||
m_yBlockTypeDistributions[i].push_back(typeDist[i] * 100);
|
||||
|
||||
if (m_yBlockTypeDistributions[2].back() + m_yBlockTypeDistributions[4].back() >= 95) {
|
||||
this->addRegion("hex.ui.diagram.byte_type_distribution.plain_text", Region { m_byteCount, m_blockSize }, 0x80FF00FF);
|
||||
} else if (std::ranges::any_of(m_blockValueCounts, [&](auto count) { return count >= m_blockSize * 0.95F; })) {
|
||||
this->addRegion("hex.ui.diagram.byte_type_distribution.similar_bytes", Region { m_byteCount, m_blockSize }, 0x8000FF00);
|
||||
}
|
||||
|
||||
m_blockCount += 1;
|
||||
m_blockValueCounts = { 0 };
|
||||
}
|
||||
@@ -898,6 +953,10 @@ namespace hex {
|
||||
m_handlePosition = filePosition;
|
||||
}
|
||||
|
||||
void enableAnnotations(bool enabled) {
|
||||
m_showAnnotations = enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::array<float, 12> calculateTypeDistribution(const std::array<ImU64, 256> &valueCounts, size_t blockSize) {
|
||||
std::array<ImU64, 12> counts = {};
|
||||
@@ -988,6 +1047,23 @@ namespace hex {
|
||||
m_xBlockTypeDistributions.push_back(m_endAddress);
|
||||
}
|
||||
|
||||
void addRegion(const UnlocalizedString &name, Region region, ImColor color) {
|
||||
const auto existingRegion = std::ranges::find_if(m_annotationRegions, [this, ®ion](const AnnotationRegion &annotation) {
|
||||
auto difference = i64(region.getEndAddress()) - i64(annotation.region.getEndAddress());
|
||||
return difference > 0 && difference < i64(m_blockSize * 32);
|
||||
});
|
||||
|
||||
if (existingRegion != m_annotationRegions.end()) {
|
||||
existingRegion->region.size += region.size;
|
||||
} else {
|
||||
m_annotationRegions.push_back({ name, region, color });
|
||||
}
|
||||
}
|
||||
|
||||
void addTag(const UnlocalizedString &name, u64 value, ImAxis axis, ImGuiCol color) {
|
||||
m_tags.push_back({ name, value, axis, color });
|
||||
}
|
||||
|
||||
private:
|
||||
// Variables used to store the parameters to process
|
||||
|
||||
@@ -1024,5 +1100,10 @@ namespace hex {
|
||||
// Hold the result of the byte distribution analysis
|
||||
std::array<std::vector<float>, 12> m_yBlockTypeDistributions, m_yBlockTypeDistributionsSampled;
|
||||
std::atomic<bool> m_processing = false;
|
||||
|
||||
std::vector<AnnotationRegion> m_annotationRegions;
|
||||
std::vector<Tag> m_tags;
|
||||
|
||||
bool m_showAnnotations = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void showError(const std::string& message);
|
||||
|
||||
void showWarning(const std::string& message);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/ui/popup.hpp>
|
||||
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class PopupCrashRecovered : public Popup<PopupCrashRecovered> {
|
||||
public:
|
||||
PopupCrashRecovered(const std::exception &e)
|
||||
: hex::Popup<PopupCrashRecovered>("hex.builtin.popup.crash_recover.title", false),
|
||||
m_errorType(typeid(e).name()),
|
||||
m_errorMessage(e.what()) { }
|
||||
|
||||
void drawContent() override {
|
||||
ImGuiExt::TextFormattedWrapped("hex.builtin.popup.crash_recover.message"_lang);
|
||||
|
||||
ImGuiExt::TextFormattedWrapped(hex::format("Error: {}: {}", llvm::itaniumDemangle(this->m_errorType), this->m_errorMessage));
|
||||
|
||||
if (ImGui::Button("hex.ui.common.okay"_lang)) {
|
||||
this->close();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
|
||||
return ImGuiWindowFlags_AlwaysAutoResize;
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 100 });
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMaxSize() const override {
|
||||
return scaled({ 600, 300 });
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_errorType, m_errorMessage;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -11,6 +11,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
class FileProvider : public hex::prv::Provider {
|
||||
public:
|
||||
constexpr static u64 MaxMemoryFileSize = 128 * 1024 * 1024;
|
||||
|
||||
FileProvider() = default;
|
||||
~FileProvider() override = default;
|
||||
|
||||
@@ -21,8 +23,6 @@ namespace hex::plugin::builtin {
|
||||
[[nodiscard]] bool isSavable() const override;
|
||||
|
||||
void resizeRaw(u64 newSize) override;
|
||||
void insertRaw(u64 offset, u64 size) override;
|
||||
void removeRaw(u64 offset, u64 size) override;
|
||||
|
||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 offset, const void *buffer, size_t size) override;
|
||||
@@ -57,12 +57,18 @@ namespace hex::plugin::builtin {
|
||||
|
||||
private:
|
||||
void convertToMemoryFile();
|
||||
void handleFileChange();
|
||||
|
||||
protected:
|
||||
std::fs::path m_path;
|
||||
wolv::io::File m_file;
|
||||
size_t m_fileSize = 0;
|
||||
|
||||
wolv::io::ChangeTracker m_changeTracker;
|
||||
std::vector<u8> m_data;
|
||||
bool m_loadedIntoMemory = false;
|
||||
bool m_ignoreNextChangeEvent = false;
|
||||
|
||||
std::optional<struct stat> m_fileStats;
|
||||
|
||||
bool m_readable = false, m_writable = false;
|
||||
|
||||
@@ -24,8 +24,6 @@ namespace hex::plugin::builtin {
|
||||
[[nodiscard]] u64 getActualSize() const override { return m_data.size(); }
|
||||
|
||||
void resizeRaw(u64 newSize) override;
|
||||
void insertRaw(u64 offset, u64 size) override;
|
||||
void removeRaw(u64 offset, u64 size) override;
|
||||
|
||||
void save() override;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(OS_WINDOWS) || defined (OS_LINUX)
|
||||
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
@@ -46,11 +46,13 @@ namespace hex::plugin::builtin {
|
||||
return m_provider->isSavable();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
|
||||
|
||||
void save() override {
|
||||
m_provider->save();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool open() override { return true; }
|
||||
[[nodiscard]] bool open() override { return m_provider != this; }
|
||||
void close() override { }
|
||||
|
||||
void resizeRaw(u64 newSize) override {
|
||||
@@ -101,8 +103,31 @@ namespace hex::plugin::builtin {
|
||||
return m_provider->getDataDescription();
|
||||
}
|
||||
|
||||
void loadSettings(const nlohmann::json &settings) override { hex::unused(settings); }
|
||||
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override { return settings; }
|
||||
void loadSettings(const nlohmann::json &settings) override {
|
||||
Provider::loadSettings(settings);
|
||||
|
||||
auto id = settings.at("id").get<u64>();
|
||||
m_startAddress = settings.at("start_address").get<u64>();
|
||||
m_size = settings.at("size").get<size_t>();
|
||||
|
||||
const auto &providers = ImHexApi::Provider::getProviders();
|
||||
auto provider = std::ranges::find_if(providers, [id](const prv::Provider *provider) {
|
||||
return provider->getID() == id;
|
||||
});
|
||||
|
||||
if (provider == providers.end())
|
||||
return;
|
||||
|
||||
m_provider = *provider;
|
||||
}
|
||||
|
||||
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override {
|
||||
settings["id"] = m_provider->getID();
|
||||
settings["start_address"] = m_startAddress;
|
||||
settings["size"] = m_size;
|
||||
|
||||
return Provider::storeSettings(settings);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override {
|
||||
return "hex.builtin.provider.view";
|
||||
|
||||
@@ -101,6 +101,7 @@ namespace hex::plugin::builtin {
|
||||
using OccurrenceTree = wolv::container::IntervalTree<Occurrence>;
|
||||
|
||||
PerProvider<std::vector<Occurrence>> m_foundOccurrences, m_sortedOccurrences;
|
||||
PerProvider<Occurrence*> m_lastSelectedOccurrence;
|
||||
PerProvider<OccurrenceTree> m_occurrenceTree;
|
||||
PerProvider<std::string> m_currFilter;
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <functional>
|
||||
|
||||
#include <TextEditor.h>
|
||||
#include "popups/popup_file_chooser.hpp"
|
||||
#include "hex/api/achievement_manager.hpp"
|
||||
|
||||
namespace pl::ptrn { class Pattern; }
|
||||
|
||||
@@ -170,7 +172,7 @@ namespace hex::plugin::builtin {
|
||||
u32 color;
|
||||
};
|
||||
|
||||
std::unique_ptr<pl::PatternLanguage> m_editorRuntime;
|
||||
std::unique_ptr<pl::PatternLanguage> m_editorRuntime;
|
||||
|
||||
PerProvider<std::vector<std::fs::path>> m_possiblePatternFiles;
|
||||
bool m_runAutomatically = false;
|
||||
@@ -255,6 +257,39 @@ namespace hex::plugin::builtin {
|
||||
void registerMenuItems();
|
||||
void registerHandlers();
|
||||
|
||||
std::function<void()> importPatternFile = [&] {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
|
||||
std::vector<std::fs::path> paths;
|
||||
|
||||
for (const auto &imhexPath : basePaths) {
|
||||
if (!wolv::io::fs::exists(imhexPath)) continue;
|
||||
|
||||
std::error_code error;
|
||||
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
|
||||
if (entry.is_regular_file() && entry.path().extension() == ".hexpat")
|
||||
paths.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
ui::PopupFileChooser::open(
|
||||
basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
|
||||
[this, provider](const std::fs::path &path) {
|
||||
this->loadPatternFile(path, provider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
std::function<void()> exportPatternFile = [&] {
|
||||
fs::openFileBrowser(
|
||||
fs::DialogMode::Save, { {"Pattern", "hexpat"} },
|
||||
[this](const auto &path) {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeString(wolv::util::trim(m_textEditor.GetText()));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
void appendEditorText(const std::string &text);
|
||||
void appendVariable(const std::string &type);
|
||||
void appendArray(const std::string &type, size_t size);
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -135,6 +135,7 @@
|
||||
"hex.builtin.menu.file.export.ips32": "IPS32 Patch",
|
||||
"hex.builtin.menu.file.export.bookmark": "Bookmark",
|
||||
"hex.builtin.menu.file.export.pattern": "Pattern File",
|
||||
"hex.builtin.menu.file.export.pattern_file": "Export pattern File",
|
||||
"hex.builtin.menu.file.export.data_processor": "Data Processor Workspace",
|
||||
"hex.builtin.menu.file.export.popup.create": "Cannot export data. Failed to create file!",
|
||||
"hex.builtin.menu.file.export.report": "Report",
|
||||
@@ -147,6 +148,7 @@
|
||||
"hex.builtin.menu.file.import.modified_file": "Modified File",
|
||||
"hex.builtin.menu.file.import.bookmark": "Bookmark",
|
||||
"hex.builtin.menu.file.import.pattern": "Pattern File",
|
||||
"hex.builtin.menu.file.import.pattern_file": "Import pattern File",
|
||||
"hex.builtin.menu.file.import.data_processor": "Data Processor Workspace",
|
||||
"hex.builtin.menu.file.import.custom_encoding": "Custom Encoding File",
|
||||
"hex.builtin.menu.file.import.modified_file.popup.invalid_size": "File selected do not have the same size as the current file. Both sizes must match",
|
||||
@@ -374,6 +376,8 @@
|
||||
"hex.builtin.popup.exit_application.desc": "You have unsaved changes made to your Project.\nAre you sure you want to exit?",
|
||||
"hex.builtin.popup.exit_application.title": "Exit Application?",
|
||||
"hex.builtin.popup.waiting_for_tasks.title": "Waiting for Tasks",
|
||||
"hex.builtin.popup.crash_recover.title": "Crash recovery",
|
||||
"hex.builtin.popup.crash_recover.message": "An exception was thrown, but ImHex was able to catch it and advert a crash",
|
||||
"hex.builtin.popup.blocking_task.title": "Running Task",
|
||||
"hex.builtin.popup.blocking_task.desc": "A task is currently executing.",
|
||||
"hex.builtin.popup.save_layout.title": "Save Layout",
|
||||
@@ -400,6 +404,8 @@
|
||||
"hex.builtin.provider.file.size": "Size",
|
||||
"hex.builtin.provider.file.menu.open_file": "Open file externally",
|
||||
"hex.builtin.provider.file.menu.open_folder": "Open containing folder",
|
||||
"hex.builtin.provider.file.too_large": "This file is too large to be loaded into memory. Opening it anyways will result in modifications to be written directly to the file. Would you like to open it as Read-Only instead?",
|
||||
"hex.builtin.provider.file.reload_changes": "The file has been modified by an external source. Do you want to reload it?",
|
||||
"hex.builtin.provider.gdb": "GDB Server Provider",
|
||||
"hex.builtin.provider.gdb.ip": "IP Address",
|
||||
"hex.builtin.provider.gdb.name": "GDB Server <{0}:{1}>",
|
||||
@@ -498,7 +504,7 @@
|
||||
"hex.builtin.setting.toolbar.icons": "Toolbar Icons",
|
||||
"hex.builtin.shortcut.next_provider": "Select next provider",
|
||||
"hex.builtin.shortcut.prev_provider": "Select previous provider",
|
||||
"hex.builtin.title_bar_button.debug_build": "Debug build",
|
||||
"hex.builtin.title_bar_button.debug_build": "Debug build\n\nSHIFT + Click to open Debug Menu",
|
||||
"hex.builtin.title_bar_button.feedback": "Leave Feedback",
|
||||
"hex.builtin.tools.ascii_table": "ASCII table",
|
||||
"hex.builtin.tools.ascii_table.octal": "Show octal",
|
||||
@@ -842,6 +848,7 @@
|
||||
"hex.builtin.information_section.info_analysis.highest_entropy": "Highest block entropy",
|
||||
"hex.builtin.information_section.info_analysis.lowest_entropy": "Lowest block entropy",
|
||||
"hex.builtin.information_section.info_analysis": "Information analysis",
|
||||
"hex.builtin.information_section.info_analysis.show_annotations": "Show annotations",
|
||||
"hex.builtin.information_section.relationship_analysis": "Byte Relationship",
|
||||
"hex.builtin.information_section.relationship_analysis.sample_size": "Sample size",
|
||||
"hex.builtin.information_section.relationship_analysis.brightness": "Brightness",
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include <hex/helpers/magic.hpp>
|
||||
#include <hex/helpers/crypto.hpp>
|
||||
#include <hex/helpers/literals.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <romfs/romfs.hpp>
|
||||
|
||||
#include <hex/api/plugin_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/subcommands/subcommands.hpp>
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
@@ -47,16 +47,30 @@ namespace hex::plugin::builtin {
|
||||
"Available subcommands:\n"
|
||||
);
|
||||
|
||||
size_t longestCommand = 0;
|
||||
size_t longestLongCommand = 0, longestShortCommand = 0;
|
||||
for (const auto &plugin : PluginManager::getPlugins()) {
|
||||
for (const auto &subCommand : plugin.getSubCommands()) {
|
||||
longestCommand = std::max(longestCommand, subCommand.commandKey.size());
|
||||
longestLongCommand = std::max(longestLongCommand, subCommand.commandLong.size());
|
||||
longestShortCommand = std::max(longestShortCommand, subCommand.commandShort.size());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &plugin : PluginManager::getPlugins()) {
|
||||
for (const auto &subCommand : plugin.getSubCommands()) {
|
||||
hex::log::println(" --{}{: <{}} {}", subCommand.commandKey, "", longestCommand - subCommand.commandKey.size(), subCommand.commandDesc);
|
||||
hex::log::println(" "
|
||||
"{}"
|
||||
"{: <{}}"
|
||||
"{}"
|
||||
"{}"
|
||||
"{: <{}}"
|
||||
"{}",
|
||||
subCommand.commandShort.empty() ? " " : "-",
|
||||
subCommand.commandShort, longestShortCommand,
|
||||
subCommand.commandShort.empty() ? " " : ", ",
|
||||
subCommand.commandLong.empty() ? " " : "--",
|
||||
subCommand.commandLong, longestLongCommand + 5,
|
||||
subCommand.commandDescription
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +153,10 @@ namespace hex::plugin::builtin {
|
||||
ImHexApi::System::impl::addInitArgument("language", args[0]);
|
||||
}
|
||||
|
||||
void handleVerboseCommand(const std::vector<std::string> &) {
|
||||
hex::log::enableDebugLogging();
|
||||
}
|
||||
|
||||
void handleHashCommand(const std::vector<std::string> &args) {
|
||||
if (args.size() != 2) {
|
||||
hex::log::println("usage: imhex --hash <algorithm> <file>");
|
||||
|
||||
@@ -164,6 +164,8 @@ namespace hex::plugin::builtin {
|
||||
m_chunkBasedEntropy.reset(m_inputChunkSize, region.getStartAddress(), region.getEndAddress(),
|
||||
provider->getBaseAddress(), provider->getActualSize());
|
||||
|
||||
m_byteTypesDistribution.enableAnnotations(m_showAnnotations);
|
||||
|
||||
// Create a handle to the file
|
||||
auto reader = prv::ProviderReader(provider);
|
||||
reader.seek(region.getStartAddress());
|
||||
@@ -194,6 +196,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
void drawSettings() override {
|
||||
ImGuiExt::InputHexadecimal("hex.builtin.information_section.info_analysis.block_size"_lang, &m_inputChunkSize);
|
||||
ImGui::Checkbox("hex.builtin.information_section.info_analysis.show_annotations"_lang, &m_showAnnotations);
|
||||
}
|
||||
|
||||
void drawContent() override {
|
||||
@@ -319,12 +322,14 @@ namespace hex::plugin::builtin {
|
||||
void load(const nlohmann::json &data) override {
|
||||
InformationSection::load(data);
|
||||
|
||||
m_inputChunkSize = data.value("block_size", 0);
|
||||
m_inputChunkSize = data.value("block_size", 0);
|
||||
m_showAnnotations = data.value("annotations", true);
|
||||
}
|
||||
|
||||
nlohmann::json store() override {
|
||||
auto result = InformationSection::store();
|
||||
result["block_size"] = m_inputChunkSize;
|
||||
result["block_size"] = m_inputChunkSize;
|
||||
result["annotations"] = m_showAnnotations;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -341,6 +346,8 @@ namespace hex::plugin::builtin {
|
||||
u64 m_lowestBlockEntropyAddress = 0x00;
|
||||
double m_plainTextCharacterPercentage = -1.0;
|
||||
|
||||
bool m_showAnnotations = true;
|
||||
|
||||
DiagramByteDistribution m_byteDistribution;
|
||||
DiagramByteTypesDistribution m_byteTypesDistribution;
|
||||
DiagramChunkBasedEntropyAnalysis m_chunkBasedEntropy;
|
||||
|
||||
@@ -21,10 +21,19 @@
|
||||
#include <popups/popup_question.hpp>
|
||||
#include <content/popups/popup_tasks_waiting.hpp>
|
||||
#include <content/popups/popup_unsaved_changes.hpp>
|
||||
#include <content/popups/popup_crash_recovered.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
static void openFile(const std::fs::path &path) {
|
||||
if (path.extension() == ".hexproj") {
|
||||
if (!ProjectFile::load(path)) {
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
|
||||
if (auto *fileProvider = dynamic_cast<FileProvider*>(provider); fileProvider != nullptr) {
|
||||
fileProvider->setPath(path);
|
||||
@@ -33,6 +42,7 @@ namespace hex::plugin::builtin {
|
||||
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
|
||||
} else {
|
||||
EventProviderOpened::post(fileProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +50,10 @@ namespace hex::plugin::builtin {
|
||||
void registerEventHandlers() {
|
||||
|
||||
static bool imhexClosing = false;
|
||||
EventCrashRecovered::subscribe([](const std::exception &e) {
|
||||
PopupCrashRecovered::open(e);
|
||||
});
|
||||
|
||||
EventWindowClosing::subscribe([](GLFWwindow *window) {
|
||||
imhexClosing = false;
|
||||
if (ImHexApi::Provider::isDirty() && !imhexClosing) {
|
||||
@@ -91,9 +105,6 @@ namespace hex::plugin::builtin {
|
||||
EventProviderOpened::subscribe([](hex::prv::Provider *provider) {
|
||||
if (provider != nullptr && ImHexApi::Provider::get() == provider) {
|
||||
RequestUpdateWindowTitle::post();
|
||||
|
||||
if (!provider->isWritable())
|
||||
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,23 +122,24 @@ namespace hex::plugin::builtin {
|
||||
if (path.extension() == ".hexproj") {
|
||||
if (!ProjectFile::load(path)) {
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
|
||||
}
|
||||
} else {
|
||||
auto newProvider = static_cast<FileProvider*>(
|
||||
ImHexApi::Provider::createProvider("hex.builtin.provider.file", true)
|
||||
);
|
||||
|
||||
if (newProvider == nullptr)
|
||||
return;
|
||||
|
||||
newProvider->setPath(path);
|
||||
if (!newProvider->open()) {
|
||||
hex::ImHexApi::Provider::remove(newProvider);
|
||||
} else {
|
||||
EventProviderOpened::post(newProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto newProvider = static_cast<FileProvider*>(
|
||||
ImHexApi::Provider::createProvider("hex.builtin.provider.file", true)
|
||||
);
|
||||
|
||||
if (newProvider == nullptr)
|
||||
return;
|
||||
|
||||
newProvider->setPath(path);
|
||||
if (!newProvider->open()) {
|
||||
hex::ImHexApi::Provider::remove(newProvider);
|
||||
} else {
|
||||
EventProviderOpened::post(newProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
|
||||
}
|
||||
}, {}, true);
|
||||
} else if (name == "Open Project") {
|
||||
@@ -239,6 +251,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopup))
|
||||
return;
|
||||
if (ImGui::IsAnyItemHovered())
|
||||
return;
|
||||
|
||||
static ImGuiWindow *lastFocusedWindow = nullptr;
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void showError(const std::string& message){
|
||||
ui::ToastError::open(message);
|
||||
log::error(message);
|
||||
}
|
||||
|
||||
void showWarning(const std::string& message){
|
||||
ui::ToastWarning::open(message);
|
||||
log::warn(message);
|
||||
}
|
||||
}
|
||||
@@ -278,6 +278,13 @@ namespace hex::plugin::builtin {
|
||||
|
||||
// Disable merge mode for this font but retain the rest of the configuration
|
||||
cfg.MergeMode = false;
|
||||
|
||||
auto size = fontSize;
|
||||
if (font.defaultSize.has_value())
|
||||
size = font.defaultSize.value() * ImHexApi::System::getGlobalScale();
|
||||
|
||||
cfg.SizePixels = size;
|
||||
|
||||
ON_SCOPE_EXIT { cfg.MergeMode = true; };
|
||||
|
||||
// Construct a range that only contains the first glyph of the font
|
||||
|
||||
@@ -28,8 +28,6 @@ using namespace wolv::literals;
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
static bool s_demoWindowOpen = false;
|
||||
|
||||
namespace {
|
||||
|
||||
bool noRunningTasks() {
|
||||
@@ -187,7 +185,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
void exportSelectionToFile() {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) {
|
||||
TaskManager::createTask("hex.ui.common.processing", TaskManager::NoProgress, [path](auto &) {
|
||||
TaskManager::createTask("hex.ui.common.processing", TaskManager::NoProgress, [path](auto &task) {
|
||||
wolv::io::File outputFile(path, wolv::io::File::Mode::Create);
|
||||
if (!outputFile.isValid()) {
|
||||
TaskManager::doLater([] {
|
||||
@@ -200,11 +198,12 @@ namespace hex::plugin::builtin {
|
||||
std::vector<u8> bytes(5_MiB);
|
||||
|
||||
auto selection = ImHexApi::HexEditor::getSelection();
|
||||
for (u64 address = selection->getStartAddress(); address <= selection->getEndAddress(); address += bytes.size()) {
|
||||
for (u64 address = selection->getStartAddress(); address < selection->getEndAddress(); address += bytes.size()) {
|
||||
bytes.resize(std::min<u64>(bytes.size(), selection->getEndAddress() - address));
|
||||
provider->read(address, bytes.data(), bytes.size());
|
||||
|
||||
outputFile.writeVector(bytes);
|
||||
task.update();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -416,7 +415,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
/* Import */
|
||||
{
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.file", "hex.builtin.menu.file.import" }, ICON_VS_SIGN_IN, 2140, []{}, noRunningTaskAndWritableProvider);
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.file", "hex.builtin.menu.file.import" }, ICON_VS_SIGN_IN, 2140, []{}, noRunningTaskAndValidProvider);
|
||||
|
||||
/* IPS */
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.ips"}, ICON_VS_GIT_PULL_REQUEST_NEW_CHANGES, 2150,
|
||||
@@ -559,13 +558,6 @@ namespace hex::plugin::builtin {
|
||||
state = !state;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::MenuItem("hex.builtin.menu.view.demo"_lang, "", &s_demoWindowOpen);
|
||||
ImGui::MenuItem("hex.builtin.menu.view.debug"_lang, "", &hex::dbg::impl::getDebugWindowState());
|
||||
#endif
|
||||
});
|
||||
|
||||
}
|
||||
@@ -573,8 +565,6 @@ namespace hex::plugin::builtin {
|
||||
static void createLayoutMenu() {
|
||||
LayoutManager::reload();
|
||||
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.workspace", 4000);
|
||||
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.workspace", "hex.builtin.menu.workspace.layout" }, ICON_VS_LAYOUT, 1050, []{}, ImHexApi::Provider::isValid);
|
||||
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.workspace", "hex.builtin.menu.workspace.layout", "hex.builtin.menu.workspace.layout.save" }, 1100, Shortcut::None, [] {
|
||||
@@ -600,12 +590,12 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
|
||||
bool shift = ImGui::GetIO().KeyShift;
|
||||
bool shiftPressed = ImGui::GetIO().KeyShift;
|
||||
for (auto &[name, path] : LayoutManager::getLayouts()) {
|
||||
if (ImGui::MenuItem(hex::format("{}{}", name, shift ? " " ICON_VS_X : "").c_str(), "", false, ImHexApi::Provider::isValid())) {
|
||||
if (shift) {
|
||||
wolv::io::fs::remove(path);
|
||||
LayoutManager::reload();
|
||||
if (ImGui::MenuItem(hex::format("{}{}", name, shiftPressed ? " " ICON_VS_X : "").c_str(), "", false, ImHexApi::Provider::isValid())) {
|
||||
if (shiftPressed) {
|
||||
LayoutManager::removeLayout(name);
|
||||
break;
|
||||
} else {
|
||||
LayoutManager::load(path);
|
||||
}
|
||||
@@ -615,6 +605,8 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
static void createWorkspaceMenu() {
|
||||
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.workspace", 4000);
|
||||
|
||||
createLayoutMenu();
|
||||
|
||||
ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.workspace" }, 3000);
|
||||
@@ -627,11 +619,19 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.workspace" }, 3200, [] {
|
||||
const auto &workspaces = WorkspaceManager::getWorkspaces();
|
||||
|
||||
bool shiftPressed = ImGui::GetIO().KeyShift;
|
||||
for (auto it = workspaces.begin(); it != workspaces.end(); ++it) {
|
||||
const auto &[name, workspace] = *it;
|
||||
|
||||
if (ImGui::MenuItem(name.c_str(), "", it == WorkspaceManager::getCurrentWorkspace(), ImHexApi::Provider::isValid())) {
|
||||
WorkspaceManager::switchWorkspace(name);
|
||||
bool canRemove = shiftPressed && !workspace.builtin;
|
||||
if (ImGui::MenuItem(hex::format("{}{}", name, canRemove ? " " ICON_VS_X : "").c_str(), "", it == WorkspaceManager::getCurrentWorkspace(), ImHexApi::Provider::isValid())) {
|
||||
if (canRemove) {
|
||||
WorkspaceManager::removeWorkspace(name);
|
||||
break;
|
||||
} else {
|
||||
WorkspaceManager::switchWorkspace(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -653,13 +653,6 @@ namespace hex::plugin::builtin {
|
||||
createWorkspaceMenu();
|
||||
createExtrasMenu();
|
||||
createHelpMenu();
|
||||
|
||||
(void)EventFrameEnd::subscribe([] {
|
||||
if (s_demoWindowOpen) {
|
||||
ImGui::ShowDemoWindow(&s_demoWindowOpen);
|
||||
ImPlot::ShowDemoWindow(&s_demoWindowOpen);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -348,6 +348,8 @@ namespace hex::plugin::builtin {
|
||||
// Draw deny button
|
||||
ImGui::SetCursorPosX(buttonPos(2));
|
||||
if (ImGui::Button("hex.ui.common.deny"_lang, buttonSize)) {
|
||||
ContentRegistry::Settings::write<int>("hex.builtin.setting.general", "hex.builtin.setting.general.server_contact", 0);
|
||||
ContentRegistry::Settings::write<int>("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", 0);
|
||||
page += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <content/helpers/notification.hpp>
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
@@ -23,26 +22,29 @@ namespace hex::plugin::builtin {
|
||||
|
||||
bool load(const std::fs::path &filePath) {
|
||||
if (!wolv::io::fs::exists(filePath) || !wolv::io::fs::isRegularFile(filePath)) {
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.file_not_found"_lang,
|
||||
wolv::util::toUTF8String(filePath)
|
||||
)));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Tar tar(filePath, Tar::Mode::Read);
|
||||
if (!tar.isValid()) {
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.invalid_tar"_lang,
|
||||
tar.getOpenErrorString()
|
||||
)));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tar.contains(MetadataPath)) {
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang)
|
||||
));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -50,9 +52,10 @@ namespace hex::plugin::builtin {
|
||||
const auto metadataContent = tar.readVector(MetadataPath);
|
||||
|
||||
if (!std::string(metadataContent.begin(), metadataContent.end()).starts_with(MetadataHeaderMagic)) {
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang)
|
||||
));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -176,4 +179,4 @@ namespace hex::plugin::builtin {
|
||||
void registerProjectHandlers() {
|
||||
hex::ProjectFile::setProjectFunctions(load, store);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
#include <content/providers/process_memory_provider.hpp>
|
||||
#include <content/providers/base64_provider.hpp>
|
||||
#include <popups/popup_notification.hpp>
|
||||
#include "content/helpers/notification.hpp"
|
||||
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::Provider::add<MemoryFileProvider>(false);
|
||||
ContentRegistry::Provider::add<ViewProvider>(false);
|
||||
|
||||
#if defined(OS_WINDOWS) ||defined (OS_LINUX)
|
||||
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
|
||||
ContentRegistry::Provider::add<ProcessMemoryProvider>();
|
||||
#endif
|
||||
|
||||
@@ -70,9 +70,11 @@ namespace hex::plugin::builtin {
|
||||
if (newProvider == nullptr) {
|
||||
// If a provider is not created, it will be overwritten when saving the project,
|
||||
// so we should prevent the project from loading at all
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.create_provider"_lang, providerType)
|
||||
));
|
||||
ui::ToastError::open(
|
||||
hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.create_provider"_lang, providerType)
|
||||
)
|
||||
);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
@@ -94,29 +96,27 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
|
||||
std::string warningMsg;
|
||||
std::string warningMessage;
|
||||
for (const auto &warning : providerWarnings){
|
||||
ImHexApi::Provider::remove(warning.first);
|
||||
warningMsg.append(
|
||||
warningMessage.append(
|
||||
hex::format("\n - {} : {}", warning.first->getName(), warning.second));
|
||||
}
|
||||
|
||||
// If no providers were opened, display an error with
|
||||
// the warnings that happened when opening them
|
||||
if (ImHexApi::Provider::getProviders().size() == 0) {
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.no_providers"_lang)) + warningMsg);
|
||||
if (ImHexApi::Provider::getProviders().empty()) {
|
||||
ui::ToastError::open(hex::format("{}{}", "hex.builtin.popup.error.project.load"_lang, "hex.builtin.popup.error.project.load.no_providers"_lang, warningMessage));
|
||||
|
||||
return false;
|
||||
} else {
|
||||
|
||||
// Else, if are warnings, still display them
|
||||
if (warningMsg.empty()) {
|
||||
} else {
|
||||
// Else, if there are warnings, still display them
|
||||
if (warningMessage.empty()) {
|
||||
return true;
|
||||
} else {
|
||||
showWarning(
|
||||
hex::format("hex.builtin.popup.error.project.load.some_providers_failed"_lang, warningMsg));
|
||||
ui::ToastWarning::open(hex::format("hex.builtin.popup.error.project.load.some_providers_failed"_lang, warningMessage));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
#elif defined(OS_LINUX)
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/fs.h>
|
||||
#if !defined(OS_FREEBSD)
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
@@ -41,8 +43,11 @@
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
#define lseek lseek64
|
||||
#if defined(OS_LINUX) && !defined(OS_FREEBSD)
|
||||
#define lseek lseek64
|
||||
#elif defined(OS_FREEBSD)
|
||||
#include <sys/disk.h>
|
||||
#define DEFAULT_SECTOR_SIZE 512
|
||||
#endif
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
@@ -83,6 +88,12 @@ namespace hex::plugin::builtin {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(OS_FREEBSD) && defined(DIOCGSECTORSIZE)
|
||||
int blkdev_get_sector_size(int fd, int *sector_size) {
|
||||
if (ioctl(fd, DIOCGSECTORSIZE, sector_size) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int blkdev_get_sector_size(int fd, int *sector_size) {
|
||||
(void)fd;
|
||||
@@ -97,6 +108,12 @@ namespace hex::plugin::builtin {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(OS_FREEBSD) && defined(DIOCGMEDIASIZE)
|
||||
int blkdev_get_size(int fd, u64 *bytes) {
|
||||
if (ioctl(fd, DIOCGMEDIASIZE, bytes) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int blkdev_get_size(int fd, u64 *bytes) {
|
||||
struct stat st;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "content/providers/file_provider.hpp"
|
||||
#include "content/providers/memory_file_provider.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <fmt/chrono.h>
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <cstring>
|
||||
#include <popups/popup_question.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
@@ -41,25 +43,38 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
bool FileProvider::isSavable() const {
|
||||
return m_undoRedoStack.canUndo();
|
||||
return m_loadedIntoMemory;
|
||||
}
|
||||
|
||||
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
||||
if (m_fileSize == 0 || (offset + size) > m_fileSize || buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
m_file.readBufferAtomic(offset, static_cast<u8*>(buffer), size);
|
||||
if (m_loadedIntoMemory)
|
||||
std::memcpy(buffer, m_data.data() + offset, size);
|
||||
else
|
||||
m_file.readBufferAtomic(offset, static_cast<u8*>(buffer), size);
|
||||
}
|
||||
|
||||
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
|
||||
if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
|
||||
if (m_loadedIntoMemory)
|
||||
std::memcpy(m_data.data() + offset, buffer, size);
|
||||
else
|
||||
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
|
||||
}
|
||||
|
||||
void FileProvider::save() {
|
||||
m_file.flush();
|
||||
if (m_loadedIntoMemory) {
|
||||
m_ignoreNextChangeEvent = true;
|
||||
m_file.open();
|
||||
m_file.writeVectorAtomic(0x00, m_data);
|
||||
m_file.setSize(m_data.size());
|
||||
} else {
|
||||
m_file.flush();
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
FILETIME ft;
|
||||
@@ -74,6 +89,9 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_loadedIntoMemory)
|
||||
m_file.close();
|
||||
|
||||
Provider::save();
|
||||
}
|
||||
|
||||
@@ -85,51 +103,12 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void FileProvider::resizeRaw(u64 newSize) {
|
||||
m_file.setSize(newSize);
|
||||
}
|
||||
if (m_loadedIntoMemory)
|
||||
m_data.resize(newSize);
|
||||
else
|
||||
m_file.setSize(newSize);
|
||||
|
||||
void FileProvider::insertRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
this->resizeRaw(oldSize + size);
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
||||
|
||||
position -= readSize;
|
||||
|
||||
this->readRaw(position, buffer.data(), readSize);
|
||||
this->writeRaw(position, zeroBuffer.data(), readSize);
|
||||
this->writeRaw(position + size, buffer.data(), readSize);
|
||||
}
|
||||
}
|
||||
|
||||
void FileProvider::removeRaw(u64 offset, u64 size) {
|
||||
if (offset > this->getActualSize() || size == 0)
|
||||
return;
|
||||
|
||||
if ((offset + size) > this->getActualSize())
|
||||
size = this->getActualSize() - offset;
|
||||
|
||||
auto oldSize = this->getActualSize();
|
||||
|
||||
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->resizeRaw(newSize);
|
||||
m_fileSize = newSize;
|
||||
}
|
||||
|
||||
u64 FileProvider::getActualSize() const {
|
||||
@@ -207,7 +186,7 @@ namespace hex::plugin::builtin {
|
||||
m_readable = true;
|
||||
m_writable = true;
|
||||
|
||||
if (!std::fs::exists(m_path)) {
|
||||
if (!wolv::io::fs::exists(m_path)) {
|
||||
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(ENOENT)));
|
||||
return false;
|
||||
}
|
||||
@@ -222,6 +201,8 @@ namespace hex::plugin::builtin {
|
||||
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
|
||||
}
|
||||
|
||||
m_file = std::move(file);
|
||||
@@ -242,12 +223,36 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
|
||||
if (m_writable) {
|
||||
if (m_fileSize < MaxMemoryFileSize) {
|
||||
m_data = m_file.readVectorAtomic(0x00, m_fileSize);
|
||||
if (!m_data.empty()) {
|
||||
m_changeTracker = wolv::io::ChangeTracker(m_file);
|
||||
m_changeTracker.startTracking([this]{ this->handleFileChange(); });
|
||||
m_file.close();
|
||||
m_loadedIntoMemory = true;
|
||||
}
|
||||
} else {
|
||||
m_writable = false;
|
||||
ui::PopupQuestion::open("hex.builtin.provider.file.too_large"_lang,
|
||||
[this] {
|
||||
m_writable = false;
|
||||
},
|
||||
[this] {
|
||||
m_writable = true;
|
||||
RequestUpdateWindowTitle::post();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileProvider::close() {
|
||||
m_file.close();
|
||||
m_data.clear();
|
||||
s_openedFiles.erase(this);
|
||||
m_changeTracker.stopTracking();
|
||||
}
|
||||
|
||||
void FileProvider::loadSettings(const nlohmann::json &settings) {
|
||||
@@ -328,4 +333,19 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
|
||||
void FileProvider::handleFileChange() {
|
||||
if (m_ignoreNextChangeEvent) {
|
||||
m_ignoreNextChangeEvent = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ui::PopupQuestion::open("hex.builtin.provider.file.reload_changes"_lang, [this] {
|
||||
this->close();
|
||||
(void)this->open();
|
||||
getUndoStack().reapply();
|
||||
},
|
||||
[]{});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -69,43 +69,6 @@ namespace hex::plugin::builtin {
|
||||
m_data.resize(newSize);
|
||||
}
|
||||
|
||||
void MemoryFileProvider::insertRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
this->resizeRaw(oldSize + size);
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
||||
|
||||
position -= readSize;
|
||||
|
||||
this->readRaw(position, buffer.data(), readSize);
|
||||
this->writeRaw(position, zeroBuffer.data(), readSize);
|
||||
this->writeRaw(position + size, buffer.data(), readSize);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryFileProvider::removeRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
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->resizeRaw(oldSize - size);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MemoryFileProvider::getName() const {
|
||||
if (m_name.empty())
|
||||
return Lang("hex.builtin.provider.mem_file.unsaved");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if defined(OS_WINDOWS) || defined (OS_LINUX)
|
||||
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
|
||||
|
||||
#include <content/providers/process_memory_provider.hpp>
|
||||
|
||||
|
||||
@@ -219,7 +219,9 @@ namespace hex::plugin::builtin::recent {
|
||||
void loadRecentEntry(const RecentEntry &recentEntry) {
|
||||
if (recentEntry.type == "project") {
|
||||
std::fs::path projectPath = recentEntry.data["path"].get<std::string>();
|
||||
ProjectFile::load(projectPath);
|
||||
if (!ProjectFile::load(projectPath)) {
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(projectPath)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto *provider = ImHexApi::Provider::createProvider(recentEntry.type, true);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
|
||||
#include <fonts/codicons_font.h>
|
||||
#include <imgui.h>
|
||||
@@ -16,21 +17,15 @@
|
||||
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
#include <csignal>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void addTitleBarButtons() {
|
||||
#if defined(DEBUG)
|
||||
ContentRegistry::Interface::addTitleBarButton(ICON_VS_DEBUG, "hex.builtin.title_bar_button.debug_build", []{
|
||||
if (ImGui::GetIO().KeyCtrl) {
|
||||
// Explicitly trigger a segfault by writing to an invalid memory location
|
||||
// Used for debugging crashes
|
||||
*reinterpret_cast<u8 *>(0x10) = 0x10;
|
||||
std::unreachable();
|
||||
} else if (ImGui::GetIO().KeyShift) {
|
||||
// Explicitly trigger an abort by throwing an uncaught exception
|
||||
// Used for debugging exception errors
|
||||
throw std::runtime_error("Debug Error");
|
||||
std::unreachable();
|
||||
if (ImGui::GetIO().KeyShift) {
|
||||
RequestOpenPopup::post("DebugMenu");
|
||||
} else {
|
||||
hex::openWebpage("https://imhex.werwolv.net/debug");
|
||||
}
|
||||
@@ -54,6 +49,86 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
|
||||
static void drawDebugPopup() {
|
||||
static bool showImGuiDemo = false;
|
||||
static bool showImPlotDemo = false;
|
||||
|
||||
ImGui::SetNextWindowSize(scaled({ 300, 150 }), ImGuiCond_Always);
|
||||
if (ImGui::BeginPopup("DebugMenu")) {
|
||||
if (ImGui::BeginTabBar("DebugTabBar")) {
|
||||
if (ImGui::BeginTabItem("ImHex")) {
|
||||
if (ImGui::BeginChild("Scrolling", ImGui::GetContentRegionAvail())) {
|
||||
ImGui::Checkbox("Show Debug Variables", &dbg::impl::getDebugWindowState());
|
||||
|
||||
ImGuiExt::Header("Information");
|
||||
ImGuiExt::TextFormatted("Running Tasks: {0}", TaskManager::getRunningTaskCount());
|
||||
ImGuiExt::TextFormatted("Running Background Tasks: {0}", TaskManager::getRunningBackgroundTaskCount());
|
||||
ImGuiExt::TextFormatted("Last Frame Time: {0:.3f}ms", ImHexApi::System::getLastFrameTime() * 1000.0F);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("ImGui")) {
|
||||
if (ImGui::BeginChild("Scrolling", ImGui::GetContentRegionAvail())) {
|
||||
auto ctx = ImGui::GetCurrentContext();
|
||||
ImGui::Checkbox("Show ImGui Demo", &showImGuiDemo);
|
||||
ImGui::Checkbox("Show ImPlot Demo", &showImPlotDemo);
|
||||
|
||||
if (ImGui::Button("Trigger Breakpoint in Item") || ctx->DebugItemPickerActive)
|
||||
ImGui::DebugStartItemPicker();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Crashes")) {
|
||||
if (ImGui::BeginChild("Scrolling", ImGui::GetContentRegionAvail())) {
|
||||
if (ImGui::Button("Throw Exception")) {
|
||||
TaskManager::doLater([] {
|
||||
throw std::runtime_error("Test exception");
|
||||
});
|
||||
}
|
||||
if (ImGui::Button("Access Invalid Memory")) {
|
||||
TaskManager::doLater([] {
|
||||
*reinterpret_cast<u32*>(0x10) = 0x10;
|
||||
std::unreachable();
|
||||
});
|
||||
}
|
||||
if (ImGui::Button("Raise SIGSEGV")) {
|
||||
TaskManager::doLater([] {
|
||||
raise(SIGSEGV);
|
||||
});
|
||||
}
|
||||
if (ImGui::Button("Corrupt Memory")) {
|
||||
TaskManager::doLater([] {
|
||||
auto bytes = new u8[0xFFFFF];
|
||||
|
||||
delete[] bytes;
|
||||
delete[] bytes;
|
||||
});
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (showImGuiDemo)
|
||||
ImGui::ShowDemoWindow(&showImGuiDemo);
|
||||
if (showImPlotDemo)
|
||||
ImPlot::ShowDemoWindow(&showImPlotDemo);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool s_drawDragDropOverlay = false;
|
||||
static void drawDragNDropOverlay() {
|
||||
if (!s_drawDragDropOverlay)
|
||||
@@ -98,6 +173,10 @@ namespace hex::plugin::builtin {
|
||||
EventFrameEnd::subscribe(drawGlobalPopups);
|
||||
EventFrameEnd::subscribe(drawDragNDropOverlay);
|
||||
|
||||
#if defined(DEBUG)
|
||||
EventFrameEnd::subscribe(drawDebugPopup);
|
||||
#endif
|
||||
|
||||
EventFileDragged::subscribe([](bool entered) {
|
||||
s_drawDragDropOverlay = entered;
|
||||
});
|
||||
@@ -229,6 +308,16 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ContentRegistry::Interface::addFooterItem([] {
|
||||
if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value()) {
|
||||
ImGuiExt::TextFormattedSelectable("0x{:02X} - 0x{:02X} ({} bytes)",
|
||||
selection->getStartAddress(),
|
||||
selection->getEndAddress(),
|
||||
selection->getSize()
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void drawProviderContextMenu(prv::Provider *provider) {
|
||||
|
||||
@@ -235,32 +235,20 @@ namespace hex::plugin::builtin {
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
void ViewAbout::drawContributorPage() {
|
||||
struct Contributor {
|
||||
const char *name;
|
||||
const char *description;
|
||||
const char *link;
|
||||
bool mainContributor;
|
||||
};
|
||||
|
||||
constexpr static std::array Contributors = {
|
||||
Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true },
|
||||
Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/Nowilltolife", true },
|
||||
Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false },
|
||||
Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false },
|
||||
Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false },
|
||||
Contributor { "All other amazing contributors", "Being part of the community, opening issues, PRs and donating", "https://github.com/WerWolv/ImHex/graphs/contributors", false }
|
||||
};
|
||||
|
||||
ImGuiExt::TextFormattedWrapped("These amazing people have contributed some incredible things to ImHex in the past.\nConsider opening a PR on the Git Repository to take your place among them!");
|
||||
ImGui::NewLine();
|
||||
struct Contributor {
|
||||
const char *name;
|
||||
const char *description;
|
||||
const char *link;
|
||||
bool mainContributor;
|
||||
};
|
||||
|
||||
static void drawContributorTable(const char *title, const auto &contributors) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
ImGuiExt::BeginSubWindow("Contributors", ImVec2(ImGui::GetContentRegionAvail().x, 0), ImGuiChildFlags_AutoResizeX);
|
||||
ImGuiExt::BeginSubWindow(title, ImVec2(ImGui::GetContentRegionAvail().x, 0), ImGuiChildFlags_AutoResizeX);
|
||||
ImGui::PopStyleVar();
|
||||
{
|
||||
if (ImGui::BeginTable("Contributors", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
|
||||
for (const auto &contributor : Contributors) {
|
||||
if (ImGui::BeginTable(title, 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
|
||||
for (const auto &contributor : contributors) {
|
||||
ImGui::TableNextRow();
|
||||
if (contributor.mainContributor) {
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_PlotHistogram) & 0x1FFFFFFF);
|
||||
@@ -270,10 +258,11 @@ namespace hex::plugin::builtin {
|
||||
if (ImGuiExt::Hyperlink(contributor.name))
|
||||
hex::openWebpage(contributor.link);
|
||||
|
||||
ImGui::Indent();
|
||||
ImGui::TextUnformatted(contributor.description);
|
||||
ImGui::Unindent();
|
||||
|
||||
if (contributor.description[0] != '\0') {
|
||||
ImGui::Indent();
|
||||
ImGui::TextUnformatted(contributor.description);
|
||||
ImGui::Unindent();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@@ -282,6 +271,36 @@ namespace hex::plugin::builtin {
|
||||
ImGuiExt::EndSubWindow();
|
||||
}
|
||||
|
||||
void ViewAbout::drawContributorPage() {
|
||||
constexpr static std::array Contributors = {
|
||||
Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true },
|
||||
Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/jumanji144", true },
|
||||
Contributor { "AxCut", "A ton of great pattern language improvements and help with the issue tracker", "https://github.com/paxcut", false },
|
||||
Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false },
|
||||
Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false },
|
||||
Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false },
|
||||
Contributor { "All other amazing contributors", "Being part of the community, opening issues, PRs and donating", "https://github.com/WerWolv/ImHex/graphs/contributors", false }
|
||||
};
|
||||
|
||||
constexpr static std::array Testers = {
|
||||
Contributor { "Nemoumbra", "Breaking my code literal seconds after I push it", "https://github.com/Nemoumbra", true },
|
||||
Contributor { "Berylskid", "", "https://github.com/Berylskid", false },
|
||||
Contributor { "Jan Polak", "", "https://github.com/polak-jan", false },
|
||||
Contributor { "Ken-Kaneki", "", "https://github.com/loneicewolf", false },
|
||||
Contributor { "Everybody who has reported issues", "Helping me find bugs and improve the software", "https://github.com/WerWolv/ImHex/issues", false }
|
||||
|
||||
};
|
||||
|
||||
ImGuiExt::TextFormattedWrapped("These amazing people have contributed some incredible things to ImHex in the past.\nConsider opening a PR on the Git Repository to take your place among them!");
|
||||
ImGui::NewLine();
|
||||
drawContributorTable("Contributors", Contributors);
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGuiExt::TextFormattedWrapped("All of these great people made ImHex work much much smoother.\nConsider joining our Tester team to help making ImHex better for everyone!");
|
||||
ImGui::NewLine();
|
||||
drawContributorTable("Testers", Testers);
|
||||
}
|
||||
|
||||
void ViewAbout::drawLibraryCreditsPage() {
|
||||
struct Library {
|
||||
const char *name;
|
||||
|
||||
@@ -313,8 +313,8 @@ namespace hex::plugin::builtin {
|
||||
u64 droppedBookmarkId = *static_cast<const u64*>(payload->Data);
|
||||
|
||||
// Find the correct bookmark with that id
|
||||
auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmark) {
|
||||
return bookmark.entry.id == droppedBookmarkId;
|
||||
auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmarkItem) {
|
||||
return bookmarkItem.entry.id == droppedBookmarkId;
|
||||
});
|
||||
|
||||
// Swap the two bookmarks
|
||||
@@ -412,16 +412,25 @@ namespace hex::plugin::builtin {
|
||||
u64 end = region.getEndAddress();
|
||||
|
||||
if (!locked) {
|
||||
bool updated = false;
|
||||
|
||||
ImGui::PushItemWidth(100_scaled);
|
||||
ImGuiExt::InputHexadecimal("##begin", &begin);
|
||||
if (ImGuiExt::InputHexadecimal("##begin", &begin))
|
||||
updated = true;
|
||||
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted(" - ");
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGuiExt::InputHexadecimal("##end", &end);
|
||||
|
||||
if (ImGuiExt::InputHexadecimal("##end", &end))
|
||||
updated = true;
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (end > begin)
|
||||
if (updated && end > begin) {
|
||||
region = Region(begin, end - begin + 1);
|
||||
EventHighlightingChanged::post();
|
||||
}
|
||||
} else {
|
||||
ImGuiExt::TextFormatted("0x{:02X} - 0x{:02X}", begin, end);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace hex::plugin::builtin {
|
||||
m_validBytes = 0;
|
||||
m_selectedProvider = nullptr;
|
||||
} else {
|
||||
m_validBytes = u64(region.getProvider()->getActualSize() - region.address);
|
||||
m_validBytes = u64((region.getProvider()->getBaseAddress() + region.getProvider()->getActualSize()) - region.address);
|
||||
m_startAddress = region.address;
|
||||
m_selectedProvider = region.getProvider();
|
||||
}
|
||||
|
||||
@@ -531,6 +531,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
m_sortedOccurrences.get(provider) = m_foundOccurrences.get(provider);
|
||||
m_lastSelectedOccurrence = nullptr;
|
||||
|
||||
for (const auto &occurrence : m_foundOccurrences.get(provider))
|
||||
m_occurrenceTree->insert({ occurrence.region.getStartAddress(), occurrence.region.getEndAddress() }, occurrence);
|
||||
@@ -895,6 +896,7 @@ namespace hex::plugin::builtin {
|
||||
m_foundOccurrences->clear();
|
||||
m_sortedOccurrences->clear();
|
||||
m_occurrenceTree->clear();
|
||||
m_lastSelectedOccurrence = nullptr;
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
}
|
||||
@@ -1007,7 +1009,11 @@ namespace hex::plugin::builtin {
|
||||
ImGuiExt::TextFormatted("{}", value);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
if (ImGui::GetIO().KeyCtrl) {
|
||||
if (ImGui::GetIO().KeyShift && m_lastSelectedOccurrence != nullptr) {
|
||||
for (auto start = std::min(&foundItem, m_lastSelectedOccurrence.get(provider)); start <= std::max(&foundItem, m_lastSelectedOccurrence.get(provider)); start += 1)
|
||||
start->selected = true;
|
||||
|
||||
} else if (ImGui::GetIO().KeyCtrl) {
|
||||
foundItem.selected = !foundItem.selected;
|
||||
} else {
|
||||
for (auto &occurrence : *m_sortedOccurrences)
|
||||
@@ -1015,6 +1021,8 @@ namespace hex::plugin::builtin {
|
||||
foundItem.selected = true;
|
||||
ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize());
|
||||
}
|
||||
|
||||
m_lastSelectedOccurrence = &foundItem;
|
||||
}
|
||||
drawContextMenu(foundItem, value);
|
||||
|
||||
|
||||
@@ -1050,7 +1050,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::SetClipboardText(
|
||||
callback(
|
||||
provider,
|
||||
selection->getStartAddress() + provider->getBaseAddress() + provider->getCurrentPageAddress(),
|
||||
selection->getStartAddress(),
|
||||
selection->size
|
||||
).c_str()
|
||||
);
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace hex::plugin::builtin {
|
||||
return std::nullopt;
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
if (!provider->isSavable())
|
||||
return std::nullopt;
|
||||
|
||||
offset -= provider->getBaseAddress();
|
||||
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/achievement_manager.hpp>
|
||||
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
#include <pl/core/preprocessor.hpp>
|
||||
#include <pl/core/parser.hpp>
|
||||
#include <pl/core/ast/ast_node_variable_decl.hpp>
|
||||
#include <pl/core/ast/ast_node_type_decl.hpp>
|
||||
#include <pl/core/ast/ast_node_builtin_type.hpp>
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
@@ -22,11 +20,9 @@
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
#include <popups/popup_file_chooser.hpp>
|
||||
#include <popups/popup_question.hpp>
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <chrono>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
@@ -34,6 +30,8 @@
|
||||
#include <wolv/utils/guards.hpp>
|
||||
#include <wolv/utils/lock.hpp>
|
||||
|
||||
#include <content/global_actions.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
using namespace hex::literals;
|
||||
@@ -88,6 +86,9 @@ namespace hex::plugin::builtin {
|
||||
langDef.mAutoIndentation = true;
|
||||
langDef.mPreprocChar = '#';
|
||||
|
||||
langDef.mGlobalDocComment = "/*!";
|
||||
langDef.mDocComment = "/**";
|
||||
|
||||
langDef.mName = "Pattern Language";
|
||||
|
||||
initialized = true;
|
||||
@@ -133,6 +134,9 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
static void drawVirtualFileTree(const std::vector<const ViewPatternEditor::VirtualFile*> &virtualFiles, u32 level = 0) {
|
||||
ImGui::PushID(level + 1);
|
||||
ON_SCOPE_EXIT { ImGui::PopID(); };
|
||||
|
||||
std::map<std::string, std::vector<const ViewPatternEditor::VirtualFile*>> currFolderEntries;
|
||||
for (const auto &file : virtualFiles) {
|
||||
const auto &path = file->path;
|
||||
@@ -146,8 +150,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::TreeNodeEx(currSegment.c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen);
|
||||
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()) {
|
||||
ImHexApi::Provider::add<prv::MemoryProvider>(file->data, wolv::util::toUTF8String(file->path.filename()));
|
||||
}
|
||||
|
||||
@@ -157,6 +160,7 @@ namespace hex::plugin::builtin {
|
||||
currFolderEntries[currSegment].emplace_back(file);
|
||||
}
|
||||
|
||||
int id = 1;
|
||||
for (const auto &[segment, entries] : currFolderEntries) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
@@ -167,11 +171,16 @@ namespace hex::plugin::builtin {
|
||||
ImGui::TextUnformatted(ICON_VS_FOLDER);
|
||||
}
|
||||
|
||||
ImGui::PushID(id);
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::TreeNodeEx(segment.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) {
|
||||
drawVirtualFileTree(entries, level + 1);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +237,14 @@ namespace hex::plugin::builtin {
|
||||
bool clickedMenuFind = false;
|
||||
bool clickedMenuReplace = false;
|
||||
if (ImGui::BeginPopup("##pattern_editor_context_menu")) {
|
||||
// no shortcut for this
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.import.pattern_file"_lang, nullptr, false))
|
||||
importPatternFile();
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.export.pattern_file"_lang, nullptr, false))
|
||||
exportPatternFile();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
const bool hasSelection = m_textEditor.HasSelection();
|
||||
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) {
|
||||
m_textEditor.Cut();
|
||||
@@ -298,6 +315,12 @@ namespace hex::plugin::builtin {
|
||||
m_replaceMode = true;
|
||||
openFindPopup = true;
|
||||
}
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt)
|
||||
hex::plugin::builtin::saveProject();
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_O, false) && ImGui::GetIO().KeyAlt)
|
||||
hex::plugin::builtin::openProject();
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt && ImGui::GetIO().KeyShift)
|
||||
hex::plugin::builtin::saveProjectAs();
|
||||
}
|
||||
|
||||
static std::string findWord;
|
||||
@@ -677,6 +700,8 @@ namespace hex::plugin::builtin {
|
||||
if (altCPressed)
|
||||
matchCase = !matchCase;
|
||||
findReplaceHandler->SetMatchCase(&m_textEditor,matchCase);
|
||||
position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true);
|
||||
count = findReplaceHandler->GetMatches().size();
|
||||
updateCount = true;
|
||||
requestFocusFind = true;
|
||||
}
|
||||
@@ -1158,7 +1183,7 @@ namespace hex::plugin::builtin {
|
||||
for (const auto &file : virtualFiles)
|
||||
virtualFilePointers.emplace_back(&file);
|
||||
|
||||
if (ImGui::BeginTable("Virtual File Tree", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg, size)) {
|
||||
if (ImGui::BeginTable("Virtual File Tree", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, size)) {
|
||||
ImGui::TableSetupColumn("##path", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
drawVirtualFileTree(virtualFilePointers);
|
||||
@@ -1277,7 +1302,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (!m_lastCompileError->empty()) {
|
||||
for (const auto &error : *m_lastCompileError) {
|
||||
errorMarkers[error.getLocation().line] = processMessage(error.getMessage());
|
||||
if (error.getLocation().source->source == pl::api::Source::DefaultSource)
|
||||
errorMarkers[error.getLocation().line] = processMessage(error.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1305,7 +1331,7 @@ namespace hex::plugin::builtin {
|
||||
pl::PatternLanguage runtime;
|
||||
ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);
|
||||
|
||||
auto mimeType = magic::getMIMEType(provider, true);
|
||||
auto mimeType = magic::getMIMEType(provider, 0, 100_KiB, true);
|
||||
|
||||
bool foundCorrectType = false;
|
||||
runtime.addPragma("MIME", [&mimeType, &foundCorrectType](const pl::PatternLanguage &runtime, const std::string &value) {
|
||||
@@ -1392,12 +1418,8 @@ namespace hex::plugin::builtin {
|
||||
if (!file.isValid())
|
||||
continue;
|
||||
|
||||
auto &preprocessor = runtime.getInternals().preprocessor;
|
||||
|
||||
pl::api::Source source(file.readString());
|
||||
|
||||
auto ret = preprocessor->preprocess(&runtime, &source);
|
||||
if (ret.hasErrs()) {
|
||||
auto result = runtime.preprocessString(file.readString(), pl::api::Source::DefaultSource);
|
||||
if (!result.has_value()) {
|
||||
log::warn("Failed to preprocess file {} during MIME analysis", entry.path().string());
|
||||
}
|
||||
|
||||
@@ -1542,6 +1564,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
m_sectionWindowDrawer.clear();
|
||||
m_consoleEditor.SetText("");
|
||||
m_virtualFiles->clear();
|
||||
|
||||
m_accessHistory = {};
|
||||
m_accessHistoryIndex = 0;
|
||||
@@ -1681,12 +1704,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (newProvider != nullptr)
|
||||
m_textEditor.SetText(m_sourceCode.get(newProvider));
|
||||
});
|
||||
|
||||
EventProviderClosed::subscribe(this, [this](prv::Provider *) {
|
||||
if (ImHexApi::Provider::getProviders().empty()) {
|
||||
else
|
||||
m_textEditor.SetText("");
|
||||
}
|
||||
});
|
||||
|
||||
RequestAddVirtualFile::subscribe(this, [this](const std::fs::path &path, const std::vector<u8> &data, Region region) {
|
||||
@@ -1731,39 +1750,11 @@ namespace hex::plugin::builtin {
|
||||
void ViewPatternEditor::registerMenuItems() {
|
||||
/* Import Pattern */
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.pattern" }, ICON_VS_FILE_CODE, 4050, Shortcut::None,
|
||||
[this] {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
|
||||
std::vector<std::fs::path> paths;
|
||||
|
||||
for (const auto &imhexPath : basePaths) {
|
||||
if (!wolv::io::fs::exists(imhexPath)) continue;
|
||||
|
||||
std::error_code error;
|
||||
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
|
||||
if (entry.is_regular_file() && entry.path().extension() == ".hexpat") {
|
||||
paths.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui::PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
|
||||
[this, provider](const std::fs::path &path) {
|
||||
this->loadPatternFile(path, provider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
|
||||
});
|
||||
}, ImHexApi::Provider::isValid);
|
||||
importPatternFile, ImHexApi::Provider::isValid);
|
||||
|
||||
/* Export Pattern */
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None,
|
||||
[this] {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, { {"Pattern", "hexpat"} },
|
||||
[this](const auto &path) {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
|
||||
file.writeString(wolv::util::trim(m_textEditor.GetText()));
|
||||
});
|
||||
}, [this] {
|
||||
exportPatternFile, [this] {
|
||||
return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid();
|
||||
}
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user