diff --git a/.github/workflows/build_web.yml b/.github/workflows/build_web.yml
new file mode 100644
index 000000000..3ed5f748a
--- /dev/null
+++ b/.github/workflows/build_web.yml
@@ -0,0 +1,79 @@
+name: Build for the web
+
+on:
+ push:
+ branches: ["*"]
+ pull_request:
+ workflow_dispatch:
+
+env:
+ BUILD_TYPE: Release
+
+permissions:
+ pages: write
+ id-token: write
+ actions: write
+
+jobs:
+
+ build:
+ runs-on: ubuntu-22.04
+ name: 🌍 WebAssembly
+ steps:
+ - name: 🧰 Checkout
+ uses: actions/checkout@v3
+ with:
+ submodules: recursive
+
+ - name: 📁 Restore docker /cache
+ uses: actions/cache@v3
+ with:
+ path: cache
+ key: build-web-cache-${{ secrets.CACHE_VERSION }}
+
+ - name: 🐳 Inject /cache into docker
+ uses: reproducible-containers/buildkit-cache-dance@v2.1.2
+ with:
+ cache-source: cache
+ cache-target: /cache
+
+ - name: 🛠️ Build using docker
+ run: |
+ docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out
+
+ - name: 🔨 Fix permissions
+ run: |
+ chmod -c -R +rX "out/" | while read line; do
+ echo "::warning title=Invalid file permissions automatically fixed::$line"
+ done
+
+ - name: ⬆️ Upload artifacts
+ uses: actions/upload-pages-artifact@v2
+ with:
+ path: out/
+
+ # See https://github.com/actions/cache/issues/342#issuecomment-1711054115
+ - name: 🗑️ Delete old cache
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ gh extension install actions/gh-actions-cache
+ gh actions-cache delete "build-web-cache-${{ secrets.CACHE_VERSION }}" --confirm
+
+
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ name: 📃 Deploy to GitHub Pages
+ runs-on: ubuntu-latest
+
+ if: github.ref == 'refs/heads/master'
+ needs: build
+
+ steps:
+ - name: 🌍 Deploy
+ id: deployment
+ uses: actions/deploy-pages@v2
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd95c0bec..6fd006dcd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@ option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON)
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF)
option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF)
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON)
+option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
# Basic compiler and cmake configurations
set(CMAKE_CXX_STANDARD 23)
diff --git a/README.md b/README.md
index e79407da8..892e96874 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,12 @@
+
+
+
+
+
+
## Supporting
If you like my work, please consider supporting me on GitHub Sponsors, Patreon or PayPal. Thanks a lot!
diff --git a/cmake/build_helpers.cmake b/cmake/build_helpers.cmake
index cbef0b1e5..d7785d1b7 100644
--- a/cmake/build_helpers.cmake
+++ b/cmake/build_helpers.cmake
@@ -30,6 +30,10 @@ macro(addDefines)
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-MinSizeRel)
add_compile_definitions(NDEBUG)
endif ()
+
+ if (IMHEX_STATIC_LINK_PLUGINS)
+ add_compile_definitions(IMHEX_STATIC_LINK_PLUGINS)
+ endif ()
endmacro()
function(addDefineToSource SOURCE DEFINE)
@@ -54,6 +58,8 @@ macro(detectOS)
set(PLUGINS_INSTALL_LOCATION "plugins")
enable_language(OBJC)
enable_language(OBJCXX)
+ elseif (EMSCRIPTEN)
+ add_compile_definitions(OS_WEB)
elseif (UNIX AND NOT APPLE)
add_compile_definitions(OS_LINUX)
include(GNUInstallDirs)
@@ -83,6 +89,8 @@ endmacro()
macro(configurePackingResources)
+ set(IMHEX_FORCE_LINK_PLUGINS "")
+
option (CREATE_PACKAGE "Create a package with CPack" OFF)
if (APPLE)
@@ -351,8 +359,8 @@ endmacro()
macro(setDefaultBuiltTypeIfUnset)
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
- set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Using Release build type as it was left unset" FORCE)
- set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
+ set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Using RelWithDebInfo build type as it was left unset" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "RelWithDebInfo")
endif()
endmacro()
@@ -441,13 +449,17 @@ macro(setupCompilerFlags target)
set(IMHEX_COMMON_FLAGS "-Wall -Wextra -Wpedantic -Werror")
endif()
- set(IMHEX_C_FLAGS "${IMHEX_COMMON_FLAGS} -Wno-array-bounds")
+ set(IMHEX_C_FLAGS "${IMHEX_COMMON_FLAGS} -Wno-array-bounds -Wno-deprecated-declarations")
set(IMHEX_CXX_FLAGS "-fexceptions -frtti")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(IMHEX_C_FLAGS "${IMHEX_C_FLAGS} -Wno-restrict -Wno-stringop-overread -Wno-stringop-overflow -Wno-dangling-reference")
endif()
endif()
+ if (EMSCRIPTEN)
+ set(IMHEX_C_FLAGS "${IMHEX_C_FLAGS} -pthread -Wno-dollar-in-identifier-extension -Wno-pthreads-mem-growth")
+ endif ()
+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${IMHEX_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${IMHEX_CXX_FLAGS} ${IMHEX_C_FLAGS}")
set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} ${IMHEX_COMMON_FLAGS}")
@@ -489,6 +501,8 @@ macro(addBundledLibraries)
set(XDGPP_INCLUDE_DIRS "${EXTERN_LIBS_FOLDER}/xdgpp")
set(FPHSA_NAME_MISMATCHED ON CACHE BOOL "")
+ find_package(PkgConfig REQUIRED)
+
if(NOT USE_SYSTEM_FMT)
add_subdirectory(${EXTERN_LIBS_FOLDER}/fmt EXCLUDE_FROM_ALL)
set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON)
@@ -504,13 +518,20 @@ macro(addBundledLibraries)
set(NFD_PORTAL ON CACHE BOOL "Use GTK for Linux file dialogs" FORCE)
endif ()
- if (NOT USE_SYSTEM_NFD)
- add_subdirectory(${EXTERN_LIBS_FOLDER}/nativefiledialog EXCLUDE_FROM_ALL)
- set_target_properties(nfd PROPERTIES POSITION_INDEPENDENT_CODE ON)
- set(NFD_LIBRARIES nfd)
- else()
- find_package(nfd)
- set(NFD_LIBRARIES nfd)
+ if (NOT EMSCRIPTEN)
+ # curl
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(LIBCURL REQUIRED IMPORTED_TARGET libcurl>=7.60.0)
+
+ # nfd
+ if (NOT USE_SYSTEM_NFD)
+ add_subdirectory(${EXTERN_LIBS_FOLDER}/nativefiledialog EXCLUDE_FROM_ALL)
+ set_target_properties(nfd PROPERTIES POSITION_INDEPENDENT_CODE ON)
+ set(NFD_LIBRARIES nfd)
+ else()
+ find_package(nfd)
+ set(NFD_LIBRARIES nfd)
+ endif()
endif()
if(NOT USE_SYSTEM_NLOHMANN_JSON)
@@ -521,9 +542,6 @@ macro(addBundledLibraries)
set(NLOHMANN_JSON_LIBRARIES nlohmann_json::nlohmann_json)
endif()
- find_package(PkgConfig REQUIRED)
- pkg_check_modules(LIBCURL REQUIRED IMPORTED_TARGET libcurl>=7.60.0)
-
if (NOT USE_SYSTEM_LLVM)
add_subdirectory(${EXTERN_LIBS_FOLDER}/llvm-demangle EXCLUDE_FROM_ALL)
set_target_properties(LLVMDemangle PROPERTIES POSITION_INDEPENDENT_CODE ON)
diff --git a/cmake/modules/ImHexPlugin.cmake b/cmake/modules/ImHexPlugin.cmake
index cfec63875..4e4051f2d 100644
--- a/cmake/modules/ImHexPlugin.cmake
+++ b/cmake/modules/ImHexPlugin.cmake
@@ -4,20 +4,32 @@ macro(add_imhex_plugin)
set(oneValueArgs NAME)
set(multiValueArgs SOURCES INCLUDES LIBRARIES)
cmake_parse_arguments(IMHEX_PLUGIN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
+
+ if (IMHEX_STATIC_LINK_PLUGINS)
+ set(IMHEX_PLUGIN_LIBRARY_TYPE STATIC)
+
+ target_link_libraries(libimhex PUBLIC ${IMHEX_PLUGIN_NAME})
+
+ configure_file(${CMAKE_SOURCE_DIR}/dist/web/plugin-bundle.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp @ONLY)
+ target_sources(main PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp)
+ else()
+ set(IMHEX_PLUGIN_LIBRARY_TYPE SHARED)
+ endif()
+
# Define new project for plugin
project(${IMHEX_PLUGIN_NAME})
# Create a new shared library for the plugin source code
- add_library(${IMHEX_PLUGIN_NAME} SHARED ${IMHEX_PLUGIN_SOURCES})
+ add_library(${IMHEX_PLUGIN_NAME} ${IMHEX_PLUGIN_LIBRARY_TYPE} ${IMHEX_PLUGIN_SOURCES})
# Add include directories and link libraries
- target_include_directories(${IMHEX_PLUGIN_NAME} PRIVATE ${IMHEX_PLUGIN_INCLUDES})
+ target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES})
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} ${IMHEX_PLUGIN_LIBRARIES})
# Add IMHEX_PROJECT_NAME and IMHEX_VERSION define
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}")
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_VERSION="${IMHEX_VERSION_STRING}")
+ target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PLUGIN_NAME=${IMHEX_PLUGIN_NAME})
# Enable required compiler flags
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
diff --git a/dist/web/Dockerfile b/dist/web/Dockerfile
new file mode 100644
index 000000000..bf772f8cf
--- /dev/null
+++ b/dist/web/Dockerfile
@@ -0,0 +1,76 @@
+FROM emscripten/emsdk:latest as build
+
+# Used to invalidate layer cache but not mount cache
+# See https://github.com/moby/moby/issues/41715#issuecomment-733976493
+ARG UNIQUEKEY 1
+
+RUN apt update
+RUN apt install -y git ccache autoconf automake libtool cmake pkg-config
+
+# Install vcpkg
+# Note: we are using my fork of the repository with a libmagic patch
+RUN git clone https://github.com/iTrooz/vcpkg --branch libmagic /vcpkg
+RUN /vcpkg/bootstrap-vcpkg.sh
+
+# Patch vcpkg build instructions to add -pthread
+RUN <> /emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
+EOF
+
+# Install dependencies with vcpkg
+RUN /vcpkg/vcpkg install --triplet=wasm32-emscripten libmagic
+RUN /vcpkg/vcpkg install --triplet=wasm32-emscripten freetype
+RUN /vcpkg/vcpkg install --triplet=wasm32-emscripten josuttis-jthread
+RUN /vcpkg/vcpkg install --triplet=wasm32-emscripten mbedtls
+
+# Build ImHex
+ARG JOBS=4
+ENV CCACHE_DIR /cache/ccache
+
+RUN mkdir /build
+WORKDIR /build
+RUN --mount=type=cache,target=/cache \
+ --mount=type=bind,source=.,target=/imhex <
+
+extern "C" void forceLinkPlugin_@IMHEX_PLUGIN_NAME@();
+
+struct StaticLoad {
+ StaticLoad() {
+ forceLinkPlugin_@IMHEX_PLUGIN_NAME@();
+ }
+};
+
+static StaticLoad staticLoad;
\ No newline at end of file
diff --git a/dist/web/serve.py b/dist/web/serve.py
new file mode 100644
index 000000000..e0224f9a1
--- /dev/null
+++ b/dist/web/serve.py
@@ -0,0 +1,14 @@
+import http.server
+import os
+
+class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
+
+ def end_headers(self):
+ self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
+ self.send_header("Cross-Origin-Opener-Policy", "same-origin")
+ http.server.SimpleHTTPRequestHandler.end_headers(self)
+
+if __name__ == '__main__':
+ os.chdir(".")
+ httpd = http.server.HTTPServer(("", 9090), MyHttpRequestHandler)
+ httpd.serve_forever()
\ No newline at end of file
diff --git a/dist/web/source/enable-threads.js b/dist/web/source/enable-threads.js
new file mode 100644
index 000000000..01d7d084c
--- /dev/null
+++ b/dist/web/source/enable-threads.js
@@ -0,0 +1,75 @@
+// NOTE: This file creates a service worker that cross-origin-isolates the page (read more here: https://web.dev/coop-coep/) which allows us to use wasm threads.
+// Normally you would set the COOP and COEP headers on the server to do this, but Github Pages doesn't allow this, so this is a hack to do that.
+
+/* Edited version of: coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */
+// From here: https://github.com/gzuidhof/coi-serviceworker
+if(typeof window === 'undefined') {
+ self.addEventListener("install", () => self.skipWaiting());
+ self.addEventListener("activate", e => e.waitUntil(self.clients.claim()));
+
+ async function handleFetch(request) {
+ if(request.cache === "only-if-cached" && request.mode !== "same-origin") {
+ return;
+ }
+
+ if(request.mode === "no-cors") { // We need to set `credentials` to "omit" for no-cors requests, per this comment: https://bugs.chromium.org/p/chromium/issues/detail?id=1309901#c7
+ request = new Request(request.url, {
+ cache: request.cache,
+ credentials: "omit",
+ headers: request.headers,
+ integrity: request.integrity,
+ destination: request.destination,
+ keepalive: request.keepalive,
+ method: request.method,
+ mode: request.mode,
+ redirect: request.redirect,
+ referrer: request.referrer,
+ referrerPolicy: request.referrerPolicy,
+ signal: request.signal,
+ });
+ }
+
+ let r = await fetch(request).catch(e => console.error(e));
+
+ if(r.status === 0) {
+ return r;
+ }
+
+ const headers = new Headers(r.headers);
+ headers.set("Cross-Origin-Embedder-Policy", "require-corp"); // or: require-corp
+ headers.set("Cross-Origin-Opener-Policy", "same-origin");
+
+ return new Response(r.body, { status: r.status, statusText: r.statusText, headers });
+ }
+
+ self.addEventListener("fetch", function(e) {
+ e.respondWith(handleFetch(e.request)); // respondWith must be executed synchonously (but can be passed a Promise)
+ });
+
+} else {
+ (async function() {
+ if(window.crossOriginIsolated !== false) return;
+
+ let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error("COOP/COEP Service Worker failed to register:", e));
+ if(registration) {
+ console.log("COOP/COEP Service Worker registered", registration.scope);
+
+ registration.addEventListener("updatefound", () => {
+ console.log("Reloading page to make use of updated COOP/COEP Service Worker.");
+ window.location.reload();
+ });
+
+ // If the registration is active, but it's not controlling the page
+ if(registration.active && !navigator.serviceWorker.controller) {
+ console.log("Reloading page to make use of COOP/COEP Service Worker.");
+ window.location.reload();
+ }
+ }
+ })();
+}
+
+// Code to deregister:
+// let registrations = await navigator.serviceWorker.getRegistrations();
+// for(let registration of registrations) {
+// await registration.unregister();
+// }
\ No newline at end of file
diff --git a/dist/web/source/favicon.ico b/dist/web/source/favicon.ico
new file mode 100644
index 000000000..a72e2ab21
Binary files /dev/null and b/dist/web/source/favicon.ico differ
diff --git a/dist/web/source/index.html b/dist/web/source/index.html
new file mode 100644
index 000000000..d33a2459c
--- /dev/null
+++ b/dist/web/source/index.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+ ImHex - Hex Editor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ImHex Web
+
+
+
+
+ ImHex is loading...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dist/web/source/style.css b/dist/web/source/style.css
new file mode 100644
index 000000000..4b9c77497
--- /dev/null
+++ b/dist/web/source/style.css
@@ -0,0 +1,28 @@
+html, body {
+ height: 100%;
+ margin: 0px;
+ user-select: none;
+}
+
+body {
+ display: flex;
+ align-items: center;
+ background-color: #121212;
+ overflow: hidden;
+}
+
+.emscripten {
+ padding-right: 0;
+ margin-left: auto;
+ margin-right: auto;
+ display: block;
+ border: 0px none;
+}
+
+#loading_text {
+ color: #F0F0F0;
+ font-size: 30px;
+ font-family: monospace;
+ width: 100%;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/dist/web/source/wasm-config.js b/dist/web/source/wasm-config.js
new file mode 100644
index 000000000..45995aa59
--- /dev/null
+++ b/dist/web/source/wasm-config.js
@@ -0,0 +1,68 @@
+function glfwSetCursorCustom(wnd, shape) {
+ let body = document.getElementsByTagName("body")[0]
+ switch (shape) {
+ case 0x00036001: // GLFW_ARROW_CURSOR
+ body.style.cursor = "default";
+ break;
+ case 0x00036002: // GLFW_IBEAM_CURSOR
+ body.style.cursor = "text";
+ break;
+ case 0x00036003: // GLFW_CROSSHAIR_CURSOR
+ body.style.cursor = "crosshair";
+ break;
+ case 0x00036004: // GLFW_HAND_CURSOR
+ body.style.cursor = "pointer";
+ break;
+ case 0x00036005: // GLFW_HRESIZE_CURSOR
+ body.style.cursor = "ew-resize";
+ break;
+ case 0x00036006: // GLFW_VRESIZE_CURSOR
+ body.style.cursor = "ns-resize";
+ break;
+ default:
+ body.style.cursor = "default";
+ break;
+ }
+
+}
+
+function glfwCreateStandardCursorCustom(shape) {
+ return shape
+}
+
+var Module = {
+ preRun: [],
+ postRun: [],
+ onRuntimeInitialized: function() {
+ // Triggered when the wasm module is loaded and ready to use.
+ document.getElementById("loading_text").style.display = "none"
+ document.getElementById("canvas").style.display = "initial"
+ },
+ print: (function() { })(),
+ printErr: function(text) { },
+ canvas: (function() {
+ let canvas = document.getElementById('canvas');
+ // As a default initial behavior, pop up an alert when webgl context is lost. To make your
+ // application robust, you may want to override this behavior before shipping!
+ // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+
+ return canvas;
+ })(),
+ setStatus: function(text) { },
+ totalDependencies: 0,
+ monitorRunDependencies: function(left) { },
+ instantiateWasm: function(imports, successCallback) {
+ imports.env.glfwSetCursor = glfwSetCursorCustom
+ imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom
+ instantiateAsync(wasmBinary, wasmBinaryFile, imports, (result) => successCallback(result.instance, result.module));
+ }
+};
+
+
+window.addEventListener('resize', js_resizeCanvas, false);
+function js_resizeCanvas() {
+ let canvas = document.getElementById('canvas');
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+}
\ No newline at end of file
diff --git a/lib/external/fmt b/lib/external/fmt
index f5e54359d..f91828936 160000
--- a/lib/external/fmt
+++ b/lib/external/fmt
@@ -1 +1 @@
-Subproject commit f5e54359df4c26b6230fc61d38aa294581393084
+Subproject commit f9182893631e4a93e8a2d947b726f95a5367fce9
diff --git a/lib/external/imgui/CMakeLists.txt b/lib/external/imgui/CMakeLists.txt
index f81b03ccc..6502bdccc 100644
--- a/lib/external/imgui/CMakeLists.txt
+++ b/lib/external/imgui/CMakeLists.txt
@@ -32,9 +32,8 @@ add_library(imgui OBJECT
)
target_compile_definitions(imgui PUBLIC IMGUI_IMPL_OPENGL_LOADER_GLAD)
-target_compile_options(imgui PRIVATE -Wno-stringop-overflow)
-
target_compile_definitions(imgui PUBLIC IMGUI_USER_CONFIG="imgui_config.h")
+target_compile_options(imgui PRIVATE -Wno-unknown-warning-option)
target_include_directories(imgui PUBLIC include ${FREETYPE_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS} ${OpenGL_INCLUDE_DIRS})
target_link_directories(imgui PUBLIC ${GLFW_LIBRARY_DIRS} ${OpenGL_LIBRARY_DIRS})
diff --git a/lib/external/imgui/source/imgui_impl_glfw.cpp b/lib/external/imgui/source/imgui_impl_glfw.cpp
index 4be45aa8d..e2728c7a1 100644
--- a/lib/external/imgui/source/imgui_impl_glfw.cpp
+++ b/lib/external/imgui/source/imgui_impl_glfw.cpp
@@ -126,6 +126,8 @@
#define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName()
#define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError()
+#undef GLFW_HAS_VULKAN
+
// GLFW data
enum GlfwClientApi
{
diff --git a/lib/external/libromfs b/lib/external/libromfs
index 31b331c02..03365d8c5 160000
--- a/lib/external/libromfs
+++ b/lib/external/libromfs
@@ -1 +1 @@
-Subproject commit 31b331c02af7464e436f38e7aed27646e097b1bd
+Subproject commit 03365d8c5a43baa22cc6bbd5725db15a3038d035
diff --git a/lib/external/libwolv b/lib/external/libwolv
index 094d87ca3..f5f081c28 160000
--- a/lib/external/libwolv
+++ b/lib/external/libwolv
@@ -1 +1 @@
-Subproject commit 094d87ca30fb3f03485fac1c8c11f53baad52210
+Subproject commit f5f081c28efb9c06e14fa3b4a6d85866d75f7350
diff --git a/lib/external/pattern_language b/lib/external/pattern_language
index ff92cf631..a3574fe6c 160000
--- a/lib/external/pattern_language
+++ b/lib/external/pattern_language
@@ -1 +1 @@
-Subproject commit ff92cf631a7483faee461947538479919e736114
+Subproject commit a3574fe6c2dcca40cac5ac61dc95a063b6799249
diff --git a/lib/external/yara/CMakeLists.txt b/lib/external/yara/CMakeLists.txt
index 11b19423b..1a61f9622 100644
--- a/lib/external/yara/CMakeLists.txt
+++ b/lib/external/yara/CMakeLists.txt
@@ -116,11 +116,13 @@ target_compile_definitions(libyara PRIVATE
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(libyara PRIVATE -Wno-shift-count-overflow -Wno-stringop-overflow)
+elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ target_compile_options(libyara PRIVATE -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare)
endif ()
target_include_directories(
libyara
- PUBLIC $ $ $
+ PUBLIC $ $
PRIVATE ${LIBYARA_SOURCE_PATH} ${MBEDTLS_INCLUDE_DIR}
)
diff --git a/lib/libimhex/CMakeLists.txt b/lib/libimhex/CMakeLists.txt
index 117239a36..49592b2c7 100644
--- a/lib/libimhex/CMakeLists.txt
+++ b/lib/libimhex/CMakeLists.txt
@@ -27,6 +27,8 @@ set(LIBIMHEX_SOURCES
source/helpers/magic.cpp
source/helpers/crypto.cpp
source/helpers/http_requests.cpp
+ source/helpers/http_requests_native.cpp
+ source/helpers/http_requests_emscripten.cpp
source/helpers/opengl.cpp
source/helpers/patches.cpp
source/helpers/encoding_file.cpp
@@ -58,7 +60,12 @@ endif ()
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
-add_library(libimhex SHARED ${LIBIMHEX_SOURCES})
+if (IMHEX_STATIC_LINK_PLUGINS)
+ add_library(libimhex STATIC ${LIBIMHEX_SOURCES})
+else()
+ add_library(libimhex SHARED ${LIBIMHEX_SOURCES})
+endif()
+
set_target_properties(libimhex PROPERTIES POSITION_INDEPENDENT_CODE ON)
setupCompilerFlags(libimhex)
@@ -68,6 +75,16 @@ generate_export_header(libimhex)
target_include_directories(libimhex PUBLIC include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${CAPSTONE_INCLUDE_DIRS} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${YARA_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
target_link_directories(libimhex PUBLIC ${MBEDTLS_LIBRARY_DIR} ${CAPSTONE_LIBRARY_DIRS} ${MAGIC_LIBRARY_DIRS})
+if (EMSCRIPTEN)
+ find_path(JOSUTTIS_JTHREAD_INCLUDE_DIRS "condition_variable_any2.hpp")
+ target_include_directories(libimhex PRIVATE ${JOSUTTIS_JTHREAD_INCLUDE_DIRS})
+else()
+ # curl is only used in non-emscripten builds
+ target_include_directories(libimhex PUBLIC ${CURL_INCLUDE_DIRS})
+ target_link_libraries(libimhex PUBLIC ${LIBCURL_LIBRARIES})
+endif()
+
+
if (WIN32)
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
@@ -77,7 +94,7 @@ elseif (APPLE)
endif ()
target_link_libraries(libimhex PRIVATE ${FMT_LIBRARIES})
-target_link_libraries(libimhex PUBLIC dl imgui ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} plcli libpl libpl-gen ${MINIAUDIO_LIBRARIES} libwolv-utils libwolv-io libwolv-hash libwolv-net libwolv-containers)
+target_link_libraries(libimhex PUBLIC dl imgui ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} plcli libpl libpl-gen ${MINIAUDIO_LIBRARIES} libwolv-utils libwolv-io libwolv-hash libwolv-net libwolv-containers)
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp
index c5b06c965..b676f82bc 100644
--- a/lib/libimhex/include/hex/api/content_registry.hpp
+++ b/lib/libimhex/include/hex/api/content_registry.hpp
@@ -16,6 +16,10 @@
#include
#include
+#if defined(OS_WEB)
+#include
+#endif
+
#include
using ImGuiDataType = int;
diff --git a/lib/libimhex/include/hex/api/plugin_manager.hpp b/lib/libimhex/include/hex/api/plugin_manager.hpp
index e15f1adf5..f3c75b265 100644
--- a/lib/libimhex/include/hex/api/plugin_manager.hpp
+++ b/lib/libimhex/include/hex/api/plugin_manager.hpp
@@ -24,9 +24,31 @@ namespace hex {
std::function&)> callback;
};
+ struct PluginFunctions {
+ using InitializePluginFunc = void (*)();
+ using GetPluginNameFunc = const char *(*)();
+ using GetPluginAuthorFunc = const char *(*)();
+ using GetPluginDescriptionFunc = const char *(*)();
+ using GetCompatibleVersionFunc = const char *(*)();
+ using SetImGuiContextFunc = void (*)(ImGuiContext *);
+ using IsBuiltinPluginFunc = bool (*)();
+ using GetSubCommandsFunc = void* (*)();
+
+ InitializePluginFunc initializePluginFunction = nullptr;
+ GetPluginNameFunc getPluginNameFunction = nullptr;
+ GetPluginAuthorFunc getPluginAuthorFunction = nullptr;
+ GetPluginDescriptionFunc getPluginDescriptionFunction = nullptr;
+ GetCompatibleVersionFunc getCompatibleVersionFunction = nullptr;
+ SetImGuiContextFunc setImGuiContextFunction = nullptr;
+ IsBuiltinPluginFunc isBuiltinPluginFunction = nullptr;
+ GetSubCommandsFunc getSubCommandsFunction = nullptr;
+ };
+
class Plugin {
public:
explicit Plugin(const std::fs::path &path);
+ explicit Plugin(PluginFunctions functions);
+
Plugin(const Plugin &) = delete;
Plugin(Plugin &&other) noexcept;
~Plugin();
@@ -46,15 +68,6 @@ namespace hex {
[[nodiscard]] std::span getSubCommands() const;
private:
- using InitializePluginFunc = void (*)();
- using GetPluginNameFunc = const char *(*)();
- using GetPluginAuthorFunc = const char *(*)();
- using GetPluginDescriptionFunc = const char *(*)();
- using GetCompatibleVersionFunc = const char *(*)();
- using SetImGuiContextFunc = void (*)(ImGuiContext *);
- using IsBuiltinPluginFunc = bool (*)();
- using GetSubCommandsFunc = void* (*)();
-
#if defined(OS_WINDOWS)
HMODULE m_handle = nullptr;
#else
@@ -64,14 +77,7 @@ namespace hex {
mutable bool m_initialized = false;
- InitializePluginFunc m_initializePluginFunction = nullptr;
- GetPluginNameFunc m_getPluginNameFunction = nullptr;
- GetPluginAuthorFunc m_getPluginAuthorFunction = nullptr;
- GetPluginDescriptionFunc m_getPluginDescriptionFunction = nullptr;
- GetCompatibleVersionFunc m_getCompatibleVersionFunction = nullptr;
- SetImGuiContextFunc m_setImGuiContextFunction = nullptr;
- IsBuiltinPluginFunc m_isBuiltinPluginFunction = nullptr;
- GetSubCommandsFunc m_getSubCommandsFunction = nullptr;
+ PluginFunctions m_functions = {};
template
[[nodiscard]] auto getPluginFunction(const std::string &symbol) {
@@ -90,7 +96,10 @@ namespace hex {
static void unload();
static void reload();
- static const std::vector &getPlugins();
+ static void addPlugin(PluginFunctions functions);
+
+ static std::vector &getPlugins();
+ static std::vector &getPluginPaths();
};
}
\ No newline at end of file
diff --git a/lib/libimhex/include/hex/api/task.hpp b/lib/libimhex/include/hex/api/task.hpp
index a5fc2eca5..c3ecd9973 100644
--- a/lib/libimhex/include/hex/api/task.hpp
+++ b/lib/libimhex/include/hex/api/task.hpp
@@ -12,6 +12,10 @@
#include
#include
+#if defined(OS_WEB)
+#include
+#endif
+
namespace hex {
class TaskHolder;
diff --git a/lib/libimhex/include/hex/helpers/fs.hpp b/lib/libimhex/include/hex/helpers/fs.hpp
index 8a04454ef..070297731 100644
--- a/lib/libimhex/include/hex/helpers/fs.hpp
+++ b/lib/libimhex/include/hex/helpers/fs.hpp
@@ -8,8 +8,6 @@
#include
#include
-#include
-
#include
namespace hex::fs {
@@ -20,8 +18,15 @@ namespace hex::fs {
Folder
};
+ struct ItemFilter {
+ // Human-friendly name
+ std::string name;
+ // Extensions that constitute this filter
+ std::string spec;
+ };
+
void setFileBrowserErrorCallback(const std::function &callback);
- bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath = {}, bool multiple = false);
+ bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath = {}, bool multiple = false);
void openFileExternal(const std::fs::path &filePath);
void openFolderExternal(const std::fs::path &dirPath);
@@ -53,7 +58,7 @@ namespace hex::fs {
std::vector getDefaultPaths(ImHexPath path, bool listNonExisting = false);
- // temporarily expose these for the migration function
+ // Temporarily expose these for the migration function
std::vector getDataPaths();
std::vector appendPath(std::vector paths, const std::fs::path &folder);
}
\ No newline at end of file
diff --git a/lib/libimhex/include/hex/helpers/http_requests.hpp b/lib/libimhex/include/hex/helpers/http_requests.hpp
index 69b1a4293..609d17341 100644
--- a/lib/libimhex/include/hex/helpers/http_requests.hpp
+++ b/lib/libimhex/include/hex/helpers/http_requests.hpp
@@ -7,8 +7,6 @@
#include
#include
-#include
-
#include
#include
@@ -19,6 +17,14 @@
#include
+#if defined(OS_WEB)
+ #include
+
+ using curl_off_t = long;
+#else
+ #include
+#endif
+
namespace hex {
class HttpRequest {
@@ -67,27 +73,9 @@ namespace hex {
HttpRequest(const HttpRequest&) = delete;
HttpRequest& operator=(const HttpRequest&) = delete;
- HttpRequest(HttpRequest &&other) noexcept {
- this->m_curl = other.m_curl;
- other.m_curl = nullptr;
+ HttpRequest(HttpRequest &&other) noexcept;
- this->m_method = std::move(other.m_method);
- this->m_url = std::move(other.m_url);
- this->m_headers = std::move(other.m_headers);
- this->m_body = std::move(other.m_body);
- }
-
- HttpRequest& operator=(HttpRequest &&other) noexcept {
- this->m_curl = other.m_curl;
- other.m_curl = nullptr;
-
- this->m_method = std::move(other.m_method);
- this->m_url = std::move(other.m_url);
- this->m_headers = std::move(other.m_headers);
- this->m_body = std::move(other.m_body);
-
- return *this;
- }
+ HttpRequest& operator=(HttpRequest &&other) noexcept;
static void setProxy(std::string proxy);
@@ -120,173 +108,28 @@ namespace hex {
}
template
- std::future> downloadFile(const std::fs::path &path) {
- return std::async(std::launch::async, [this, path] {
- std::vector response;
+ std::future> downloadFile(const std::fs::path &path);
- wolv::io::File file(path, wolv::io::File::Mode::Create);
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &file);
-
- return this->executeImpl(response);
- });
- }
-
- std::future>> downloadFile() {
- return std::async(std::launch::async, [this] {
- std::vector response;
-
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &response);
-
- return this->executeImpl>(response);
- });
- }
+ std::future>> downloadFile();
template
- std::future> uploadFile(const std::fs::path &path, const std::string &mimeName = "filename") {
- return std::async(std::launch::async, [this, path, mimeName]{
- auto fileName = wolv::util::toUTF8String(path.filename());
-
- curl_mime *mime = curl_mime_init(this->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 file = static_cast(arg);
-
- return fread(buffer, size, nitems, file);
- },
- [](void *arg, curl_off_t offset, int origin) -> int {
- auto file = static_cast(arg);
-
- if (fseek(file, offset, origin) != 0)
- return CURL_SEEKFUNC_CANTSEEK;
- else
- return CURL_SEEKFUNC_OK;
- },
- [](void *arg) {
- auto file = static_cast(arg);
-
- fclose(file);
- },
- file.getHandle());
- curl_mime_filename(part, fileName.c_str());
- curl_mime_name(part, mimeName.c_str());
-
- curl_easy_setopt(this->m_curl, CURLOPT_MIMEPOST, mime);
-
- std::vector responseData;
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
-
- return this->executeImpl(responseData);
- });
- }
- template
- std::future> uploadFile(std::vector data, const std::string &mimeName = "filename", const std::fs::path &fileName = "data.bin") {
- return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
- curl_mime *mime = curl_mime_init(this->m_curl);
- curl_mimepart *part = curl_mime_addpart(mime);
-
- curl_mime_data(part, reinterpret_cast(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(this->m_curl, CURLOPT_MIMEPOST, mime);
-
- std::vector responseData;
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
-
- return this->executeImpl(responseData);
- });
- }
+ std::future> uploadFile(const std::fs::path &path, const std::string &mimeName = "filename");
template
- std::future> execute() {
- return std::async(std::launch::async, [this] {
+ std::future> uploadFile(std::vector data, const std::string &mimeName = "filename", const std::fs::path &fileName = "data.bin");
- std::vector responseData;
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
- curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
+ template
+ std::future> execute();
- return this->executeImpl(responseData);
- });
- }
+ std::string urlEncode(const std::string &input);
- std::string urlEncode(const std::string &input) {
- auto escapedString = curl_easy_escape(this->m_curl, input.c_str(), std::strlen(input.c_str()));
-
- if (escapedString != nullptr) {
- std::string output = escapedString;
- curl_free(escapedString);
-
- return output;
- }
-
- return {};
- }
-
- std::string urlDecode(const std::string &input) {
- auto unescapedString = curl_easy_unescape(this->m_curl, input.c_str(), std::strlen(input.c_str()), nullptr);
-
- if (unescapedString != nullptr) {
- std::string output = unescapedString;
- curl_free(unescapedString);
-
- return output;
- }
-
- return {};
- }
+ std::string urlDecode(const std::string &input);
protected:
void setDefaultConfig();
template
- Result executeImpl(std::vector &data) {
- curl_easy_setopt(this->m_curl, CURLOPT_URL, this->m_url.c_str());
- curl_easy_setopt(this->m_curl, CURLOPT_CUSTOMREQUEST, this->m_method.c_str());
-
- setDefaultConfig();
-
- if (!this->m_body.empty()) {
- curl_easy_setopt(this->m_curl, CURLOPT_POSTFIELDS, this->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] : this->m_headers) {
- std::string header = hex::format("{}: {}", key, value);
- headers = curl_slist_append(headers, header.c_str());
- }
- curl_easy_setopt(this->m_curl, CURLOPT_HTTPHEADER, headers);
-
- {
- std::scoped_lock lock(this->m_transmissionMutex);
-
- auto result = curl_easy_perform(this->m_curl);
- if (result != CURLE_OK){
- char *url = nullptr;
- curl_easy_getinfo(this->m_curl, CURLINFO_EFFECTIVE_URL, &url);
- log::error("Http request '{0} {1}' failed with error {2}: '{3}'", this->m_method, url, u32(result), curl_easy_strerror(result));
- checkProxyErrors();
-
- return { };
- }
- }
-
- long statusCode = 0;
- curl_easy_getinfo(this->m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
-
- return Result(statusCode, { data.begin(), data.end() });
- }
+ Result executeImpl(std::vector &data);
static size_t writeToVector(void *contents, size_t size, size_t nmemb, void *userdata);
static size_t writeToFile(void *contents, size_t size, size_t nmemb, void *userdata);
@@ -296,18 +139,29 @@ namespace hex {
static void checkProxyErrors();
private:
+ #if defined(OS_WEB)
+ emscripten_fetch_attr_t m_attr;
+ #else
CURL *m_curl;
+ #endif
std::mutex m_transmissionMutex;
std::string m_method;
std::string m_url;
std::string m_body;
+ std::promise> m_promise;
std::map m_headers;
u32 m_timeout = 1000;
std::atomic m_progress = 0.0F;
std::atomic m_canceled = false;
};
+}
-}
\ No newline at end of file
+
+#if defined(OS_WEB)
+#include
+#else
+#include
+#endif
diff --git a/lib/libimhex/include/hex/helpers/http_requests_emscripten.hpp b/lib/libimhex/include/hex/helpers/http_requests_emscripten.hpp
new file mode 100644
index 000000000..f5a094ed9
--- /dev/null
+++ b/lib/libimhex/include/hex/helpers/http_requests_emscripten.hpp
@@ -0,0 +1,72 @@
+#pragma once
+
+#include
+
+#include
+
+namespace hex {
+ template
+ std::future> HttpRequest::downloadFile(const std::fs::path &path) {
+ return std::async(std::launch::async, [this, path] {
+ std::vector response;
+
+ // Execute the request
+ auto result = this->executeImpl(response);
+
+ // Write the result to the file
+ wolv::io::File file(path, wolv::io::File::Mode::Create);
+ file.writeBuffer(reinterpret_cast(result.getData().data()), result.getData().size());
+
+ return result;
+ });
+ }
+
+ template
+ std::future> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
+ hex::unused(path, mimeName);
+ throw std::logic_error("Not implemented");
+ }
+
+ template
+ std::future> HttpRequest::uploadFile(std::vector data, const std::string &mimeName, const std::fs::path &fileName) {
+ hex::unused(data, mimeName, fileName);
+ throw std::logic_error("Not implemented");
+ }
+
+ template
+ std::future> HttpRequest::execute() {
+ return std::async(std::launch::async, [this] {
+ std::vector responseData;
+
+ return this->executeImpl(responseData);
+ });
+ }
+
+ template
+ HttpRequest::Result HttpRequest::executeImpl(std::vector &data) {
+ strcpy(this->m_attr.requestMethod, this->m_method.c_str());
+ this->m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
+
+ if (!this->m_body.empty()) {
+ this->m_attr.requestData = this->m_body.c_str();
+ this->m_attr.requestDataSize = this->m_body.size();
+ }
+
+ std::vector headers;
+ for (auto it = this->m_headers.begin(); it != this->m_headers.end(); it++) {
+ headers.push_back(it->first.c_str());
+ headers.push_back(it->second.c_str());
+ }
+ headers.push_back(nullptr);
+ this->m_attr.requestHeaders = headers.data();
+
+ // Send request
+ emscripten_fetch_t* fetch = emscripten_fetch(&this->m_attr, this->m_url.c_str());
+
+ data.resize(fetch->numBytes);
+ std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
+
+ return Result(fetch->status, { data.begin(), data.end() });
+ }
+
+}
\ No newline at end of file
diff --git a/lib/libimhex/include/hex/helpers/http_requests_native.hpp b/lib/libimhex/include/hex/helpers/http_requests_native.hpp
new file mode 100644
index 000000000..7b5226940
--- /dev/null
+++ b/lib/libimhex/include/hex/helpers/http_requests_native.hpp
@@ -0,0 +1,140 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+namespace hex {
+
+ template
+ std::future> HttpRequest::downloadFile(const std::fs::path &path) {
+ return std::async(std::launch::async, [this, path] {
+ std::vector response;
+
+ wolv::io::File file(path, wolv::io::File::Mode::Create);
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &file);
+
+ return this->executeImpl(response);
+ });
+ }
+
+ template
+ std::future> 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(this->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 file = static_cast(arg);
+
+ return fread(buffer, size, nitems, file);
+ },
+ [](void *arg, curl_off_t offset, int origin) -> int {
+ auto file = static_cast(arg);
+
+ if (fseek(file, offset, origin) != 0)
+ return CURL_SEEKFUNC_CANTSEEK;
+ else
+ return CURL_SEEKFUNC_OK;
+ },
+ [](void *arg) {
+ auto file = static_cast(arg);
+
+ fclose(file);
+ },
+ file.getHandle());
+ curl_mime_filename(part, fileName.c_str());
+ curl_mime_name(part, mimeName.c_str());
+
+ curl_easy_setopt(this->m_curl, CURLOPT_MIMEPOST, mime);
+
+ std::vector responseData;
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
+
+ return this->executeImpl(responseData);
+ });
+ }
+
+ template
+ std::future> HttpRequest::uploadFile(std::vector 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(this->m_curl);
+ curl_mimepart *part = curl_mime_addpart(mime);
+
+ curl_mime_data(part, reinterpret_cast(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(this->m_curl, CURLOPT_MIMEPOST, mime);
+
+ std::vector responseData;
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
+
+ return this->executeImpl(responseData);
+ });
+ }
+
+ template
+ std::future> HttpRequest::execute() {
+ return std::async(std::launch::async, [this] {
+
+ std::vector responseData;
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &responseData);
+
+ return this->executeImpl(responseData);
+ });
+ }
+
+ template
+ HttpRequest::Result HttpRequest::executeImpl(std::vector &data) {
+ curl_easy_setopt(this->m_curl, CURLOPT_URL, this->m_url.c_str());
+ curl_easy_setopt(this->m_curl, CURLOPT_CUSTOMREQUEST, this->m_method.c_str());
+
+ setDefaultConfig();
+
+ if (!this->m_body.empty()) {
+ curl_easy_setopt(this->m_curl, CURLOPT_POSTFIELDS, this->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] : this->m_headers) {
+ std::string header = hex::format("{}: {}", key, value);
+ headers = curl_slist_append(headers, header.c_str());
+ }
+ curl_easy_setopt(this->m_curl, CURLOPT_HTTPHEADER, headers);
+
+ {
+ std::scoped_lock lock(this->m_transmissionMutex);
+
+ auto result = curl_easy_perform(this->m_curl);
+ if (result != CURLE_OK){
+ char *url = nullptr;
+ curl_easy_getinfo(this->m_curl, CURLINFO_EFFECTIVE_URL, &url);
+ log::error("Http request '{0} {1}' failed with error {2}: '{3}'", this->m_method, url, u32(result), curl_easy_strerror(result));
+ checkProxyErrors();
+
+ return { };
+ }
+ }
+
+ long statusCode = 0;
+ curl_easy_getinfo(this->m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
+
+ return Result(statusCode, { data.begin(), data.end() });
+ }
+
+}
\ No newline at end of file
diff --git a/lib/libimhex/include/hex/helpers/opengl.hpp b/lib/libimhex/include/hex/helpers/opengl.hpp
index 1f8eb8c85..f0423041b 100644
--- a/lib/libimhex/include/hex/helpers/opengl.hpp
+++ b/lib/libimhex/include/hex/helpers/opengl.hpp
@@ -9,7 +9,14 @@
#include
#include
-#include
+#if defined(OS_WEB)
+ #define GLFW_INCLUDE_ES3
+ #include
+#else
+ #include
+#endif
+
+#include
namespace hex::gl {
diff --git a/lib/libimhex/include/hex/helpers/tar.hpp b/lib/libimhex/include/hex/helpers/tar.hpp
index fee117da0..531c12901 100644
--- a/lib/libimhex/include/hex/helpers/tar.hpp
+++ b/lib/libimhex/include/hex/helpers/tar.hpp
@@ -52,7 +52,7 @@ namespace hex {
bool m_valid = false;
- // these will be updated when the constructor is called
+ // These will be updated when the constructor is called
int m_tarOpenErrno = MTAR_ESUCCESS;
int m_fileOpenErrno = 0;
};
diff --git a/lib/libimhex/include/hex/helpers/types.hpp b/lib/libimhex/include/hex/helpers/types.hpp
index 0df6343f2..983650941 100644
--- a/lib/libimhex/include/hex/helpers/types.hpp
+++ b/lib/libimhex/include/hex/helpers/types.hpp
@@ -21,7 +21,7 @@ namespace hex {
struct Region {
u64 address;
- size_t size;
+ u64 size;
[[nodiscard]] constexpr bool isWithin(const Region &other) const {
if (*this == Invalid() || other == Invalid())
diff --git a/lib/libimhex/include/hex/plugin.hpp b/lib/libimhex/include/hex/plugin.hpp
index 346498d1a..ac0c265a0 100644
--- a/lib/libimhex/include/hex/plugin.hpp
+++ b/lib/libimhex/include/hex/plugin.hpp
@@ -9,6 +9,13 @@
#include
#include
+#include
+
+#if defined (IMHEX_STATIC_LINK_PLUGINS)
+ #define IMHEX_PLUGIN_VISIBILITY_PREFIX static
+#else
+ #define IMHEX_PLUGIN_VISIBILITY_PREFIX extern "C" [[gnu::visibility("default")]]
+#endif
/**
* This macro is used to define all the required entry points for a plugin.
@@ -16,16 +23,29 @@
*/
#define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(name, author, description)
-#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
- extern "C" [[gnu::visibility("default")]] const char *getPluginName() { return name; } \
- extern "C" [[gnu::visibility("default")]] const char *getPluginAuthor() { return author; } \
- extern "C" [[gnu::visibility("default")]] const char *getPluginDescription() { return description; } \
- extern "C" [[gnu::visibility("default")]] const char *getCompatibleVersion() { return IMHEX_VERSION; } \
- extern "C" [[gnu::visibility("default")]] void setImGuiContext(ImGuiContext *ctx) { \
- ImGui::SetCurrentContext(ctx); \
- GImGui = ctx; \
- } \
- extern "C" [[gnu::visibility("default")]] void initializePlugin()
+#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
+ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \
+ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \
+ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \
+ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \
+ IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \
+ ImGui::SetCurrentContext(ctx); \
+ GImGui = ctx; \
+ } \
+ IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin(); \
+ extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
+ hex::PluginManager::addPlugin(hex::PluginFunctions { \
+ initializePlugin, \
+ getPluginName, \
+ getPluginAuthor, \
+ getPluginDescription, \
+ getCompatibleVersion, \
+ setImGuiContext, \
+ nullptr, \
+ nullptr \
+ }); \
+ } \
+ IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin()
/**
* This macro is used to define subcommands defined by the plugin
diff --git a/lib/libimhex/include/hex/providers/provider_data.hpp b/lib/libimhex/include/hex/providers/provider_data.hpp
index f80cec521..b36e0b1dd 100644
--- a/lib/libimhex/include/hex/providers/provider_data.hpp
+++ b/lib/libimhex/include/hex/providers/provider_data.hpp
@@ -83,18 +83,18 @@ namespace hex {
this->m_data.clear();
});
- // moves the data of this PerProvider instance from one provider to another
+ // Moves the data of this PerProvider instance from one provider to another
EventManager::subscribe(this, [this](prv::Provider *from, prv::Provider *to) {
- // get the value from the old provider, (removes it from the map)
+ // Get the value from the old provider, (removes it from the map)
auto node = m_data.extract(from);
- // ensure the value existed
+ // Ensure the value existed
if (node.empty()) return;
- // delete the value from the new provider, that we want to replace
+ // Delete the value from the new provider, that we want to replace
this->m_data.erase(to);
- // re-insert it with the key of the new provider
+ // Re-insert it with the key of the new provider
node.key() = to;
this->m_data.insert(std::move(node));
});
diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h
index 3dbf71b9d..756420772 100644
--- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h
+++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h
@@ -177,7 +177,7 @@ namespace ImGui {
}
inline void TextFormattedWrappedSelectable(const std::string &fmt, auto &&...args) {
- //Manually wrap text, using the letter M (generally the widest character in non-monospaced fonts) to calculate the character width to use.
+ // Manually wrap text, using the letter M (generally the widest character in non-monospaced fonts) to calculate the character width to use.
auto text = wolv::util::wrapMonospacedString(
hex::format(fmt, std::forward(args)...),
ImGui::CalcTextSize("M").x,
diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp
index 8242d4edc..62270ca2b 100644
--- a/lib/libimhex/source/api/content_registry.cpp
+++ b/lib/libimhex/source/api/content_registry.cpp
@@ -8,6 +8,10 @@
#include
#include
+#if defined(OS_WEB)
+#include
+#include
+#endif
#include
@@ -18,7 +22,7 @@ namespace hex {
namespace ContentRegistry::Settings {
- constexpr auto SettingsFile = "settings.json";
+ [[maybe_unused]] constexpr auto SettingsFile = "settings.json";
namespace impl {
@@ -49,43 +53,71 @@ namespace hex {
return settings;
}
- void load() {
- bool loaded = false;
- for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
- wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Read);
+ #if defined(OS_WEB)
+ void load() {
+ char *data = (char *) MAIN_THREAD_EM_ASM_INT({
+ let data = localStorage.getItem("config");
+ return data ? stringToNewUTF8(data) : null;
+ });
- if (file.isValid()) {
- getSettingsData() = nlohmann::json::parse(file.readString());
- loaded = true;
- break;
+ if (data == nullptr) {
+ store();
+ } else {
+ getSettingsData() = nlohmann::json::parse(data);
}
}
- if (!loaded)
- store();
- }
-
- void store() {
- // During a crash settings can be empty, causing them to be overwritten.
- if(getSettingsData().empty()) {
- return;
+ void store() {
+ auto data = getSettingsData().dump();
+ MAIN_THREAD_EM_ASM({
+ localStorage.setItem("config", UTF8ToString($0));
+ }, data.c_str());
}
- for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
- wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Create);
+ void clear() {
+ MAIN_THREAD_EM_ASM({
+ localStorage.removeItem("config");
+ });
+ }
+ #else
+ void load() {
+ bool loaded = false;
+ for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
+ wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Read);
- if (file.isValid()) {
- file.writeString(getSettingsData().dump(4));
- break;
+ if (file.isValid()) {
+ getSettingsData() = nlohmann::json::parse(file.readString());
+ loaded = true;
+ break;
+ }
+ }
+
+ if (!loaded)
+ store();
+ }
+
+ void store() {
+ // During a crash settings can be empty, causing them to be overwritten.
+ if(getSettingsData().empty()) {
+ return;
+ }
+
+ for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
+ wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Create);
+
+ if (file.isValid()) {
+ file.writeString(getSettingsData().dump(4));
+ break;
+ }
}
}
- }
- void clear() {
- for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
- wolv::io::fs::remove(dir / SettingsFile);
+ void clear() {
+ for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
+ wolv::io::fs::remove(dir / SettingsFile);
+ }
}
- }
+ #endif
static auto getCategoryEntry(const std::string &unlocalizedCategory) {
auto &entries = getEntries();
diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp
index 7eb451b89..0eca8739c 100644
--- a/lib/libimhex/source/api/imhex_api.cpp
+++ b/lib/libimhex/source/api/imhex_api.cpp
@@ -370,7 +370,7 @@ namespace hex {
namespace impl {
- // default to true means we forward to ourselves by default
+ // Default to true means we forward to ourselves by default
static bool s_isMainInstance = true;
void setMainInstanceStatus(bool status) {
@@ -547,6 +547,8 @@ namespace hex {
return "Linux";
#elif defined(OS_MACOS)
return "macOS";
+ #elif defined(OS_WEB)
+ return "Web";
#else
return "Unknown";
#endif
@@ -559,7 +561,7 @@ namespace hex {
::GetVersionExA(&info);
return hex::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber);
- #elif defined(OS_LINUX) || defined(OS_MACOS)
+ #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
struct utsname details;
if (uname(&details) != 0) {
@@ -591,7 +593,7 @@ namespace hex {
default:
return "Unknown";
}
- #elif defined(OS_LINUX) || defined(OS_MACOS)
+ #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
struct utsname details;
if (uname(&details) != 0) {
diff --git a/lib/libimhex/source/api/plugin_manager.cpp b/lib/libimhex/source/api/plugin_manager.cpp
index 47a438cdb..6000bc204 100644
--- a/lib/libimhex/source/api/plugin_manager.cpp
+++ b/lib/libimhex/source/api/plugin_manager.cpp
@@ -12,6 +12,7 @@
namespace hex {
Plugin::Plugin(const std::fs::path &path) : m_path(path) {
+
#if defined(OS_WINDOWS)
this->m_handle = LoadLibraryW(path.c_str());
@@ -28,38 +29,30 @@ namespace hex {
}
#endif
- this->m_initializePluginFunction = getPluginFunction("initializePlugin");
- this->m_getPluginNameFunction = getPluginFunction("getPluginName");
- this->m_getPluginAuthorFunction = getPluginFunction("getPluginAuthor");
- this->m_getPluginDescriptionFunction = getPluginFunction("getPluginDescription");
- this->m_getCompatibleVersionFunction = getPluginFunction("getCompatibleVersion");
- this->m_setImGuiContextFunction = getPluginFunction("setImGuiContext");
- this->m_isBuiltinPluginFunction = getPluginFunction("isBuiltinPlugin");
- this->m_getSubCommandsFunction = getPluginFunction("getSubCommands");
+ this->m_functions.initializePluginFunction = getPluginFunction("initializePlugin");
+ this->m_functions.getPluginNameFunction = getPluginFunction("getPluginName");
+ this->m_functions.getPluginAuthorFunction = getPluginFunction("getPluginAuthor");
+ this->m_functions.getPluginDescriptionFunction = getPluginFunction("getPluginDescription");
+ this->m_functions.getCompatibleVersionFunction = getPluginFunction("getCompatibleVersion");
+ this->m_functions.setImGuiContextFunction = getPluginFunction("setImGuiContext");
+ this->m_functions.isBuiltinPluginFunction = getPluginFunction("isBuiltinPlugin");
+ this->m_functions.getSubCommandsFunction = getPluginFunction("getSubCommands");
}
+ Plugin::Plugin(hex::PluginFunctions functions) {
+ this->m_handle = nullptr;
+ this->m_functions = functions;
+ }
+
+
Plugin::Plugin(Plugin &&other) noexcept {
this->m_handle = other.m_handle;
+ other.m_handle = nullptr;
+
this->m_path = std::move(other.m_path);
- this->m_initializePluginFunction = other.m_initializePluginFunction;
- this->m_getPluginNameFunction = other.m_getPluginNameFunction;
- this->m_getPluginAuthorFunction = other.m_getPluginAuthorFunction;
- this->m_getPluginDescriptionFunction = other.m_getPluginDescriptionFunction;
- this->m_getCompatibleVersionFunction = other.m_getCompatibleVersionFunction;
- this->m_setImGuiContextFunction = other.m_setImGuiContextFunction;
- this->m_isBuiltinPluginFunction = other.m_isBuiltinPluginFunction;
- this->m_getSubCommandsFunction = other.m_getSubCommandsFunction;
-
- other.m_handle = nullptr;
- other.m_initializePluginFunction = nullptr;
- other.m_getPluginNameFunction = nullptr;
- other.m_getPluginAuthorFunction = nullptr;
- other.m_getPluginDescriptionFunction = nullptr;
- other.m_getCompatibleVersionFunction = nullptr;
- other.m_setImGuiContextFunction = nullptr;
- other.m_isBuiltinPluginFunction = nullptr;
- other.m_getSubCommandsFunction = nullptr;
+ this->m_functions = other.m_functions;
+ other.m_functions = {};
}
Plugin::~Plugin() {
@@ -67,15 +60,10 @@ namespace hex {
if (this->m_handle != nullptr)
FreeLibrary(this->m_handle);
#else
- if (this->m_handle != nullptr)
- dlclose(this->m_handle);
#endif
}
bool Plugin::initializePlugin() const {
- if (this->m_handle == nullptr)
- return false;
-
const auto pluginName = wolv::util::toUTF8String(this->m_path.filename());
const auto requestedVersion = getCompatibleVersion();
@@ -88,9 +76,9 @@ namespace hex {
}
}
- if (this->m_initializePluginFunction != nullptr) {
+ if (this->m_functions.initializePluginFunction != nullptr) {
try {
- this->m_initializePluginFunction();
+ this->m_functions.initializePluginFunction();
} catch (const std::exception &e) {
log::error("Plugin '{}' threw an exception on init: {}", pluginName, e.what());
return false;
@@ -99,6 +87,7 @@ namespace hex {
return false;
}
} else {
+ log::error("Plugin '{}' does not have a proper entrypoint", pluginName);
return false;
}
@@ -107,41 +96,41 @@ namespace hex {
}
std::string Plugin::getPluginName() const {
- if (this->m_getPluginNameFunction != nullptr)
- return this->m_getPluginNameFunction();
+ if (this->m_functions.getPluginNameFunction != nullptr)
+ return this->m_functions.getPluginNameFunction();
else
return hex::format("Unknown Plugin @ 0x{0:016X}", reinterpret_cast(this->m_handle));
}
std::string Plugin::getPluginAuthor() const {
- if (this->m_getPluginAuthorFunction != nullptr)
- return this->m_getPluginAuthorFunction();
+ if (this->m_functions.getPluginAuthorFunction != nullptr)
+ return this->m_functions.getPluginAuthorFunction();
else
return "Unknown";
}
std::string Plugin::getPluginDescription() const {
- if (this->m_getPluginDescriptionFunction != nullptr)
- return this->m_getPluginDescriptionFunction();
+ if (this->m_functions.getPluginDescriptionFunction != nullptr)
+ return this->m_functions.getPluginDescriptionFunction();
else
return "";
}
std::string Plugin::getCompatibleVersion() const {
- if (this->m_getCompatibleVersionFunction != nullptr)
- return this->m_getCompatibleVersionFunction();
+ if (this->m_functions.getCompatibleVersionFunction != nullptr)
+ return this->m_functions.getCompatibleVersionFunction();
else
return "";
}
void Plugin::setImGuiContext(ImGuiContext *ctx) const {
- if (this->m_setImGuiContextFunction != nullptr)
- this->m_setImGuiContextFunction(ctx);
+ if (this->m_functions.setImGuiContextFunction != nullptr)
+ this->m_functions.setImGuiContextFunction(ctx);
}
[[nodiscard]] bool Plugin::isBuiltinPlugin() const {
- if (this->m_isBuiltinPluginFunction != nullptr)
- return this->m_isBuiltinPluginFunction();
+ if (this->m_functions.isBuiltinPluginFunction != nullptr)
+ return this->m_functions.isBuiltinPluginFunction();
else
return false;
}
@@ -155,8 +144,8 @@ namespace hex {
}
std::span Plugin::getSubCommands() const {
- if (this->m_getSubCommandsFunction != nullptr) {
- auto result = this->m_getSubCommandsFunction();
+ if (this->m_functions.getSubCommandsFunction != nullptr) {
+ auto result = this->m_functions.getSubCommandsFunction();
return *reinterpret_cast*>(result);
} else
return { };
@@ -171,43 +160,50 @@ namespace hex {
#endif
}
-
- namespace {
-
- std::fs::path s_pluginFolder;
- std::vector s_plugins;
-
- }
-
bool PluginManager::load(const std::fs::path &pluginFolder) {
if (!wolv::io::fs::exists(pluginFolder))
return false;
- s_pluginFolder = pluginFolder;
+ getPluginPaths().push_back(pluginFolder);
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug")
- s_plugins.emplace_back(pluginPath.path());
+ getPlugins().emplace_back(pluginPath.path());
}
- if (s_plugins.empty())
+ if (getPlugins().empty())
return false;
return true;
}
void PluginManager::unload() {
- s_plugins.clear();
- s_pluginFolder.clear();
+ getPlugins().clear();
+ getPluginPaths().clear();
}
void PluginManager::reload() {
+ auto paths = getPluginPaths();
+
PluginManager::unload();
- PluginManager::load(s_pluginFolder);
+ for (const auto &path : paths)
+ PluginManager::load(path);
}
- const std::vector &PluginManager::getPlugins() {
- return s_plugins;
+ void PluginManager::addPlugin(hex::PluginFunctions functions) {
+ getPlugins().emplace_back(functions);
+ }
+
+ std::vector &PluginManager::getPlugins() {
+ static std::vector plugins;
+
+ return plugins;
+ }
+
+ std::vector &PluginManager::getPluginPaths() {
+ static std::vector pluginPaths;
+
+ return pluginPaths;
}
}
diff --git a/lib/libimhex/source/api/task.cpp b/lib/libimhex/source/api/task.cpp
index 14cc59149..98b7f16d9 100644
--- a/lib/libimhex/source/api/task.cpp
+++ b/lib/libimhex/source/api/task.cpp
@@ -49,6 +49,8 @@ namespace hex {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
#elif defined(OS_LINUX)
pthread_setname_np(pthread_self(), name.c_str());
+ #elif defined(OS_WEB)
+ hex::unused(name);
#elif defined(OS_MACOS)
pthread_setname_np(name.c_str());
#endif
diff --git a/lib/libimhex/source/helpers/crypto.cpp b/lib/libimhex/source/helpers/crypto.cpp
index 7539d859d..7ce3b1d50 100644
--- a/lib/libimhex/source/helpers/crypto.cpp
+++ b/lib/libimhex/source/helpers/crypto.cpp
@@ -82,7 +82,7 @@ namespace hex::crypt {
template requires (std::has_single_bit(NumBits))
class Crc {
- // use reflected algorithm, so we reflect only if refin / refout is FALSE
+ // Use reflected algorithm, so we reflect only if refin / refout is FALSE
// mask values, 0b1 << 64 is UB, so use 0b10 << 63
public:
@@ -396,7 +396,7 @@ namespace hex::crypt {
ON_SCOPE_EXIT { mbedtls_mpi_free(&ctx); };
- // read buffered
+ // Read buffered
constexpr static auto BufferSize = 0x100;
for (size_t offset = 0; offset < input.size(); offset += BufferSize) {
std::string inputPart = input.substr(offset, std::min(BufferSize, input.size() - offset));
diff --git a/lib/libimhex/source/helpers/fs.cpp b/lib/libimhex/source/helpers/fs.cpp
index 07d3beaa8..6ba9326d4 100644
--- a/lib/libimhex/source/helpers/fs.cpp
+++ b/lib/libimhex/source/helpers/fs.cpp
@@ -13,7 +13,13 @@
#include
#elif defined(OS_LINUX)
#include
- #include
+ #include
+#endif
+
+#if !defined(OS_WEB)
+#include
+#else
+#include
#endif
#include
@@ -84,66 +90,173 @@ namespace hex::fs {
));
system(R"(osascript -e 'tell application "Finder" to activate')");
#elif defined(OS_LINUX)
- // fallback to only opening the folder for now
+ // Fallback to only opening the folder for now
// TODO actually select the file
executeCmd({"xdg-open", wolv::util::toUTF8String(selectedFilePath.parent_path())});
#endif
}
- bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath, bool multiple) {
- NFD::ClearError();
+ #if defined(OS_WEB)
- if (NFD::Init() != NFD_OKAY) {
- log::error("NFD init returned an error: {}", NFD::GetError());
- if (s_fileBrowserErrorCallback != nullptr)
- s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
- return false;
+ std::function currentCallback;
+
+ EMSCRIPTEN_KEEPALIVE
+ extern "C" void fileBrowserCallback(char* path) {
+ currentCallback(path);
}
- NFD::UniquePathU8 outPath;
- NFD::UniquePathSet outPaths;
- nfdresult_t result;
- switch (mode) {
- case DialogMode::Open:
- if (multiple)
- result = NFD::OpenDialogMultiple(outPaths, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
- else
- result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
- break;
- case DialogMode::Save:
- result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
- break;
- case DialogMode::Folder:
- result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str());
- break;
- default:
- std::unreachable();
- }
+ EM_JS(int, callJs_saveFile, (const char *rawFilename), {
+ let filename = UTF8ToString(rawFilename) || "file.bin";
+ FS.createPath("/", "savedFiles");
- if (result == NFD_OKAY){
- if(outPath != nullptr) {
- callback(reinterpret_cast(outPath.get()));
+ if (FS.analyzePath(filename).exists) {
+ FS.unlink(filename);
}
- if (outPaths != nullptr) {
- nfdpathsetsize_t numPaths = 0;
- if (NFD::PathSet::Count(outPaths, numPaths) == NFD_OKAY) {
- for (size_t i = 0; i < numPaths; i++) {
- NFD::UniquePathSetPath path;
- if (NFD::PathSet::GetPath(outPaths, i, path) == NFD_OKAY)
- callback(reinterpret_cast(path.get()));
+
+ // Call callback that will write the file
+ Module._fileBrowserCallback(stringToNewUTF8("/savedFiles/" + filename));
+
+ let data = FS.readFile("/savedFiles/" + filename);
+
+ const reader = Object.assign(new FileReader(), {
+ onload: () => {
+
+ // Show popup to user to download
+ let saver = document.createElement('a');
+ saver.href = reader.result;
+ saver.download = filename;
+ saver.style = "display: none";
+
+ saver.click();
+
+ },
+ onerror: () => {
+ throw new Error(reader.error);
+ },
+ });
+ reader.readAsDataURL(new File([data], "", { type: "application/octet-stream" }));
+
+ });
+
+ EM_JS(int, callJs_openFile, (bool multiple), {
+ let selector = document.createElement("input");
+ selector.type = "file";
+ selector.style = "display: none";
+ if (multiple) {
+ selector.multiple = true;
+ }
+ selector.onchange = () => {
+ if (selector.files.length == 0) return;
+
+ FS.createPath("/", "openedFiles");
+ for (let file of selector.files) {
+ const fr = new FileReader();
+ fr.onload = () => {
+ let path = "/openedFiles/"+file.name;
+ if (FS.analyzePath(path).exists) {
+ FS.unlink(path);
+ }
+ FS.createDataFile("/openedFiles/", file.name, fr.result, true, true);
+ Module._fileBrowserCallback(stringToNewUTF8(path));
+ };
+
+ fr.readAsBinaryString(file);
+ }
+ };
+ selector.click();
+ });
+
+ bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath, bool multiple) {
+ switch (mode) {
+ case DialogMode::Open: {
+ currentCallback = callback;
+ callJs_openFile(multiple);
+ break;
+ }
+ case DialogMode::Save: {
+ currentCallback = callback;
+ std::fs::path path;
+
+ if (!defaultPath.empty())
+ path = std::fs::path(defaultPath).filename();
+ else if (!validExtensions.empty())
+ path = "file." + validExtensions[0].spec;
+
+ callJs_saveFile(path.filename().string().c_str());
+ break;
+ }
+ case DialogMode::Folder: {
+ throw std::logic_error("Selecting a folder is not implemented");
+ return false;
+ }
+ default:
+ std::unreachable();
+ }
+ return true;
+ }
+
+ #else
+
+ bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath, bool multiple) {
+ std::vector validExtensionsNfd;
+ for (auto ext : validExtensions) {
+ validExtensionsNfd.emplace_back(nfdfilteritem_t{ext.name.c_str(), ext.spec.c_str()});
+ }
+ NFD::ClearError();
+
+ if (NFD::Init() != NFD_OKAY) {
+ log::error("NFD init returned an error: {}", NFD::GetError());
+ if (s_fileBrowserErrorCallback != nullptr)
+ s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
+ return false;
+ }
+
+ NFD::UniquePathU8 outPath;
+ NFD::UniquePathSet outPaths;
+ nfdresult_t result;
+ switch (mode) {
+ case DialogMode::Open:
+ if (multiple)
+ result = NFD::OpenDialogMultiple(outPaths, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
+ else
+ result = NFD::OpenDialog(outPath, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
+ break;
+ case DialogMode::Save:
+ result = NFD::SaveDialog(outPath, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
+ break;
+ case DialogMode::Folder:
+ result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str());
+ break;
+ default:
+ std::unreachable();
+ }
+
+ if (result == NFD_OKAY){
+ if(outPath != nullptr) {
+ callback(reinterpret_cast(outPath.get()));
+ }
+ if (outPaths != nullptr) {
+ nfdpathsetsize_t numPaths = 0;
+ if (NFD::PathSet::Count(outPaths, numPaths) == NFD_OKAY) {
+ for (size_t i = 0; i < numPaths; i++) {
+ NFD::UniquePathSetPath path;
+ if (NFD::PathSet::GetPath(outPaths, i, path) == NFD_OKAY)
+ callback(reinterpret_cast(path.get()));
+ }
}
}
+ } else if (result == NFD_ERROR) {
+ log::error("Requested file dialog returned an error: {}", NFD::GetError());
+ if (s_fileBrowserErrorCallback != nullptr)
+ s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
}
- } else if (result == NFD_ERROR) {
- log::error("Requested file dialog returned an error: {}", NFD::GetError());
- if (s_fileBrowserErrorCallback != nullptr)
- s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
+
+ NFD::Quit();
+
+ return result == NFD_OKAY;
}
- NFD::Quit();
-
- return result == NFD_OKAY;
- }
+ #endif
std::vector getDataPaths() {
std::vector paths;
@@ -202,7 +315,7 @@ namespace hex::fs {
return getDataPaths();
#elif defined(OS_MACOS)
return getDataPaths();
- #elif defined(OS_LINUX)
+ #elif defined(OS_LINUX) || defined(OS_WEB)
return {xdg::ConfigHomeDir() / "imhex"};
#endif
}
diff --git a/lib/libimhex/source/helpers/http_requests.cpp b/lib/libimhex/source/helpers/http_requests.cpp
index 88e0f1572..cfa4c762e 100644
--- a/lib/libimhex/source/helpers/http_requests.cpp
+++ b/lib/libimhex/source/helpers/http_requests.cpp
@@ -1,47 +1,9 @@
#include
+#include
+
namespace hex {
- namespace {
-
- std::string s_proxyUrl;
-
- }
-
-
- HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) {
- AT_FIRST_TIME {
- curl_global_init(CURL_GLOBAL_ALL);
- };
-
- AT_FINAL_CLEANUP {
- curl_global_cleanup();
- };
-
- this->m_curl = curl_easy_init();
- }
-
- HttpRequest::~HttpRequest() {
- curl_easy_cleanup(this->m_curl);
- }
-
- void HttpRequest::setDefaultConfig() {
- curl_easy_setopt(this->m_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
- curl_easy_setopt(this->m_curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
- curl_easy_setopt(this->m_curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(this->m_curl, CURLOPT_USERAGENT, "ImHex/1.0");
- curl_easy_setopt(this->m_curl, CURLOPT_DEFAULT_PROTOCOL, "https");
- curl_easy_setopt(this->m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
- curl_easy_setopt(this->m_curl, CURLOPT_SSL_VERIFYHOST, 2L);
- curl_easy_setopt(this->m_curl, CURLOPT_TIMEOUT_MS, 0L);
- curl_easy_setopt(this->m_curl, CURLOPT_CONNECTTIMEOUT_MS, this->m_timeout);
- curl_easy_setopt(this->m_curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(this->m_curl, CURLOPT_NOPROGRESS, 0L);
- curl_easy_setopt(this->m_curl, CURLOPT_XFERINFODATA, this);
- curl_easy_setopt(this->m_curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
- curl_easy_setopt(this->m_curl, CURLOPT_PROXY, s_proxyUrl.c_str());
- }
-
size_t HttpRequest::writeToVector(void *contents, size_t size, size_t nmemb, void *userdata) {
auto &response = *reinterpret_cast*>(userdata);
auto startSize = response.size();
@@ -60,27 +22,36 @@ namespace hex {
return size * nmemb;
}
- int HttpRequest::progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) {
- auto &request = *static_cast(contents);
-
- if (dlTotal > 0)
- request.m_progress = float(dlNow) / dlTotal;
- else if (ulTotal > 0)
- request.m_progress = float(ulNow) / ulTotal;
- else
- request.m_progress = 0.0F;
-
- return request.m_canceled ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK;
- }
-
- void HttpRequest::setProxy(std::string proxy) {
- s_proxyUrl = std::move(proxy);
- }
-
- void HttpRequest::checkProxyErrors() {
- if (!s_proxyUrl.empty()){
- log::info("A custom proxy '{0}' is in use. Is it working correctly?", s_proxyUrl);
+ std::string HttpRequest::urlEncode(const std::string &input) {
+ std::string result;
+ for (char c : input){
+ if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
+ result += c;
+ else
+ result += hex::format("%02X", c);
}
+ return result;
+ }
+
+ std::string HttpRequest::urlDecode(const std::string &input) {
+ std::string result;
+
+ for (u32 i = 0; i < input.size(); i++){
+ if (input[i] != '%'){
+ if (input[i] == '+')
+ result += ' ';
+ else
+ result += input[i];
+ } else {
+ const auto hex = crypt::decode16(input.substr(i + 1, 2));
+ if (hex.empty())
+ return "";
+
+ result += char(hex[0]);
+ i += 2;
+ }
+ }
+ return input;
}
}
\ No newline at end of file
diff --git a/lib/libimhex/source/helpers/http_requests_emscripten.cpp b/lib/libimhex/source/helpers/http_requests_emscripten.cpp
new file mode 100644
index 000000000..d13a0698c
--- /dev/null
+++ b/lib/libimhex/source/helpers/http_requests_emscripten.cpp
@@ -0,0 +1,55 @@
+#if defined(OS_WEB)
+
+#include
+
+namespace hex {
+
+ HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) {
+ emscripten_fetch_attr_init(&this->m_attr);
+ }
+
+ HttpRequest::HttpRequest(HttpRequest &&other) noexcept {
+ this->m_attr = other.m_attr;
+
+ this->m_method = std::move(other.m_method);
+ this->m_url = std::move(other.m_url);
+ this->m_headers = std::move(other.m_headers);
+ this->m_body = std::move(other.m_body);
+ }
+
+ HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept {
+ this->m_attr = other.m_attr;
+
+ this->m_method = std::move(other.m_method);
+ this->m_url = std::move(other.m_url);
+ this->m_headers = std::move(other.m_headers);
+ this->m_body = std::move(other.m_body);
+
+ return *this;
+ }
+
+ HttpRequest::~HttpRequest() { }
+
+ void HttpRequest::setDefaultConfig() { }
+
+ std::future>> HttpRequest::downloadFile() {
+ return std::async(std::launch::async, [this] {
+ std::vector response;
+
+ return this->executeImpl>(response);
+ });
+ }
+
+ void HttpRequest::setProxy(std::string proxy) {
+ hex::unused(proxy);
+ }
+
+ void HttpRequest::checkProxyErrors() { }
+
+ int HttpRequest::progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) {
+ hex::unused(contents, dlTotal, dlNow, ulTotal, ulNow);
+ return -1;
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/lib/libimhex/source/helpers/http_requests_native.cpp b/lib/libimhex/source/helpers/http_requests_native.cpp
new file mode 100644
index 000000000..cae9953df
--- /dev/null
+++ b/lib/libimhex/source/helpers/http_requests_native.cpp
@@ -0,0 +1,105 @@
+#if !defined(OS_WEB)
+
+#include
+
+namespace hex {
+
+ namespace {
+ std::string s_proxyUrl;
+ }
+
+ HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) {
+ AT_FIRST_TIME {
+ curl_global_init(CURL_GLOBAL_ALL);
+ };
+
+ AT_FINAL_CLEANUP {
+ curl_global_cleanup();
+ };
+
+ this->m_curl = curl_easy_init();
+ }
+
+ HttpRequest::~HttpRequest() {
+ curl_easy_cleanup(this->m_curl);
+ }
+
+ HttpRequest::HttpRequest(HttpRequest &&other) noexcept {
+ this->m_curl = other.m_curl;
+ other.m_curl = nullptr;
+
+ this->m_method = std::move(other.m_method);
+ this->m_url = std::move(other.m_url);
+ this->m_headers = std::move(other.m_headers);
+ this->m_body = std::move(other.m_body);
+ }
+
+ HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept {
+ this->m_curl = other.m_curl;
+ other.m_curl = nullptr;
+
+ this->m_method = std::move(other.m_method);
+ this->m_url = std::move(other.m_url);
+ this->m_headers = std::move(other.m_headers);
+ this->m_body = std::move(other.m_body);
+
+ return *this;
+ }
+
+ void HttpRequest::setDefaultConfig() {
+ curl_easy_setopt(this->m_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+ curl_easy_setopt(this->m_curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
+ curl_easy_setopt(this->m_curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(this->m_curl, CURLOPT_USERAGENT, "ImHex/1.0");
+ curl_easy_setopt(this->m_curl, CURLOPT_DEFAULT_PROTOCOL, "https");
+ curl_easy_setopt(this->m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
+ curl_easy_setopt(this->m_curl, CURLOPT_SSL_VERIFYHOST, 2L);
+ curl_easy_setopt(this->m_curl, CURLOPT_TIMEOUT_MS, 0L);
+ curl_easy_setopt(this->m_curl, CURLOPT_CONNECTTIMEOUT_MS, this->m_timeout);
+ curl_easy_setopt(this->m_curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(this->m_curl, CURLOPT_NOPROGRESS, 0L);
+ curl_easy_setopt(this->m_curl, CURLOPT_XFERINFODATA, this);
+ curl_easy_setopt(this->m_curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
+ curl_easy_setopt(this->m_curl, CURLOPT_PROXY, s_proxyUrl.c_str());
+ }
+
+ std::future>> HttpRequest::downloadFile() {
+ return std::async(std::launch::async, [this] {
+ std::vector response;
+
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
+ curl_easy_setopt(this->m_curl, CURLOPT_WRITEDATA, &response);
+
+ return this->executeImpl>(response);
+ });
+ }
+
+
+
+ void HttpRequest::setProxy(std::string proxy) {
+ s_proxyUrl = std::move(proxy);
+ }
+
+ void HttpRequest::checkProxyErrors() {
+ if (!s_proxyUrl.empty()){
+ log::info("A custom proxy '{0}' is in use. Is it working correctly?", s_proxyUrl);
+ }
+ }
+
+ int HttpRequest::progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) {
+ auto &request = *static_cast(contents);
+
+ if (dlTotal > 0)
+ request.m_progress = float(dlNow) / dlTotal;
+ else if (ulTotal > 0)
+ request.m_progress = float(ulNow) / ulTotal;
+ else
+ request.m_progress = 0.0F;
+
+ return request.m_canceled ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK;
+ }
+
+}
+
+
+#endif
\ No newline at end of file
diff --git a/lib/libimhex/source/helpers/opengl.cpp b/lib/libimhex/source/helpers/opengl.cpp
index 81ac3c6bd..0a2d489ef 100644
--- a/lib/libimhex/source/helpers/opengl.cpp
+++ b/lib/libimhex/source/helpers/opengl.cpp
@@ -5,6 +5,13 @@
#include
+#if defined(OS_WEB)
+ #define GLFW_INCLUDE_ES3
+ #include
+#else
+ #include
+#endif
+
namespace hex::gl {
Shader::Shader(std::string_view vertexSource, std::string_view fragmentSource) {
diff --git a/lib/libimhex/source/helpers/tar.cpp b/lib/libimhex/source/helpers/tar.cpp
index 75d75563b..69c76da08 100644
--- a/lib/libimhex/source/helpers/tar.cpp
+++ b/lib/libimhex/source/helpers/tar.cpp
@@ -35,7 +35,7 @@ namespace hex {
if (!this->m_valid) {
this->m_tarOpenErrno = tar_error;
- // hopefully this errno corresponds to the file open call in mtar_open
+ // Hopefully this errno corresponds to the file open call in mtar_open
this->m_fileOpenErrno = errno;
}
}
diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp
index 4b93238e3..1231fcb0a 100644
--- a/lib/libimhex/source/helpers/utils.cpp
+++ b/lib/libimhex/source/helpers/utils.cpp
@@ -20,6 +20,8 @@
#elif defined(OS_MACOS)
#include
#include
+#elif defined(OS_WEB)
+ #include "emscripten.h"
#endif
namespace hex {
@@ -319,6 +321,8 @@ namespace hex {
hex::unused(system(hex::format("open {0}", command).c_str()));
#elif defined(OS_LINUX)
executeCmd({"xdg-open", command});
+ #elif defined(OS_WEB)
+ hex::unused(command);
#endif
}
@@ -332,6 +336,10 @@ namespace hex {
openWebpageMacos(url.c_str());
#elif defined(OS_LINUX)
executeCmd({"xdg-open", url});
+ #elif defined(OS_WEB)
+ EM_ASM({
+ window.open(UTF8ToString($0), '_blank');
+ }, url.c_str());
#else
#warning "Unknown OS, can't open webpages"
#endif
@@ -497,26 +505,27 @@ namespace hex {
}
bool isProcessElevated() {
-#if defined(OS_WINDOWS)
- bool elevated = false;
- HANDLE token = INVALID_HANDLE_VALUE;
+ #if defined(OS_WINDOWS)
+ bool elevated = false;
+ HANDLE token = INVALID_HANDLE_VALUE;
- if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) {
- TOKEN_ELEVATION elevation;
- DWORD elevationSize = sizeof(TOKEN_ELEVATION);
+ if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) {
+ TOKEN_ELEVATION elevation;
+ DWORD elevationSize = sizeof(TOKEN_ELEVATION);
- if (::GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &elevationSize))
- elevated = elevation.TokenIsElevated;
- }
+ if (::GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &elevationSize))
+ elevated = elevation.TokenIsElevated;
+ }
- if (token != INVALID_HANDLE_VALUE)
- ::CloseHandle(token);
+ if (token != INVALID_HANDLE_VALUE)
+ ::CloseHandle(token);
- return elevated;
-
-#elif defined(OS_LINUX) || defined(OS_MACOS)
- return getuid() == 0 || getuid() != geteuid();
-#endif
+ return elevated;
+ #elif defined(OS_LINUX) || defined(OS_MACOS)
+ return getuid() == 0 || getuid() != geteuid();
+ #else
+ return false;
+ #endif
}
std::optional getEnvironmentVariable(const std::string &env) {
diff --git a/lib/libimhex/source/subcommands/subcommands.cpp b/lib/libimhex/source/subcommands/subcommands.cpp
index 8971d7ad5..10c82e67d 100644
--- a/lib/libimhex/source/subcommands/subcommands.cpp
+++ b/lib/libimhex/source/subcommands/subcommands.cpp
@@ -34,26 +34,26 @@ namespace hex::subcommands {
auto argsIter = args.begin();
- // get subcommand associated with the first argument
+ // Get subcommand associated with the first argument
std::optional currentSubCommand = findSubCommand(*argsIter);
if (currentSubCommand) {
argsIter++;
- // if it is a valid subcommand, remove it from the argument list
+ // If it is a valid subcommand, remove it from the argument list
} else {
- // if no (valid) subcommand was provided, the default one is --open
+ // If no (valid) subcommand was provided, the default one is --open
currentSubCommand = findSubCommand("--open");
}
- // arguments of the current subcommand
+ // Arguments of the current subcommand
std::vector currentSubCommandArgs;
- // compute all subcommands to run
+ // Compute all subcommands to run
while (argsIter != args.end()) {
const std::string &arg = *argsIter;
if (arg == "--othercmd") {
- // save command to run
+ // Save command to run
if (currentSubCommand) {
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
}
@@ -62,10 +62,10 @@ namespace hex::subcommands {
currentSubCommandArgs = { };
} else if (currentSubCommand) {
- // add current argument to the current command
+ // Add current argument to the current command
currentSubCommandArgs.push_back(arg);
} else {
- // get next subcommand from current argument
+ // Get next subcommand from current argument
currentSubCommand = findSubCommand(arg);
if (!currentSubCommand) {
log::error("No subcommand named '{}' found", arg);
@@ -76,17 +76,17 @@ namespace hex::subcommands {
argsIter++;
}
- // save last command to run
+ // Save last command to run
if (currentSubCommand) {
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
}
- // run the subcommands
+ // Run the subcommands
for (auto& subCommandPair : subCommands) {
subCommandPair.first.callback(subCommandPair.second);
}
- // exit the process if its not the main instance (the commands have been forwarded to another instance)
+ // Exit the process if its not the main instance (the commands have been forwarded to another instance)
if (!ImHexApi::System::isMainInstance()) {
exit(0);
}
diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp
index 111091c4a..cb6b76ee9 100644
--- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp
+++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp
@@ -9,8 +9,6 @@
#include
-#include
-
#include
#include
diff --git a/main/gui/CMakeLists.txt b/main/gui/CMakeLists.txt
index 69dbdf92f..b01a84aa4 100644
--- a/main/gui/CMakeLists.txt
+++ b/main/gui/CMakeLists.txt
@@ -8,11 +8,13 @@ add_executable(main ${APPLICATION_TYPE}
source/window/win_window.cpp
source/window/macos_window.cpp
source/window/linux_window.cpp
+ source/window/web_window.cpp
source/messaging/common.cpp
source/messaging/linux.cpp
source/messaging/macos.cpp
source/messaging/win.cpp
+ source/messaging/web.cpp
source/init/splash_window.cpp
source/init/tasks.cpp
@@ -29,6 +31,18 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/external/libromfs ${CMAKE
set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_dependencies(imhex_all main)
+if (EMSCRIPTEN)
+ target_link_options(main PRIVATE -sUSE_GLFW=3 -sUSE_PTHREADS=1 -sALLOW_MEMORY_GROWTH=1)
+ target_link_options(main PRIVATE -sTOTAL_MEMORY=134217728)
+ target_link_options(main PRIVATE -sMAX_WEBGL_VERSION=2)
+ target_link_options(main PRIVATE -sEXPORTED_RUNTIME_METHODS=ccall)
+ target_link_options(main PRIVATE -sFETCH)
+ target_link_options(main PRIVATE -sWASM_BIGINT)
+ target_link_options(main PRIVATE -O1)
+ target_link_options(main PRIVATE -sLEGACY_GL_EMULATION)
+ target_link_libraries(main PRIVATE idbfs.js)
+endif ()
+
set_target_properties(main PROPERTIES
OUTPUT_NAME ${IMHEX_APPLICATION_NAME}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../..
diff --git a/main/gui/include/window.hpp b/main/gui/include/window.hpp
index de061d56d..3110230b8 100644
--- a/main/gui/include/window.hpp
+++ b/main/gui/include/window.hpp
@@ -28,6 +28,8 @@ namespace hex {
static void initNative();
+ void resize(i32 width, i32 height);
+
private:
void setupNativeWindow();
void beginNativeWindowFrame();
diff --git a/main/gui/source/init/splash_window.cpp b/main/gui/source/init/splash_window.cpp
index 236499628..d78bde129 100644
--- a/main/gui/source/init/splash_window.cpp
+++ b/main/gui/source/init/splash_window.cpp
@@ -16,7 +16,6 @@
#include
#include
#include
-#include
#include
#include
@@ -29,6 +28,14 @@
#include
#include
+#if defined(OS_WEB)
+ #define GLFW_INCLUDE_ES3
+ #include
+ #include
+#else
+ #include
+#endif
+
using namespace std::literals::chrono_literals;
namespace hex::init {
@@ -324,6 +331,8 @@ namespace hex::init {
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
+ glfwWindowHint(GLFW_SAMPLES, 1);
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
// Create the splash screen window
this->m_window = glfwCreateWindow(1, 400, "Starting ImHex...", nullptr, nullptr);
@@ -373,6 +382,8 @@ namespace hex::init {
#if defined(OS_MACOS)
ImGui_ImplOpenGL3_Init("#version 150");
+ #elif defined(OS_WEB)
+ ImGui_ImplOpenGL3_Init();
#else
ImGui_ImplOpenGL3_Init("#version 130");
#endif
diff --git a/main/gui/source/init/tasks.cpp b/main/gui/source/init/tasks.cpp
index 9bf3b9335..09832227e 100644
--- a/main/gui/source/init/tasks.cpp
+++ b/main/gui/source/init/tasks.cpp
@@ -80,6 +80,13 @@ namespace hex::init {
}
TaskManager::createBackgroundTask("Sending statistics...", [uuid, versionString](auto&) {
+ // To avoid potentially flooding our database with lots of dead users
+ // from people just visiting the website, don't send telemetry data from
+ // the web version
+ #if defined(OS_WEB)
+ return;
+ #endif
+
// Make telemetry request
nlohmann::json telemetry = {
{ "uuid", uuid },
@@ -145,7 +152,7 @@ namespace hex::init {
wolv::io::File newConfigFile(newConfigPath / "settings.json", wolv::io::File::Mode::Read);
if (!newConfigFile.isValid()) {
- // find an old config
+ // Find an old config
std::fs::path oldConfigPath;
for (const auto &dir : hex::fs::appendPath(hex::fs::getDataPaths(), "config")) {
wolv::io::File oldConfigFile(dir / "settings.json", wolv::io::File::Mode::Read);
@@ -493,15 +500,17 @@ namespace hex::init {
// ImHex requires exactly one built-in plugin
// If no built-in plugin or more than one was found, something's wrong and we can't continue
- if (builtinPlugins == 0) {
- log::error("Built-in plugin not found!");
- ImHexApi::System::impl::addInitArgument("no-builtin-plugin");
- return false;
- } else if (builtinPlugins > 1) {
- log::error("Found more than one built-in plugin!");
- ImHexApi::System::impl::addInitArgument("multiple-builtin-plugins");
- return false;
- }
+ #if !defined(EMSCRIPTEN)
+ if (builtinPlugins == 0) {
+ log::error("Built-in plugin not found!");
+ ImHexApi::System::impl::addInitArgument("no-builtin-plugin");
+ return false;
+ } else if (builtinPlugins > 1) {
+ log::error("Found more than one built-in plugin!");
+ ImHexApi::System::impl::addInitArgument("multiple-builtin-plugins");
+ return false;
+ }
+ #endif
return true;
}
diff --git a/main/gui/source/main.cpp b/main/gui/source/main.cpp
index 3f32e35c2..d722853dc 100644
--- a/main/gui/source/main.cpp
+++ b/main/gui/source/main.cpp
@@ -19,6 +19,11 @@
#include
#include
+#if defined(OS_WEB)
+ #include
+ #include
+#endif
+
using namespace hex;
namespace {
@@ -66,6 +71,7 @@ namespace {
/**
* @brief Displays ImHex's splash screen and runs all initialization tasks. The splash screen will be displayed until all tasks have finished.
*/
+ [[maybe_unused]]
void initializeImHex() {
init::WindowSplash splashWindow;
@@ -105,6 +111,103 @@ namespace {
}
}
+
+ #if defined(OS_WEB)
+ using namespace hex::init;
+
+ void saveFsData() {
+ EM_ASM({
+ FS.syncfs(function (err) {
+ if (!err)
+ return;
+ alert("Failed to save permanent file system: "+err);
+ });
+ });
+ }
+
+ int runImHex() {
+ auto splashWindow = new WindowSplash();
+
+ log::info("Using '{}' GPU", ImHexApi::System::getGPUVendor());
+
+ // Add initialization tasks to run
+ TaskManager::init();
+ for (const auto &[name, task, async] : init::getInitTasks())
+ splashWindow->addStartupTask(name, task, async);
+
+ splashWindow->startStartupTasks();
+
+ // Draw the splash window while tasks are running
+ emscripten_set_main_loop_arg([](void *arg) {
+ auto splashWindow = reinterpret_cast(arg);
+
+ FrameResult res = splashWindow->fullFrame();
+ if (res == FrameResult::success) {
+ handleFileOpenRequest();
+
+ // Clean up everything after the main window is closed
+ emscripten_set_beforeunload_callback(nullptr, [](int eventType, const void *reserved, void *userData) {
+ hex::unused(eventType, reserved, userData);
+
+ try {
+ saveFsData();
+ deinitializeImHex();
+ return "";
+ } catch (const std::exception &ex) {
+ std::string *msg = new std::string("Failed to deinitialize ImHex. This is just a message warning you of this, the application has already closed, you probably can't do anything about it. Message: ");
+ msg->append(std::string(ex.what()));
+ log::fatal("{}", *msg);
+ return msg->c_str();
+ }
+ });
+
+ // Delete splash window (do it before creating the main window so glfw destroys the window)
+ delete splashWindow;
+
+ emscripten_cancel_main_loop();
+
+ // Main window
+ static Window window;
+ emscripten_set_main_loop([]() {
+ window.fullFrame();
+ }, 60, 0);
+ }
+ }, splashWindow, 60, 0);
+
+ return -1;
+ }
+
+ #else
+
+ int runImHex() {
+
+ bool shouldRestart = false;
+ do {
+ // Register an event handler that will make ImHex restart when requested
+ shouldRestart = false;
+ EventManager::subscribe([&] {
+ shouldRestart = true;
+ });
+
+ initializeImHex();
+ handleFileOpenRequest();
+
+ // Clean up everything after the main window is closed
+ ON_SCOPE_EXIT {
+ deinitializeImHex();
+ };
+
+ // Main window
+ Window window;
+ window.loop();
+
+ } while (shouldRestart);
+
+ return EXIT_SUCCESS;
+ }
+
+ #endif
+
}
/**
@@ -127,27 +230,5 @@ int main(int argc, char **argv) {
ImHexApi::System::impl::setPortableVersion(isPortableVersion());
- bool shouldRestart = false;
- do {
- // Register an event handler that will make ImHex restart when requested
- shouldRestart = false;
- EventManager::subscribe([&] {
- shouldRestart = true;
- });
-
- initializeImHex();
- handleFileOpenRequest();
-
- // Clean up everything after the main window is closed
- ON_SCOPE_EXIT {
- deinitializeImHex();
- };
-
- // Main window
- Window window;
- window.loop();
-
- } while (shouldRestart);
-
- return EXIT_SUCCESS;
-}
+ return runImHex();
+};
diff --git a/main/gui/source/messaging/web.cpp b/main/gui/source/messaging/web.cpp
new file mode 100644
index 000000000..5c6b9a4f5
--- /dev/null
+++ b/main/gui/source/messaging/web.cpp
@@ -0,0 +1,24 @@
+#if defined(OS_WEB)
+
+#include
+
+#include
+#include
+
+#include "messaging.hpp"
+
+namespace hex::messaging {
+
+ void sendToOtherInstance(const std::string &eventName, const std::vector &args) {
+ hex::unused(eventName);
+ hex::unused(args);
+ log::error("Unimplemented function 'sendToOtherInstance()' called");
+ }
+
+ // Not implemented, so lets say we are the main instance every time so events are forwarded to ourselves
+ bool setupNative() {
+ return true;
+ }
+}
+
+#endif
diff --git a/main/gui/source/messaging/win.cpp b/main/gui/source/messaging/win.cpp
index aea59cb31..d77310882 100644
--- a/main/gui/source/messaging/win.cpp
+++ b/main/gui/source/messaging/win.cpp
@@ -22,13 +22,13 @@ namespace hex::messaging {
// Check if the window is visible and if it's an ImHex window
if (::IsWindowVisible(hWnd) == TRUE && length != 0) {
if (windowName.starts_with("ImHex")) {
- // it's our window, return it and stop iteration
+ // It's our window, return it and stop iteration
*reinterpret_cast(ret) = hWnd;
return FALSE;
}
}
- // continue iteration
+ // Continue iteration
return TRUE;
}, reinterpret_cast(&imhexWindow));
@@ -69,7 +69,7 @@ namespace hex::messaging {
constexpr static auto UniqueMutexId = "ImHex/a477ea68-e334-4d07-a439-4f159c683763";
- // check if an ImHex instance is already running by opening a global mutex
+ // Check if an ImHex instance is already running by opening a global mutex
HANDLE globalMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, UniqueMutexId);
if (globalMutex == nullptr) {
// If no ImHex instance is running, create a new global mutex
diff --git a/main/gui/source/window/linux_window.cpp b/main/gui/source/window/linux_window.cpp
index 75b6ee868..457faa250 100644
--- a/main/gui/source/window/linux_window.cpp
+++ b/main/gui/source/window/linux_window.cpp
@@ -44,7 +44,7 @@ namespace hex {
executeCmd({"zenity", "--error", "--text", message});
} else if(isFileInPath("notify-send")) {
executeCmd({"notify-send", "-i", "script-error", "Error", message});
- } // hopefully one of these commands is installed
+ } // Hopefully one of these commands is installed
}
void Window::initNative() {
diff --git a/main/gui/source/window/web_window.cpp b/main/gui/source/window/web_window.cpp
new file mode 100644
index 000000000..0f05d3167
--- /dev/null
+++ b/main/gui/source/window/web_window.cpp
@@ -0,0 +1,71 @@
+#include "window.hpp"
+
+#if defined(OS_WEB)
+
+#include
+#include
+
+// Function used by c++ to get the size of the html canvas
+EM_JS(int, canvas_get_width, (), {
+ return Module.canvas.width;
+});
+
+// Function used by c++ to get the size of the html canvas
+EM_JS(int, canvas_get_height, (), {
+ return Module.canvas.height;
+});
+
+// Function called by javascript
+EM_JS(void, resizeCanvas, (), {
+ js_resizeCanvas();
+});
+
+namespace hex {
+
+ void nativeErrorMessage(const std::string &message) {
+ log::fatal(message);
+ EM_ASM({
+ alert(UTF8ToString($0));
+ }, message.c_str());
+ }
+
+ void Window::initNative() {
+ EM_ASM({
+ // Save data directory
+ FS.mkdir("/home/web_user/.local");
+ FS.mount(IDBFS, {}, '/home/web_user/.local');
+
+ FS.syncfs(true, function (err) {
+ if (!err)
+ return;
+ alert("Failed to load permanent file system: "+err);
+ });
+ });
+ }
+
+ void Window::setupNativeWindow() {
+ resizeCanvas();
+ }
+
+ void Window::beginNativeWindowFrame() {
+ static i32 prevWidth = 0;
+ static i32 prevHeight = 0;
+
+ auto width = canvas_get_width();
+ auto height = canvas_get_height();
+
+ if (prevWidth != width || prevHeight != height) {
+ // Size has changed
+
+ prevWidth = width;
+ prevHeight = height;
+ this->resize(width, height);
+ }
+ }
+
+ void Window::endNativeWindowFrame() {
+ }
+
+}
+
+#endif
\ No newline at end of file
diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp
index 193c163f7..29a0c004c 100644
--- a/main/gui/source/window/window.cpp
+++ b/main/gui/source/window/window.cpp
@@ -156,6 +156,8 @@ namespace hex {
}
void Window::fullFrame() {
+ this->m_lastFrameTime = glfwGetTime();
+
glfwPollEvents();
// Render frame
@@ -255,7 +257,7 @@ namespace hex {
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabActive));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabHovered));
- // custom titlebar buttons implementation for borderless window mode
+ // Custom titlebar buttons implementation for borderless window mode
auto &titleBarButtons = ContentRegistry::Interface::impl::getTitleBarButtons();
// Draw custom title bar buttons
@@ -812,6 +814,8 @@ namespace hex {
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
+ glfwWindowHint(GLFW_SAMPLES, 1);
if (restoreWindowPos) {
int maximized = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.maximized", GLFW_FALSE);
@@ -924,30 +928,32 @@ namespace hex {
win->processEvent();
});
- // Register key press callback
- glfwSetKeyCallback(this->m_window, [](GLFWwindow *window, int key, int scancode, int action, int mods) {
- hex::unused(mods);
+ #if !defined(OS_WEB)
+ // Register key press callback
+ glfwSetKeyCallback(this->m_window, [](GLFWwindow *window, int key, int scancode, int action, int mods) {
+ hex::unused(mods);
- auto win = static_cast(glfwGetWindowUserPointer(window));
+ auto win = static_cast(glfwGetWindowUserPointer(window));
- if (action == GLFW_RELEASE) {
- win->m_buttonDown = false;
- } else {
- win->m_buttonDown = true;
- }
+ if (action == GLFW_RELEASE) {
+ win->m_buttonDown = false;
+ } else {
+ win->m_buttonDown = true;
+ }
- if (key == GLFW_KEY_UNKNOWN) return;
+ if (key == GLFW_KEY_UNKNOWN) return;
- auto keyName = glfwGetKeyName(key, scancode);
- if (keyName != nullptr)
- key = std::toupper(keyName[0]);
+ auto keyName = glfwGetKeyName(key, scancode);
+ if (keyName != nullptr)
+ key = std::toupper(keyName[0]);
- if (action == GLFW_PRESS || action == GLFW_REPEAT) {
- win->m_pressedKeys.push_back(key);
- }
+ if (action == GLFW_PRESS || action == GLFW_REPEAT) {
+ win->m_pressedKeys.push_back(key);
+ }
- win->processEvent();
- });
+ win->processEvent();
+ });
+ #endif
// Register cursor position callback
glfwSetCursorPosCallback(this->m_window, [](GLFWwindow *window, double x, double y) {
@@ -995,6 +1001,10 @@ namespace hex {
glfwShowWindow(this->m_window);
}
+ void Window::resize(i32 width, i32 height) {
+ glfwSetWindowSize(this->m_window, width, height);
+ }
+
void Window::initImGui() {
IMGUI_CHECKVERSION();
@@ -1094,10 +1104,13 @@ namespace hex {
}
}
+
ImGui_ImplGlfw_InitForOpenGL(this->m_window, true);
#if defined(OS_MACOS)
ImGui_ImplOpenGL3_Init("#version 150");
+ #elif defined(OS_WEB)
+ ImGui_ImplOpenGL3_Init();
#else
ImGui_ImplOpenGL3_Init("#version 130");
#endif
diff --git a/plugins/builtin/include/content/helpers/diagrams.hpp b/plugins/builtin/include/content/helpers/diagrams.hpp
index c6800feb3..43b6ca5f5 100644
--- a/plugins/builtin/include/content/helpers/diagrams.hpp
+++ b/plugins/builtin/include/content/helpers/diagrams.hpp
@@ -183,7 +183,7 @@ namespace hex {
private:
size_t m_sampleSize;
- // The number of byte processed and the size of
+ // The number of bytes processed and the size of
// the file to analyze (useful for iterative analysis)
u64 m_byteCount;
u64 m_fileSize;
@@ -276,7 +276,7 @@ namespace hex {
private:
size_t m_sampleSize;
- // The number of byte processed and the size of
+ // The number of bytes processed and the size of
// the file to analyze (useful for iterative analysis)
u64 m_byteCount;
u64 m_fileSize;
@@ -313,7 +313,7 @@ namespace hex {
ImPlot::PlotLine("##ChunkBasedAnalysisLine", this->m_xBlockEntropy.data(), this->m_yBlockEntropySampled.data(), this->m_xBlockEntropy.size());
// 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.
+ // but just a set of bytes, we won't be able to use the drag bar correctly.
if (updateHandle) {
// Set a draggable line on the plot
if (ImPlot::DragLineX(1, &this->m_handlePosition, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
@@ -427,7 +427,7 @@ namespace hex {
}
// Method used to compute the entropy of a block of size `blockSize`
- // using the bytes occurrences from `valueCounts` array.
+ // using the byte occurrences from `valueCounts` array.
double calculateEntropy(std::array &valueCounts, size_t blockSize) {
double entropy = 0;
@@ -560,8 +560,8 @@ namespace hex {
// Position of the handle inside the plot
double m_handlePosition = 0.0;
- // Hold the number of block that have been processed
- // during the chunk based entropy analysis
+ // Hold the number of blocks that have been processed
+ // during the chunk-based entropy analysis
u64 m_blockCount;
// Hold the number of bytes that have been processed
@@ -572,7 +572,7 @@ namespace hex {
// (useful for the iterative analysis)
std::array m_blockValueCounts;
- // Variable to hold the result of the chunk based
+ // Variable to hold the result of the chunk-based
// entropy analysis
std::vector m_xBlockEntropy;
std::vector