mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 15:57:03 -05:00
Compare commits
66 Commits
feature/cl
...
v1.33.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8802a8e861 | ||
|
|
8a01f3cc6b | ||
|
|
7d7bd0d642 | ||
|
|
2b1be7bc30 | ||
|
|
9df21c7952 | ||
|
|
70b9ceba72 | ||
|
|
27f3c634ba | ||
|
|
33b091e9e6 | ||
|
|
976caf53ae | ||
|
|
c2bb6e37b1 | ||
|
|
b4b47ff60e | ||
|
|
db2ad0951f | ||
|
|
e5969c5d2e | ||
|
|
b1d6086c0c | ||
|
|
7dfcbb15a4 | ||
|
|
4d470385d8 | ||
|
|
b0f010690c | ||
|
|
360ae718b9 | ||
|
|
6f45a7939a | ||
|
|
e27a4df8b0 | ||
|
|
84cd3f989b | ||
|
|
2e09a4e567 | ||
|
|
0ce1a87cbf | ||
|
|
6a158f99d7 | ||
|
|
77f46317f0 | ||
|
|
4c98f6bca6 | ||
|
|
77bc45ca17 | ||
|
|
1647fa9446 | ||
|
|
41a3fdaf3c | ||
|
|
e517406f06 | ||
|
|
0cf2477988 | ||
|
|
cc3a5aed9a | ||
|
|
af10317bae | ||
|
|
5153ad6458 | ||
|
|
432f2f0862 | ||
|
|
7eb3ee7150 | ||
|
|
6cf2990808 | ||
|
|
6cb0d4d7d8 | ||
|
|
0d08b36a73 | ||
|
|
7efdaa73f1 | ||
|
|
6d548180bb | ||
|
|
4b64e044f7 | ||
|
|
4a118b94cc | ||
|
|
8e27eb8d36 | ||
|
|
b33453f03c | ||
|
|
7cf88d128b | ||
|
|
5b7c4324ff | ||
|
|
aaf9fdf61c | ||
|
|
6e36586ebc | ||
|
|
773f9d3f1c | ||
|
|
b448583105 | ||
|
|
d1de10c606 | ||
|
|
9ce3a9e612 | ||
|
|
99eaca4d09 | ||
|
|
29443af90f | ||
|
|
d1fb41783d | ||
|
|
99277d71cc | ||
|
|
db11c4c791 | ||
|
|
87c254a437 | ||
|
|
63a4d65d96 | ||
|
|
cf1868b0b3 | ||
|
|
a135381b80 | ||
|
|
c424e71f7e | ||
|
|
976baec753 | ||
|
|
25cfa6f10b | ||
|
|
265213bbac |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -2,7 +2,9 @@ name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
4
.github/workflows/build_web.yml
vendored
4
.github/workflows/build_web.yml
vendored
@@ -2,7 +2,9 @@ name: Build for the web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
8
.github/workflows/tests.yml
vendored
8
.github/workflows/tests.yml
vendored
@@ -2,9 +2,13 @@ name: "Unit Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -19,6 +19,8 @@ option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build
|
||||
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
|
||||
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
|
||||
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
|
||||
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" OFF)
|
||||
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
|
||||
|
||||
# Basic compiler and cmake configurations
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
@@ -46,6 +46,9 @@ function(addDefineToSource SOURCE DEFINE)
|
||||
APPEND
|
||||
PROPERTY COMPILE_DEFINITIONS "${DEFINE}"
|
||||
)
|
||||
|
||||
# Disable precompiled headers for this file
|
||||
set_source_files_properties(${SOURCE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
endfunction()
|
||||
|
||||
# Detect current OS / System
|
||||
@@ -67,6 +70,9 @@ macro(detectOS)
|
||||
add_compile_definitions(OS_WEB)
|
||||
elseif (UNIX AND NOT APPLE)
|
||||
add_compile_definitions(OS_LINUX)
|
||||
if (BSD AND BSD STREQUAL "FreeBSD")
|
||||
add_compile_definitions(OS_FREEBSD)
|
||||
endif()
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(IMHEX_PLUGINS_IN_SHARE)
|
||||
@@ -503,7 +509,7 @@ macro(setupCompilerFlags target)
|
||||
set(IMHEX_CXX_FLAGS "-fexceptions -frtti")
|
||||
|
||||
# Disable some warnings
|
||||
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations")
|
||||
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
@@ -611,6 +617,7 @@ macro(addBundledLibraries)
|
||||
endif()
|
||||
|
||||
set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF CACHE BOOL "" FORCE)
|
||||
set(LIBPL_ENABLE_PRECOMPILED_HEADERS ${IMHEX_ENABLE_PRECOMPILED_HEADERS} CACHE BOOL "" FORCE)
|
||||
|
||||
if (WIN32)
|
||||
set(LIBPL_SHARED_LIBRARY ON CACHE BOOL "" FORCE)
|
||||
@@ -758,4 +765,19 @@ endfunction()
|
||||
function(addIncludesFromLibrary target library)
|
||||
get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(${target} PRIVATE ${library_include_dirs})
|
||||
endfunction()
|
||||
|
||||
function(precompileHeaders target includeFolder)
|
||||
if (NOT IMHEX_ENABLE_PRECOMPILED_HEADERS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE TARGET_INCLUDES "${includeFolder}/**/*.hpp")
|
||||
set(SYSTEM_INCLUDES "<algorithm>;<array>;<atomic>;<chrono>;<cmath>;<cstddef>;<cstdint>;<cstdio>;<cstdlib>;<cstring>;<exception>;<filesystem>;<functional>;<iterator>;<limits>;<list>;<map>;<memory>;<optional>;<ranges>;<set>;<stdexcept>;<string>;<string_view>;<thread>;<tuple>;<type_traits>;<unordered_map>;<unordered_set>;<utility>;<variant>;<vector>")
|
||||
set(INCLUDES "${SYSTEM_INCLUDES};${TARGET_INCLUDES}")
|
||||
string(REPLACE ">" "$<ANGLE-R>" INCLUDES "${INCLUDES}")
|
||||
target_precompile_headers(${target}
|
||||
PUBLIC
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${INCLUDES}>"
|
||||
)
|
||||
endfunction()
|
||||
@@ -1,4 +1,4 @@
|
||||
find_file(libyara.a YARA_LIBRARIES)
|
||||
find_library(YARA_LIBRARIES NAMES yara)
|
||||
find_file(yara.h YARA_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(YARA_LIBRARIES YARA_INCLUDE_DIRS)
|
||||
@@ -36,8 +36,12 @@ macro(add_imhex_plugin)
|
||||
|
||||
# Add include directories and link libraries
|
||||
target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES})
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${IMHEX_PLUGIN_LIBRARIES} ${FMT_LIBRARIES} imgui_all_includes libwolv)
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_LIBRARIES})
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} imgui_all_includes libwolv)
|
||||
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl)
|
||||
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl-gen)
|
||||
|
||||
precompileHeaders(${IMHEX_PLUGIN_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
# Add IMHEX_PROJECT_NAME and IMHEX_VERSION define
|
||||
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}")
|
||||
@@ -88,6 +92,12 @@ macro(add_imhex_plugin)
|
||||
elseif (UNIX)
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH_USE_ORIGIN ON INSTALL_RPATH "$ORIGIN/")
|
||||
endif()
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt AND IMHEX_ENABLE_UNIT_TESTS)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_NAME}_tests)
|
||||
target_compile_definitions(${IMHEX_PLUGIN_NAME}_tests PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}-tests")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(add_romfs_resource input output)
|
||||
|
||||
2
lib/external/libwolv
vendored
2
lib/external/libwolv
vendored
Submodule lib/external/libwolv updated: 2ddf596306...7806c1939d
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
Submodule lib/external/pattern_language updated: 2dae41613b...d4648c4a59
@@ -77,37 +77,6 @@ else()
|
||||
target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
|
||||
endif()
|
||||
|
||||
enableUnityBuild(libimhex)
|
||||
setupCompilerFlags(libimhex)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(libimhex)
|
||||
|
||||
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
|
||||
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
# curl is only used in non-emscripten builds
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
if (WIN32)
|
||||
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
|
||||
elseif (APPLE)
|
||||
find_library(FOUNDATION NAMES Foundation)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${JTHREAD_LIBRARIES})
|
||||
target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES})
|
||||
|
||||
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
|
||||
if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_BRANCH)
|
||||
set(GIT_COMMIT_HASH_LONG "${IMHEX_COMMIT_HASH_LONG}")
|
||||
@@ -142,4 +111,38 @@ endif ()
|
||||
|
||||
addDefineToSource(source/api/imhex_api.cpp "IMHEX_VERSION=\"${IMHEX_VERSION_STRING}\"")
|
||||
|
||||
enableUnityBuild(libimhex)
|
||||
setupCompilerFlags(libimhex)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(libimhex)
|
||||
|
||||
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
|
||||
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
# curl is only used in non-emscripten builds
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
if (WIN32)
|
||||
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
|
||||
elseif (APPLE)
|
||||
find_library(FOUNDATION NAMES Foundation)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${JTHREAD_LIBRARIES})
|
||||
target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
|
||||
|
||||
precompileHeaders(libimhex "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
endif()
|
||||
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES})
|
||||
|
||||
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
|
||||
add_dependencies(imhex_all libimhex)
|
||||
@@ -22,7 +22,7 @@
|
||||
static void subscribe(void *token, Event::Callback function) { EventManager::subscribe<event_name>(token, function); } \
|
||||
static void unsubscribe(const EventManager::EventList::iterator &token) noexcept { EventManager::unsubscribe(token); } \
|
||||
static void unsubscribe(void *token) noexcept { EventManager::unsubscribe<event_name>(token); } \
|
||||
static void post(auto &&...args) noexcept { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
|
||||
static void post(auto &&...args) { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
|
||||
};
|
||||
|
||||
#define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__)
|
||||
@@ -71,11 +71,12 @@ namespace hex {
|
||||
|
||||
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
|
||||
|
||||
void operator()(Params... params) const noexcept {
|
||||
void operator()(std::string_view eventName, Params... params) const {
|
||||
try {
|
||||
m_func(params...);
|
||||
} catch (const std::exception &e) {
|
||||
log::error("An exception occurred while handling event: {}", e.what());
|
||||
log::error("An exception occurred while handling event {}: {}", eventName, e.what());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,12 +173,12 @@ namespace hex {
|
||||
* @param args Arguments to pass to the event
|
||||
*/
|
||||
template<impl::EventType E>
|
||||
static void post(auto &&...args) noexcept {
|
||||
static void post(auto && ...args) {
|
||||
std::scoped_lock lock(getEventMutex());
|
||||
|
||||
for (const auto &[id, event] : getEvents()) {
|
||||
if (id == E::Id) {
|
||||
(*static_cast<E *const>(event.get()))(std::forward<decltype(args)>(args)...);
|
||||
(*static_cast<E *const>(event.get()))(wolv::type::getTypeName<E>(), std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,4 +309,9 @@ namespace hex {
|
||||
* The 'from' provider should not have any per provider data after this, and should be immediately deleted
|
||||
*/
|
||||
EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *);
|
||||
|
||||
/**
|
||||
* Called when ImHex managed to catch an error in a general try/catch to prevent/recover from a crash
|
||||
*/
|
||||
EVENT_DEF(EventCrashRecovered, const std::exception &);
|
||||
}
|
||||
@@ -698,6 +698,7 @@ namespace hex {
|
||||
std::vector<GlyphRange> glyphRanges;
|
||||
Offset offset;
|
||||
u32 flags;
|
||||
std::optional<u32> defaultSize;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
@@ -716,8 +717,8 @@ namespace hex {
|
||||
GlyphRange range(const char *glyphBegin, const char *glyphEnd);
|
||||
GlyphRange range(u32 codepointBegin, u32 codepointEnd);
|
||||
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
|
||||
|
||||
constexpr static float DefaultFontSize = 13.0;
|
||||
|
||||
|
||||
@@ -1,72 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#if defined(OS_WEB)
|
||||
|
||||
#include <emscripten/fetch.h>
|
||||
#include <future>
|
||||
|
||||
namespace hex {
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
#include <emscripten/fetch.h>
|
||||
|
||||
// Execute the request
|
||||
auto result = this->executeImpl<T>(response);
|
||||
namespace hex {
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
|
||||
// Write the result to the file
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
|
||||
// Execute the request
|
||||
auto result = this->executeImpl<T>(response);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
// Write the result to the file
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
hex::unused(path, mimeName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
hex::unused(data, mimeName, fileName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
std::vector<u8> responseData;
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
strcpy(m_attr.requestMethod, m_method.c_str());
|
||||
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
||||
|
||||
if (!m_body.empty()) {
|
||||
m_attr.requestData = m_body.c_str();
|
||||
m_attr.requestDataSize = m_body.size();
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<const char*> headers;
|
||||
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
|
||||
headers.push_back(it->first.c_str());
|
||||
headers.push_back(it->second.c_str());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
hex::unused(path, mimeName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
headers.push_back(nullptr);
|
||||
m_attr.requestHeaders = headers.data();
|
||||
|
||||
// Send request
|
||||
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
hex::unused(data, mimeName, fileName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
data.resize(fetch->numBytes);
|
||||
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
std::vector<u8> responseData;
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
strcpy(m_attr.requestMethod, m_method.c_str());
|
||||
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
||||
|
||||
if (!m_body.empty()) {
|
||||
m_attr.requestData = m_body.c_str();
|
||||
m_attr.requestDataSize = m_body.size();
|
||||
}
|
||||
|
||||
std::vector<const char*> headers;
|
||||
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
|
||||
headers.push_back(it->first.c_str());
|
||||
headers.push_back(it->second.c_str());
|
||||
}
|
||||
headers.push_back(nullptr);
|
||||
m_attr.requestHeaders = headers.data();
|
||||
|
||||
// Send request
|
||||
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
|
||||
|
||||
data.resize(fetch->numBytes);
|
||||
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
|
||||
|
||||
return Result<T>(fetch->status, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
return Result<T>(fetch->status, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,145 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <future>
|
||||
#if !defined(OS_WEB)
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
#include <future>
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
namespace hex {
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
namespace hex {
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
|
||||
return this->executeImpl<T>(response);
|
||||
});
|
||||
}
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
return std::async(std::launch::async, [this, path, mimeName]{
|
||||
auto fileName = wolv::util::toUTF8String(path.filename());
|
||||
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
curl_mime_data_cb(part, file.getSize(),
|
||||
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
return fread(buffer, size, nitems, handle);
|
||||
},
|
||||
[](void *arg, curl_off_t offset, int origin) -> int {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
if (fseek(handle, offset, origin) != 0)
|
||||
return CURL_SEEKFUNC_CANTSEEK;
|
||||
else
|
||||
return CURL_SEEKFUNC_OK;
|
||||
},
|
||||
[](void *arg) {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
fclose(handle);
|
||||
},
|
||||
file.getHandle());
|
||||
curl_mime_filename(part, fileName.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
|
||||
curl_mime_filename(part, fileNameStr.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
|
||||
|
||||
setDefaultConfig();
|
||||
|
||||
if (!m_body.empty()) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
|
||||
return this->executeImpl<T>(response);
|
||||
});
|
||||
}
|
||||
|
||||
curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
return std::async(std::launch::async, [this, path, mimeName]{
|
||||
auto fileName = wolv::util::toUTF8String(path.filename());
|
||||
|
||||
for (auto &[key, value] : m_headers) {
|
||||
std::string header = hex::format("{}: {}", key, value);
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
curl_mime_data_cb(part, file.getSize(),
|
||||
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
return fread(buffer, size, nitems, handle);
|
||||
},
|
||||
[](void *arg, curl_off_t offset, int origin) -> int {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
if (fseek(handle, offset, origin) != 0)
|
||||
return CURL_SEEKFUNC_CANTSEEK;
|
||||
else
|
||||
return CURL_SEEKFUNC_OK;
|
||||
},
|
||||
[](void *arg) {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
fclose(handle);
|
||||
},
|
||||
file.getHandle());
|
||||
curl_mime_filename(part, fileName.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_transmissionMutex);
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
auto result = curl_easy_perform(m_curl);
|
||||
if (result != CURLE_OK){
|
||||
char *url = nullptr;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
|
||||
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
|
||||
checkProxyErrors();
|
||||
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
|
||||
curl_mime_filename(part, fileNameStr.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
return { };
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
|
||||
|
||||
setDefaultConfig();
|
||||
|
||||
if (!m_body.empty()) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
|
||||
}
|
||||
|
||||
curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
|
||||
|
||||
for (auto &[key, value] : m_headers) {
|
||||
std::string header = hex::format("{}: {}", key, value);
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
}
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_transmissionMutex);
|
||||
|
||||
auto result = curl_easy_perform(m_curl);
|
||||
if (result != CURLE_OK){
|
||||
char *url = nullptr;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
|
||||
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
|
||||
checkProxyErrors();
|
||||
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
long statusCode = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
|
||||
|
||||
return Result<T>(statusCode, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
long statusCode = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
|
||||
|
||||
return Result<T>(statusCode, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -40,13 +40,15 @@ namespace hex::log {
|
||||
std::scoped_lock lock(getLoggerMutex());
|
||||
|
||||
auto dest = getDestination();
|
||||
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
|
||||
try {
|
||||
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
|
||||
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
|
||||
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
|
||||
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
namespace color {
|
||||
@@ -92,19 +94,23 @@ namespace hex::log {
|
||||
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::getLoggerMutex());
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}", message);
|
||||
fflush(dest);
|
||||
try {
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}", message);
|
||||
fflush(dest);
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
[[maybe_unused]] void println(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::getLoggerMutex());
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
try {
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,7 +74,7 @@ namespace hex {
|
||||
return m_data | std::views::values;
|
||||
}
|
||||
|
||||
void setOnCreateCallback(std::function<void(const prv::Provider *, T&)> callback) {
|
||||
void setOnCreateCallback(std::function<void(prv::Provider *, T&)> callback) {
|
||||
m_onCreateCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace hex {
|
||||
auto [it, inserted] = m_data.emplace(provider, T());
|
||||
auto &[key, value] = *it;
|
||||
if (m_onCreateCallback)
|
||||
m_onCreateCallback(key, value);
|
||||
m_onCreateCallback(provider, value);
|
||||
});
|
||||
|
||||
EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){
|
||||
@@ -121,7 +121,7 @@ namespace hex {
|
||||
|
||||
private:
|
||||
std::map<const prv::Provider *, T> m_data;
|
||||
std::function<void(const prv::Provider *, T&)> m_onCreateCallback;
|
||||
std::function<void(prv::Provider *, T&)> m_onCreateCallback;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -533,6 +533,11 @@ namespace hex {
|
||||
|
||||
pl::PatternLanguage& getRuntime() {
|
||||
static PerProvider<pl::PatternLanguage> runtime;
|
||||
AT_FIRST_TIME {
|
||||
runtime.setOnCreateCallback([](prv::Provider *provider, pl::PatternLanguage &runtime) {
|
||||
configureRuntime(runtime, provider);
|
||||
});
|
||||
};
|
||||
|
||||
return *runtime;
|
||||
}
|
||||
|
||||
@@ -658,7 +658,11 @@ namespace hex {
|
||||
#if defined(OS_WINDOWS)
|
||||
return "Windows";
|
||||
#elif defined(OS_LINUX)
|
||||
return "Linux";
|
||||
#if defined(OS_FREEBSD)
|
||||
return "FreeBSD";
|
||||
#else
|
||||
return "Linux";
|
||||
#endif
|
||||
#elif defined(OS_MACOS)
|
||||
return "macOS";
|
||||
#elif defined(OS_WEB)
|
||||
@@ -915,7 +919,7 @@ namespace hex {
|
||||
};
|
||||
}
|
||||
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
|
||||
wolv::io::File fontFile(path, wolv::io::File::Mode::Read);
|
||||
if (!fontFile.isValid()) {
|
||||
log::error("Failed to load font from file '{}'", wolv::util::toUTF8String(path));
|
||||
@@ -927,17 +931,19 @@ namespace hex {
|
||||
fontFile.readVector(),
|
||||
glyphRanges,
|
||||
offset,
|
||||
flags
|
||||
flags,
|
||||
defaultSize
|
||||
});
|
||||
}
|
||||
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
|
||||
impl::s_fonts->emplace_back(Font {
|
||||
name,
|
||||
{ data.begin(), data.end() },
|
||||
glyphRanges,
|
||||
offset,
|
||||
flags
|
||||
flags,
|
||||
defaultSize
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -412,13 +412,15 @@ namespace hex {
|
||||
void TaskManager::runDeferredCalls() {
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
|
||||
for (const auto &call : s_deferredCalls)
|
||||
call();
|
||||
for (const auto &[location, call] : s_onceDeferredCalls)
|
||||
call();
|
||||
|
||||
s_deferredCalls.clear();
|
||||
s_onceDeferredCalls.clear();
|
||||
while (!s_deferredCalls.empty()) {
|
||||
auto callback = s_deferredCalls.front();
|
||||
s_deferredCalls.pop_front();
|
||||
callback();
|
||||
}
|
||||
while (!s_onceDeferredCalls.empty()) {
|
||||
auto node = s_onceDeferredCalls.extract(s_onceDeferredCalls.begin());
|
||||
node.mapped()();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::runWhenTasksFinished(const std::function<void()> &function) {
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
#include <shellapi.h>
|
||||
#elif defined(OS_LINUX) || defined(OS_WEB)
|
||||
#include <xdg.hpp>
|
||||
# if defined(OS_FREEBSD)
|
||||
#include <sys/syslimits.h>
|
||||
# else
|
||||
#include <limits.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(OS_WEB)
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace hex::log {
|
||||
s_loggerFile.disableBuffering();
|
||||
|
||||
if (s_loggerFile.isValid()) {
|
||||
s_colorOutputEnabled = true;
|
||||
s_colorOutputEnabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ else ()
|
||||
target_link_libraries(main PRIVATE pthread)
|
||||
endif ()
|
||||
|
||||
precompileHeaders(main ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
if (APPLE)
|
||||
add_compile_definitions(GL_SILENCE_DEPRECATION)
|
||||
endif ()
|
||||
@@ -238,7 +238,7 @@ namespace hex::init {
|
||||
|
||||
|
||||
FrameResult WindowSplash::fullFrame() {
|
||||
glfwSetWindowSize(m_window, 640_scaled, 400_scaled);
|
||||
glfwSetWindowSize(m_window, 640, 400);
|
||||
centerWindow(m_window);
|
||||
|
||||
glfwPollEvents();
|
||||
@@ -248,23 +248,21 @@ namespace hex::init {
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
auto scale = ImHexApi::System::getGlobalScale();
|
||||
|
||||
// Draw the splash screen background
|
||||
auto drawList = ImGui::GetBackgroundDrawList();
|
||||
{
|
||||
|
||||
// Draw the splash screen background
|
||||
drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize() * scale);
|
||||
drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize());
|
||||
|
||||
{
|
||||
|
||||
// Function to highlight a given number of bytes at a position in the splash screen
|
||||
const auto highlightBytes = [&](ImVec2 start, size_t count, ImColor color, float opacity) {
|
||||
// Dimensions and number of bytes that are drawn. Taken from the splash screen image
|
||||
const auto hexSize = ImVec2(29, 18) * scale;
|
||||
const auto hexSpacing = ImVec2(17.4, 15) * scale;
|
||||
const auto hexStart = ImVec2(27, 127) * scale;
|
||||
const auto hexSize = ImVec2(29, 18);
|
||||
const auto hexSpacing = ImVec2(17.4, 15);
|
||||
const auto hexStart = ImVec2(27, 127);
|
||||
|
||||
constexpr auto HexCount = ImVec2(13, 7);
|
||||
|
||||
@@ -312,10 +310,10 @@ namespace hex::init {
|
||||
this->progressLerp += (m_progress - this->progressLerp) * 0.1F;
|
||||
|
||||
// Draw the splash screen foreground
|
||||
drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize() * scale);
|
||||
drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize());
|
||||
|
||||
// Draw the "copyright" notice
|
||||
drawList->AddText(ImVec2(35, 85) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str());
|
||||
drawList->AddText(ImVec2(35, 85), ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str());
|
||||
|
||||
// Draw version information
|
||||
// In debug builds, also display the current commit hash and branch
|
||||
@@ -325,18 +323,18 @@ namespace hex::init {
|
||||
const static auto VersionInfo = hex::format("{0}", ImHexApi::System::getImHexVersion());
|
||||
#endif
|
||||
|
||||
drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x * scale - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105 * scale), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str());
|
||||
drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str());
|
||||
}
|
||||
|
||||
// Draw the task progress bar
|
||||
{
|
||||
std::lock_guard guard(m_progressMutex);
|
||||
|
||||
const auto progressBackgroundStart = ImVec2(99, 357) * scale;
|
||||
const auto progressBackgroundSize = ImVec2(442, 30) * scale;
|
||||
const auto progressBackgroundStart = ImVec2(99, 357);
|
||||
const auto progressBackgroundSize = ImVec2(442, 30);
|
||||
|
||||
const auto progressStart = progressBackgroundStart + ImVec2(0, 20) * scale;
|
||||
const auto progressSize = ImVec2(progressBackgroundSize.x * m_progress, 10 * scale);
|
||||
const auto progressStart = progressBackgroundStart + ImVec2(0, 20);
|
||||
const auto progressSize = ImVec2(progressBackgroundSize.x * m_progress, 10);
|
||||
|
||||
// Draw progress bar
|
||||
drawList->AddRectFilled(progressStart, progressStart + progressSize, 0xD0FFFFFF);
|
||||
@@ -344,7 +342,7 @@ namespace hex::init {
|
||||
// Draw task names separated by | characters
|
||||
if (!m_currTaskNames.empty()) {
|
||||
drawList->PushClipRect(progressBackgroundStart, progressBackgroundStart + progressBackgroundSize, true);
|
||||
drawList->AddText(progressStart + ImVec2(5, -20) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(m_currTaskNames, " | ")).c_str());
|
||||
drawList->AddText(progressStart + ImVec2(5, -20), ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(m_currTaskNames, " | ")).c_str());
|
||||
drawList->PopClipRect();
|
||||
}
|
||||
}
|
||||
@@ -492,7 +490,7 @@ namespace hex::init {
|
||||
|
||||
ImFontConfig cfg;
|
||||
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
|
||||
cfg.SizePixels = 13.0_scaled;
|
||||
cfg.SizePixels = ImHexApi::Fonts::DefaultFontSize;
|
||||
io.Fonts->AddFontDefault(&cfg);
|
||||
|
||||
std::uint8_t *px;
|
||||
|
||||
@@ -126,6 +126,7 @@ namespace hex {
|
||||
throw;
|
||||
} catch (const std::exception &e) {
|
||||
log::fatal("Unhandled exception: {}", e.what());
|
||||
EventCrashRecovered::post(e);
|
||||
} catch (...) {
|
||||
log::fatal("Unhandled exception: Unknown exception");
|
||||
}
|
||||
@@ -133,26 +134,47 @@ namespace hex {
|
||||
|
||||
void errorRecoverLogCallback(void*, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
std::string message;
|
||||
message.resize(std::vsnprintf(nullptr, 0, fmt, args));
|
||||
std::vsnprintf(message.data(), message.size(), fmt, args);
|
||||
message.resize(message.size() - 1);
|
||||
|
||||
va_start(args, fmt);
|
||||
message.resize(std::vsnprintf(nullptr, 0, fmt, args));
|
||||
va_end(args);
|
||||
|
||||
va_start(args, fmt);
|
||||
std::vsnprintf(message.data(), message.size(), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
message.resize(message.size() - 1);
|
||||
|
||||
log::error("{}", message);
|
||||
}
|
||||
|
||||
void Window::fullFrame() {
|
||||
static u32 crashWatchdog = 0;
|
||||
|
||||
try {
|
||||
this->frameBegin();
|
||||
this->frame();
|
||||
this->frameEnd();
|
||||
|
||||
// Feed the watchdog
|
||||
crashWatchdog = 0;
|
||||
} catch (...) {
|
||||
// If an exception keeps being thrown, abort the application after 10 frames
|
||||
// This is done to avoid the application getting stuck in an infinite loop of exceptions
|
||||
crashWatchdog += 1;
|
||||
if (crashWatchdog > 10) {
|
||||
log::fatal("Crash watchdog triggered, aborting");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
// Try to recover from the exception by bringing ImGui back into a working state
|
||||
ImGui::ErrorCheckEndFrameRecover(errorRecoverLogCallback, nullptr);
|
||||
ImGui::EndFrame();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
|
||||
// Handle the exception
|
||||
handleException();
|
||||
}
|
||||
}
|
||||
@@ -165,10 +187,9 @@ namespace hex {
|
||||
bool shouldLongSleep = !m_unlockFrameRate;
|
||||
|
||||
// Wait 5 frames before actually enabling the long sleep mode to make animations not stutter
|
||||
constexpr static auto LongSleepTimeout = 5;
|
||||
static i32 lockTimeout = 0;
|
||||
if (!shouldLongSleep) {
|
||||
lockTimeout = LongSleepTimeout;
|
||||
lockTimeout = m_lastFrameTime * 10'000;
|
||||
} else if (lockTimeout > 0) {
|
||||
lockTimeout -= 1;
|
||||
}
|
||||
@@ -192,6 +213,7 @@ namespace hex {
|
||||
constexpr static auto LongSleepFPS = 5.0;
|
||||
const double timeout = std::max(0.0, (1.0 / LongSleepFPS) - (glfwGetTime() - m_lastStartFrameTime));
|
||||
|
||||
glfwPollEvents();
|
||||
glfwWaitEventsTimeout(timeout);
|
||||
} else {
|
||||
glfwPollEvents();
|
||||
@@ -567,29 +589,54 @@ namespace hex {
|
||||
// Finalize ImGui frame
|
||||
ImGui::Render();
|
||||
|
||||
// Hash the draw data to determine if anything changed on the screen
|
||||
// Compare the previous frame buffer to the current one to determine if the window content has changed
|
||||
// If not, there's no point in sending the draw data off to the GPU and swapping buffers
|
||||
// NOTE: For anybody looking at this code and thinking "why not just hash the buffer and compare the hashes",
|
||||
// the reason is that hashing the buffer is significantly slower than just comparing the buffers directly.
|
||||
// The buffer might become quite large if there's a lot of vertices on the screen but it's still usually less than
|
||||
// 10MB (out of which only the active portion needs to actually be compared) which is worth the ~60x speedup.
|
||||
bool shouldRender = false;
|
||||
{
|
||||
u32 drawDataHash = 0;
|
||||
static u32 previousDrawDataHash = 0;
|
||||
static std::vector<u8> previousVtxData;
|
||||
static size_t previousVtxDataSize = 0;
|
||||
|
||||
size_t offset = 0;
|
||||
size_t vtxDataSize = 0;
|
||||
|
||||
for (const auto viewPort : ImGui::GetPlatformIO().Viewports) {
|
||||
auto drawData = viewPort->DrawData;
|
||||
for (int n = 0; n < drawData->CmdListsCount; n++) {
|
||||
const ImDrawList *cmdList = drawData->CmdLists[n];
|
||||
drawDataHash = ImHashData(cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert), drawDataHash);
|
||||
vtxDataSize += drawData->CmdLists[n]->VtxBuffer.size() * sizeof(ImDrawVert);
|
||||
}
|
||||
}
|
||||
for (const auto viewPort : ImGui::GetPlatformIO().Viewports) {
|
||||
auto drawData = viewPort->DrawData;
|
||||
for (int n = 0; n < drawData->CmdListsCount; n++) {
|
||||
const ImDrawList *cmdList = drawData->CmdLists[n];
|
||||
drawDataHash = ImHashData(cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx), drawDataHash);
|
||||
|
||||
if (vtxDataSize == previousVtxDataSize) {
|
||||
shouldRender = shouldRender || std::memcmp(previousVtxData.data() + offset, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.size() * sizeof(ImDrawVert)) != 0;
|
||||
} else {
|
||||
shouldRender = true;
|
||||
}
|
||||
|
||||
if (previousVtxData.size() < offset + cmdList->VtxBuffer.size() * sizeof(ImDrawVert)) {
|
||||
previousVtxData.resize(offset + cmdList->VtxBuffer.size() * sizeof(ImDrawVert));
|
||||
}
|
||||
|
||||
std::memcpy(previousVtxData.data() + offset, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.size() * sizeof(ImDrawVert));
|
||||
offset += cmdList->VtxBuffer.size() * sizeof(ImDrawVert);
|
||||
}
|
||||
}
|
||||
|
||||
shouldRender = drawDataHash != previousDrawDataHash;
|
||||
previousDrawDataHash = drawDataHash;
|
||||
previousVtxDataSize = vtxDataSize;
|
||||
}
|
||||
|
||||
GLFWwindow *backupContext = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backupContext);
|
||||
|
||||
if (shouldRender) {
|
||||
int displayWidth, displayHeight;
|
||||
glfwGetFramebufferSize(m_window, &displayWidth, &displayHeight);
|
||||
@@ -603,11 +650,6 @@ namespace hex {
|
||||
m_unlockFrameRate = true;
|
||||
}
|
||||
|
||||
GLFWwindow *backupContext = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backupContext);
|
||||
|
||||
// Process layout load requests
|
||||
// NOTE: This needs to be done before a new frame is started, otherwise ImGui won't handle docking correctly
|
||||
LayoutManager::process();
|
||||
|
||||
@@ -61,6 +61,8 @@ std::optional<std::fs::path> downloadUpdate(const std::string &url) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
hex::log::info("Writing update to file: {}", file.getPath().string());
|
||||
|
||||
// Write the downloaded update data to the file
|
||||
file.writeVector(data);
|
||||
|
||||
@@ -97,7 +99,7 @@ int installUpdate(const std::string &type, std::fs::path updatePath) {
|
||||
};
|
||||
|
||||
constexpr static auto UpdateHandlers = {
|
||||
UpdateHandler { "win-msi", ".msi", "msiexec /fa {} /passive" },
|
||||
UpdateHandler { "win-msi", ".msi", "msiexec /passive /package {}" },
|
||||
UpdateHandler { "macos-dmg", ".dmg", "hdiutil attach {}" },
|
||||
UpdateHandler { "linux-deb-22.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" },
|
||||
UpdateHandler { "linux-deb-23.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" },
|
||||
@@ -108,10 +110,14 @@ int installUpdate(const std::string &type, std::fs::path updatePath) {
|
||||
// Rename the update file to the correct extension
|
||||
const auto originalPath = updatePath;
|
||||
updatePath.replace_extension(handler.extension);
|
||||
|
||||
hex::log::info("Moving update package from {} to {}", originalPath.string(), updatePath.string());
|
||||
std::fs::rename(originalPath, updatePath);
|
||||
|
||||
// Install the update using the correct command
|
||||
hex::startProgram(hex::format(handler.command, updatePath.string()));
|
||||
const auto command = hex::format(handler.command, updatePath.string());
|
||||
hex::log::info("Starting update process with command: '{}'", command);
|
||||
hex::startProgram(command);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -163,6 +169,8 @@ int main(int argc, char **argv) {
|
||||
if (!updatePath.has_value())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
hex::log::info("Downloaded update successfully");
|
||||
|
||||
// Install the update
|
||||
return installUpdate(updateType, *updatePath);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/ui/popup.hpp>
|
||||
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class PopupCrashRecovered : public Popup<PopupCrashRecovered> {
|
||||
public:
|
||||
PopupCrashRecovered(const std::exception &e)
|
||||
: hex::Popup<PopupCrashRecovered>("hex.builtin.popup.crash_recover.title", false),
|
||||
m_errorType(typeid(e).name()),
|
||||
m_errorMessage(e.what()) { }
|
||||
|
||||
void drawContent() override {
|
||||
ImGuiExt::TextFormattedWrapped("hex.builtin.popup.crash_recover.message"_lang);
|
||||
|
||||
ImGuiExt::TextFormattedWrapped(hex::format("Error: {}: {}", llvm::itaniumDemangle(this->m_errorType), this->m_errorMessage));
|
||||
|
||||
if (ImGui::Button("hex.ui.common.okay"_lang)) {
|
||||
this->close();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
|
||||
return ImGuiWindowFlags_AlwaysAutoResize;
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 100 });
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMaxSize() const override {
|
||||
return scaled({ 600, 300 });
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_errorType, m_errorMessage;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(OS_WINDOWS) || defined (OS_LINUX)
|
||||
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
@@ -46,11 +46,13 @@ namespace hex::plugin::builtin {
|
||||
return m_provider->isSavable();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
|
||||
|
||||
void save() override {
|
||||
m_provider->save();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool open() override { return true; }
|
||||
[[nodiscard]] bool open() override { return m_provider != this; }
|
||||
void close() override { }
|
||||
|
||||
void resizeRaw(u64 newSize) override {
|
||||
@@ -101,8 +103,31 @@ namespace hex::plugin::builtin {
|
||||
return m_provider->getDataDescription();
|
||||
}
|
||||
|
||||
void loadSettings(const nlohmann::json &settings) override { hex::unused(settings); }
|
||||
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override { return settings; }
|
||||
void loadSettings(const nlohmann::json &settings) override {
|
||||
Provider::loadSettings(settings);
|
||||
|
||||
auto id = settings.at("id").get<u64>();
|
||||
m_startAddress = settings.at("start_address").get<u64>();
|
||||
m_size = settings.at("size").get<size_t>();
|
||||
|
||||
const auto &providers = ImHexApi::Provider::getProviders();
|
||||
auto provider = std::ranges::find_if(providers, [id](const prv::Provider *provider) {
|
||||
return provider->getID() == id;
|
||||
});
|
||||
|
||||
if (provider == providers.end())
|
||||
return;
|
||||
|
||||
m_provider = *provider;
|
||||
}
|
||||
|
||||
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override {
|
||||
settings["id"] = m_provider->getID();
|
||||
settings["start_address"] = m_startAddress;
|
||||
settings["size"] = m_size;
|
||||
|
||||
return Provider::storeSettings(settings);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override {
|
||||
return "hex.builtin.provider.view";
|
||||
|
||||
@@ -374,6 +374,8 @@
|
||||
"hex.builtin.popup.exit_application.desc": "You have unsaved changes made to your Project.\nAre you sure you want to exit?",
|
||||
"hex.builtin.popup.exit_application.title": "Exit Application?",
|
||||
"hex.builtin.popup.waiting_for_tasks.title": "Waiting for Tasks",
|
||||
"hex.builtin.popup.crash_recover.title": "Crash recovery",
|
||||
"hex.builtin.popup.crash_recover.message": "An exception was thrown, but ImHex was able to catch it and advert a crash",
|
||||
"hex.builtin.popup.blocking_task.title": "Running Task",
|
||||
"hex.builtin.popup.blocking_task.desc": "A task is currently executing.",
|
||||
"hex.builtin.popup.save_layout.title": "Save Layout",
|
||||
@@ -498,7 +500,7 @@
|
||||
"hex.builtin.setting.toolbar.icons": "Toolbar Icons",
|
||||
"hex.builtin.shortcut.next_provider": "Select next provider",
|
||||
"hex.builtin.shortcut.prev_provider": "Select previous provider",
|
||||
"hex.builtin.title_bar_button.debug_build": "Debug build",
|
||||
"hex.builtin.title_bar_button.debug_build": "Debug build\nShift+Click to crash using exception\nCtrl+Click to crash using signal",
|
||||
"hex.builtin.title_bar_button.feedback": "Leave Feedback",
|
||||
"hex.builtin.tools.ascii_table": "ASCII table",
|
||||
"hex.builtin.tools.ascii_table.octal": "Show octal",
|
||||
|
||||
@@ -21,10 +21,19 @@
|
||||
#include <popups/popup_question.hpp>
|
||||
#include <content/popups/popup_tasks_waiting.hpp>
|
||||
#include <content/popups/popup_unsaved_changes.hpp>
|
||||
#include <content/popups/popup_crash_recovered.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
static void openFile(const std::fs::path &path) {
|
||||
if (path.extension() == ".hexproj") {
|
||||
if (!ProjectFile::load(path)) {
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
|
||||
if (auto *fileProvider = dynamic_cast<FileProvider*>(provider); fileProvider != nullptr) {
|
||||
fileProvider->setPath(path);
|
||||
@@ -33,6 +42,7 @@ namespace hex::plugin::builtin {
|
||||
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
|
||||
} else {
|
||||
EventProviderOpened::post(fileProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +50,10 @@ namespace hex::plugin::builtin {
|
||||
void registerEventHandlers() {
|
||||
|
||||
static bool imhexClosing = false;
|
||||
EventCrashRecovered::subscribe([](const std::exception &e) {
|
||||
PopupCrashRecovered::open(e);
|
||||
});
|
||||
|
||||
EventWindowClosing::subscribe([](GLFWwindow *window) {
|
||||
imhexClosing = false;
|
||||
if (ImHexApi::Provider::isDirty() && !imhexClosing) {
|
||||
@@ -91,9 +105,6 @@ namespace hex::plugin::builtin {
|
||||
EventProviderOpened::subscribe([](hex::prv::Provider *provider) {
|
||||
if (provider != nullptr && ImHexApi::Provider::get() == provider) {
|
||||
RequestUpdateWindowTitle::post();
|
||||
|
||||
if (!provider->isWritable())
|
||||
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,23 +122,24 @@ namespace hex::plugin::builtin {
|
||||
if (path.extension() == ".hexproj") {
|
||||
if (!ProjectFile::load(path)) {
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
|
||||
}
|
||||
} else {
|
||||
auto newProvider = static_cast<FileProvider*>(
|
||||
ImHexApi::Provider::createProvider("hex.builtin.provider.file", true)
|
||||
);
|
||||
|
||||
if (newProvider == nullptr)
|
||||
return;
|
||||
|
||||
newProvider->setPath(path);
|
||||
if (!newProvider->open()) {
|
||||
hex::ImHexApi::Provider::remove(newProvider);
|
||||
} else {
|
||||
EventProviderOpened::post(newProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto newProvider = static_cast<FileProvider*>(
|
||||
ImHexApi::Provider::createProvider("hex.builtin.provider.file", true)
|
||||
);
|
||||
|
||||
if (newProvider == nullptr)
|
||||
return;
|
||||
|
||||
newProvider->setPath(path);
|
||||
if (!newProvider->open()) {
|
||||
hex::ImHexApi::Provider::remove(newProvider);
|
||||
} else {
|
||||
EventProviderOpened::post(newProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
|
||||
}
|
||||
}, {}, true);
|
||||
} else if (name == "Open Project") {
|
||||
@@ -239,6 +251,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopup))
|
||||
return;
|
||||
if (ImGui::IsAnyItemHovered())
|
||||
return;
|
||||
|
||||
static ImGuiWindow *lastFocusedWindow = nullptr;
|
||||
|
||||
|
||||
@@ -278,6 +278,13 @@ namespace hex::plugin::builtin {
|
||||
|
||||
// Disable merge mode for this font but retain the rest of the configuration
|
||||
cfg.MergeMode = false;
|
||||
|
||||
auto size = fontSize;
|
||||
if (font.defaultSize.has_value())
|
||||
size = font.defaultSize.value() * ImHexApi::System::getGlobalScale();
|
||||
|
||||
cfg.SizePixels = size;
|
||||
|
||||
ON_SCOPE_EXIT { cfg.MergeMode = true; };
|
||||
|
||||
// Construct a range that only contains the first glyph of the font
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace hex::plugin::builtin {
|
||||
std::vector<u8> bytes(5_MiB);
|
||||
|
||||
auto selection = ImHexApi::HexEditor::getSelection();
|
||||
for (u64 address = selection->getStartAddress(); address <= selection->getEndAddress(); address += bytes.size()) {
|
||||
for (u64 address = selection->getStartAddress(); address < selection->getEndAddress(); address += bytes.size()) {
|
||||
bytes.resize(std::min<u64>(bytes.size(), selection->getEndAddress() - address));
|
||||
provider->read(address, bytes.data(), bytes.size());
|
||||
|
||||
@@ -416,7 +416,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
/* Import */
|
||||
{
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.file", "hex.builtin.menu.file.import" }, ICON_VS_SIGN_IN, 2140, []{}, noRunningTaskAndWritableProvider);
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.file", "hex.builtin.menu.file.import" }, ICON_VS_SIGN_IN, 2140, []{}, noRunningTaskAndValidProvider);
|
||||
|
||||
/* IPS */
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.ips"}, ICON_VS_GIT_PULL_REQUEST_NEW_CHANGES, 2150,
|
||||
|
||||
@@ -348,6 +348,8 @@ namespace hex::plugin::builtin {
|
||||
// Draw deny button
|
||||
ImGui::SetCursorPosX(buttonPos(2));
|
||||
if (ImGui::Button("hex.ui.common.deny"_lang, buttonSize)) {
|
||||
ContentRegistry::Settings::write<int>("hex.builtin.setting.general", "hex.builtin.setting.general.server_contact", 0);
|
||||
ContentRegistry::Settings::write<int>("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", 0);
|
||||
page += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::Provider::add<MemoryFileProvider>(false);
|
||||
ContentRegistry::Provider::add<ViewProvider>(false);
|
||||
|
||||
#if defined(OS_WINDOWS) ||defined (OS_LINUX)
|
||||
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
|
||||
ContentRegistry::Provider::add<ProcessMemoryProvider>();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
#elif defined(OS_LINUX)
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/fs.h>
|
||||
#if !defined(OS_FREEBSD)
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
@@ -41,8 +43,11 @@
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
#define lseek lseek64
|
||||
#if defined(OS_LINUX) && !defined(OS_FREEBSD)
|
||||
#define lseek lseek64
|
||||
#elif defined(OS_FREEBSD)
|
||||
#include <sys/disk.h>
|
||||
#define DEFAULT_SECTOR_SIZE 512
|
||||
#endif
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
@@ -83,6 +88,12 @@ namespace hex::plugin::builtin {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(OS_FREEBSD) && defined(DIOCGSECTORSIZE)
|
||||
int blkdev_get_sector_size(int fd, int *sector_size) {
|
||||
if (ioctl(fd, DIOCGSECTORSIZE, sector_size) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int blkdev_get_sector_size(int fd, int *sector_size) {
|
||||
(void)fd;
|
||||
@@ -97,6 +108,12 @@ namespace hex::plugin::builtin {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(OS_FREEBSD) && defined(DIOCGMEDIASIZE)
|
||||
int blkdev_get_size(int fd, u64 *bytes) {
|
||||
if (ioctl(fd, DIOCGMEDIASIZE, bytes) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int blkdev_get_size(int fd, u64 *bytes) {
|
||||
struct stat st;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "content/providers/file_provider.hpp"
|
||||
#include "content/providers/memory_file_provider.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <fmt/chrono.h>
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
@@ -86,6 +87,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
void FileProvider::resizeRaw(u64 newSize) {
|
||||
m_file.setSize(newSize);
|
||||
m_fileSize = newSize;
|
||||
}
|
||||
|
||||
void FileProvider::insertRaw(u64 offset, u64 size) {
|
||||
@@ -207,7 +209,7 @@ namespace hex::plugin::builtin {
|
||||
m_readable = true;
|
||||
m_writable = true;
|
||||
|
||||
if (!std::fs::exists(m_path)) {
|
||||
if (!wolv::io::fs::exists(m_path)) {
|
||||
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(ENOENT)));
|
||||
return false;
|
||||
}
|
||||
@@ -222,6 +224,8 @@ namespace hex::plugin::builtin {
|
||||
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
|
||||
}
|
||||
|
||||
m_file = std::move(file);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if defined(OS_WINDOWS) || defined (OS_LINUX)
|
||||
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
|
||||
|
||||
#include <content/providers/process_memory_provider.hpp>
|
||||
|
||||
|
||||
@@ -219,7 +219,9 @@ namespace hex::plugin::builtin::recent {
|
||||
void loadRecentEntry(const RecentEntry &recentEntry) {
|
||||
if (recentEntry.type == "project") {
|
||||
std::fs::path projectPath = recentEntry.data["path"].get<std::string>();
|
||||
ProjectFile::load(projectPath);
|
||||
if (!ProjectFile::load(projectPath)) {
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(projectPath)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto *provider = ImHexApi::Provider::createProvider(recentEntry.type, true);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
|
||||
#include <fonts/codicons_font.h>
|
||||
#include <imgui.h>
|
||||
|
||||
@@ -235,32 +235,20 @@ namespace hex::plugin::builtin {
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
void ViewAbout::drawContributorPage() {
|
||||
struct Contributor {
|
||||
const char *name;
|
||||
const char *description;
|
||||
const char *link;
|
||||
bool mainContributor;
|
||||
};
|
||||
|
||||
constexpr static std::array Contributors = {
|
||||
Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true },
|
||||
Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/Nowilltolife", true },
|
||||
Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false },
|
||||
Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false },
|
||||
Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false },
|
||||
Contributor { "All other amazing contributors", "Being part of the community, opening issues, PRs and donating", "https://github.com/WerWolv/ImHex/graphs/contributors", false }
|
||||
};
|
||||
|
||||
ImGuiExt::TextFormattedWrapped("These amazing people have contributed some incredible things to ImHex in the past.\nConsider opening a PR on the Git Repository to take your place among them!");
|
||||
ImGui::NewLine();
|
||||
struct Contributor {
|
||||
const char *name;
|
||||
const char *description;
|
||||
const char *link;
|
||||
bool mainContributor;
|
||||
};
|
||||
|
||||
static void drawContributorTable(const char *title, const auto &contributors) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
ImGuiExt::BeginSubWindow("Contributors", ImVec2(ImGui::GetContentRegionAvail().x, 0), ImGuiChildFlags_AutoResizeX);
|
||||
ImGuiExt::BeginSubWindow(title, ImVec2(ImGui::GetContentRegionAvail().x, 0), ImGuiChildFlags_AutoResizeX);
|
||||
ImGui::PopStyleVar();
|
||||
{
|
||||
if (ImGui::BeginTable("Contributors", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
|
||||
for (const auto &contributor : Contributors) {
|
||||
if (ImGui::BeginTable(title, 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
|
||||
for (const auto &contributor : contributors) {
|
||||
ImGui::TableNextRow();
|
||||
if (contributor.mainContributor) {
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_PlotHistogram) & 0x1FFFFFFF);
|
||||
@@ -270,10 +258,11 @@ namespace hex::plugin::builtin {
|
||||
if (ImGuiExt::Hyperlink(contributor.name))
|
||||
hex::openWebpage(contributor.link);
|
||||
|
||||
ImGui::Indent();
|
||||
ImGui::TextUnformatted(contributor.description);
|
||||
ImGui::Unindent();
|
||||
|
||||
if (contributor.description[0] != '\0') {
|
||||
ImGui::Indent();
|
||||
ImGui::TextUnformatted(contributor.description);
|
||||
ImGui::Unindent();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@@ -282,6 +271,35 @@ namespace hex::plugin::builtin {
|
||||
ImGuiExt::EndSubWindow();
|
||||
}
|
||||
|
||||
void ViewAbout::drawContributorPage() {
|
||||
constexpr static std::array Contributors = {
|
||||
Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true },
|
||||
Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/jumanji144", true },
|
||||
Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false },
|
||||
Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false },
|
||||
Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false },
|
||||
Contributor { "All other amazing contributors", "Being part of the community, opening issues, PRs and donating", "https://github.com/WerWolv/ImHex/graphs/contributors", false }
|
||||
};
|
||||
|
||||
constexpr static std::array Testers = {
|
||||
Contributor { "Nemoumbra", "Breaking my code literal seconds after I push it", "https://github.com/Nemoumbra", true },
|
||||
Contributor { "Berylskid", "", "https://github.com/Berylskid", false },
|
||||
Contributor { "Jan Polak", "", "https://github.com/polak-jan", false },
|
||||
Contributor { "Ken-Kaneki", "", "https://github.com/loneicewolf", false },
|
||||
Contributor { "Everybody who has reported issues", "Helping me find bugs and improve the software", "https://github.com/WerWolv/ImHex/issues", false }
|
||||
|
||||
};
|
||||
|
||||
ImGuiExt::TextFormattedWrapped("These amazing people have contributed some incredible things to ImHex in the past.\nConsider opening a PR on the Git Repository to take your place among them!");
|
||||
ImGui::NewLine();
|
||||
drawContributorTable("Contributors", Contributors);
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGuiExt::TextFormattedWrapped("All of these great people made ImHex work much much smoother.\nConsider joining our Tester team to help making ImHex better for everyone!");
|
||||
ImGui::NewLine();
|
||||
drawContributorTable("Testers", Testers);
|
||||
}
|
||||
|
||||
void ViewAbout::drawLibraryCreditsPage() {
|
||||
struct Library {
|
||||
const char *name;
|
||||
|
||||
@@ -313,8 +313,8 @@ namespace hex::plugin::builtin {
|
||||
u64 droppedBookmarkId = *static_cast<const u64*>(payload->Data);
|
||||
|
||||
// Find the correct bookmark with that id
|
||||
auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmark) {
|
||||
return bookmark.entry.id == droppedBookmarkId;
|
||||
auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmarkItem) {
|
||||
return bookmarkItem.entry.id == droppedBookmarkId;
|
||||
});
|
||||
|
||||
// Swap the two bookmarks
|
||||
@@ -412,16 +412,25 @@ namespace hex::plugin::builtin {
|
||||
u64 end = region.getEndAddress();
|
||||
|
||||
if (!locked) {
|
||||
bool updated = false;
|
||||
|
||||
ImGui::PushItemWidth(100_scaled);
|
||||
ImGuiExt::InputHexadecimal("##begin", &begin);
|
||||
if (ImGuiExt::InputHexadecimal("##begin", &begin))
|
||||
updated = true;
|
||||
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted(" - ");
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGuiExt::InputHexadecimal("##end", &end);
|
||||
|
||||
if (ImGuiExt::InputHexadecimal("##end", &end))
|
||||
updated = true;
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (end > begin)
|
||||
if (updated && end > begin) {
|
||||
region = Region(begin, end - begin + 1);
|
||||
EventHighlightingChanged::post();
|
||||
}
|
||||
} else {
|
||||
ImGuiExt::TextFormatted("0x{:02X} - 0x{:02X}", begin, end);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace hex::plugin::builtin {
|
||||
m_validBytes = 0;
|
||||
m_selectedProvider = nullptr;
|
||||
} else {
|
||||
m_validBytes = u64(region.getProvider()->getActualSize() - region.address);
|
||||
m_validBytes = u64((region.getProvider()->getBaseAddress() + region.getProvider()->getActualSize()) - region.address);
|
||||
m_startAddress = region.address;
|
||||
m_selectedProvider = region.getProvider();
|
||||
}
|
||||
|
||||
@@ -1050,7 +1050,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::SetClipboardText(
|
||||
callback(
|
||||
provider,
|
||||
selection->getStartAddress() + provider->getBaseAddress() + provider->getCurrentPageAddress(),
|
||||
selection->getStartAddress(),
|
||||
selection->size
|
||||
).c_str()
|
||||
);
|
||||
|
||||
@@ -133,6 +133,9 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
static void drawVirtualFileTree(const std::vector<const ViewPatternEditor::VirtualFile*> &virtualFiles, u32 level = 0) {
|
||||
ImGui::PushID(level + 1);
|
||||
ON_SCOPE_EXIT { ImGui::PopID(); };
|
||||
|
||||
std::map<std::string, std::vector<const ViewPatternEditor::VirtualFile*>> currFolderEntries;
|
||||
for (const auto &file : virtualFiles) {
|
||||
const auto &path = file->path;
|
||||
@@ -146,8 +149,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::TreeNodeEx(currSegment.c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen);
|
||||
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()) {
|
||||
ImHexApi::Provider::add<prv::MemoryProvider>(file->data, wolv::util::toUTF8String(file->path.filename()));
|
||||
}
|
||||
|
||||
@@ -157,6 +159,7 @@ namespace hex::plugin::builtin {
|
||||
currFolderEntries[currSegment].emplace_back(file);
|
||||
}
|
||||
|
||||
int id = 1;
|
||||
for (const auto &[segment, entries] : currFolderEntries) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
@@ -167,11 +170,16 @@ namespace hex::plugin::builtin {
|
||||
ImGui::TextUnformatted(ICON_VS_FOLDER);
|
||||
}
|
||||
|
||||
ImGui::PushID(id);
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::TreeNodeEx(segment.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) {
|
||||
drawVirtualFileTree(entries, level + 1);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1158,7 +1166,7 @@ namespace hex::plugin::builtin {
|
||||
for (const auto &file : virtualFiles)
|
||||
virtualFilePointers.emplace_back(&file);
|
||||
|
||||
if (ImGui::BeginTable("Virtual File Tree", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg, size)) {
|
||||
if (ImGui::BeginTable("Virtual File Tree", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, size)) {
|
||||
ImGui::TableSetupColumn("##path", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
drawVirtualFileTree(virtualFilePointers);
|
||||
@@ -1305,7 +1313,7 @@ namespace hex::plugin::builtin {
|
||||
pl::PatternLanguage runtime;
|
||||
ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);
|
||||
|
||||
auto mimeType = magic::getMIMEType(provider, true);
|
||||
auto mimeType = magic::getMIMEType(provider, 0, 100_KiB, true);
|
||||
|
||||
bool foundCorrectType = false;
|
||||
runtime.addPragma("MIME", [&mimeType, &foundCorrectType](const pl::PatternLanguage &runtime, const std::string &value) {
|
||||
@@ -1392,12 +1400,8 @@ namespace hex::plugin::builtin {
|
||||
if (!file.isValid())
|
||||
continue;
|
||||
|
||||
auto &preprocessor = runtime.getInternals().preprocessor;
|
||||
|
||||
pl::api::Source source(file.readString());
|
||||
|
||||
auto ret = preprocessor->preprocess(&runtime, &source);
|
||||
if (ret.hasErrs()) {
|
||||
auto result = runtime.preprocessString(file.readString(), pl::api::Source::DefaultSource);
|
||||
if (!result.has_value()) {
|
||||
log::warn("Failed to preprocess file {} during MIME analysis", entry.path().string());
|
||||
}
|
||||
|
||||
@@ -1542,6 +1546,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
m_sectionWindowDrawer.clear();
|
||||
m_consoleEditor.SetText("");
|
||||
m_virtualFiles->clear();
|
||||
|
||||
m_accessHistory = {};
|
||||
m_accessHistoryIndex = 0;
|
||||
|
||||
@@ -198,7 +198,6 @@ namespace hex::plugin::builtin {
|
||||
ImGui::BeginDisabled(m_updateAllTask.isRunning() || m_updateCount == 0);
|
||||
if (ImGuiExt::IconButton(ICON_VS_CLOUD_DOWNLOAD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
m_updateAllTask = TaskManager::createTask("Update All...", m_updateCount, [this](auto &task) {
|
||||
u32 progress = 0;
|
||||
for (auto &category : m_categories) {
|
||||
for (auto &entry : category.entries) {
|
||||
if (entry.hasUpdate) {
|
||||
@@ -207,8 +206,12 @@ namespace hex::plugin::builtin {
|
||||
continue;
|
||||
|
||||
m_download.wait();
|
||||
this->handleDownloadFinished(category, entry);
|
||||
task.update(progress);
|
||||
|
||||
while (m_download.valid()) {
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
task.increment();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,7 +372,6 @@ namespace hex::plugin::builtin {
|
||||
log::error("Download failed! HTTP Code {}", response.getStatusCode());
|
||||
}
|
||||
|
||||
|
||||
m_download = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <hex.hpp>
|
||||
#include <hex/helpers/http_requests.hpp>
|
||||
|
||||
#include <hex/api/workspace_manager.hpp>
|
||||
#include <hex/api/event_manager.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
@@ -7,7 +8,11 @@
|
||||
#include <hex/api/layout_manager.hpp>
|
||||
#include <hex/api/achievement_manager.hpp>
|
||||
#include <hex/api_urls.hpp>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
#include <hex/helpers/http_requests.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
@@ -28,8 +33,6 @@
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <popups/popup_question.hpp>
|
||||
#include <hex/api/workspace_manager.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
@@ -546,14 +549,17 @@ namespace hex::plugin::builtin {
|
||||
// Restore callback
|
||||
[crashFileData, backupFilePath, hasProject, hasBackupFile] {
|
||||
if (hasBackupFile) {
|
||||
ProjectFile::load(backupFilePath);
|
||||
if (hasProject) {
|
||||
ProjectFile::setPath(crashFileData["project"].get<std::string>());
|
||||
if (ProjectFile::load(backupFilePath)) {
|
||||
if (hasProject) {
|
||||
ProjectFile::setPath(crashFileData["project"].get<std::string>());
|
||||
} else {
|
||||
ProjectFile::setPath("");
|
||||
}
|
||||
RequestUpdateWindowTitle::post();
|
||||
} else {
|
||||
ProjectFile::setPath("");
|
||||
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(backupFilePath)));
|
||||
}
|
||||
RequestUpdateWindowTitle::post();
|
||||
}else{
|
||||
} else {
|
||||
if (hasProject) {
|
||||
ProjectFile::setPath(crashFileData["project"].get<std::string>());
|
||||
}
|
||||
|
||||
@@ -80,9 +80,10 @@ namespace hex::plugin::decompress {
|
||||
if (stream.avail_out != 0)
|
||||
break;
|
||||
|
||||
section.resize(section.size() * 2);
|
||||
stream.next_out = section.data();
|
||||
stream.avail_out = section.size();
|
||||
const auto prevSectionSize = section.size();
|
||||
section.resize(prevSectionSize * 2);
|
||||
stream.next_out = section.data() + prevSectionSize;
|
||||
stream.avail_out = prevSectionSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -126,9 +127,10 @@ namespace hex::plugin::decompress {
|
||||
if (stream.avail_out != 0)
|
||||
break;
|
||||
|
||||
section.resize(section.size() * 2);
|
||||
stream.next_out = reinterpret_cast<char*>(section.data());
|
||||
stream.avail_out = section.size();
|
||||
const auto prevSectionSize = section.size();
|
||||
section.resize(prevSectionSize * 2);
|
||||
stream.next_out = reinterpret_cast<char*>(section.data()) + prevSectionSize;
|
||||
stream.avail_out = prevSectionSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -173,9 +175,10 @@ namespace hex::plugin::decompress {
|
||||
if (stream.avail_out != 0)
|
||||
break;
|
||||
|
||||
section.resize(section.size() * 2);
|
||||
stream.next_out = compressedData.data();
|
||||
stream.avail_out = compressedData.size();
|
||||
const auto prevSectionSize = section.size();
|
||||
section.resize(prevSectionSize * 2);
|
||||
stream.next_out = section.data() + prevSectionSize;
|
||||
stream.avail_out = prevSectionSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace hex::plugin::diffing {
|
||||
~ViewDiff() override;
|
||||
|
||||
void drawContent() override;
|
||||
void drawAlwaysVisibleContent() override;
|
||||
ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; }
|
||||
|
||||
public:
|
||||
|
||||
@@ -192,14 +192,12 @@ namespace hex::plugin::diffing {
|
||||
ImGui::TableSetupColumn("hex.diffing.view.diff.provider_b"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImVec2 buttonPos;
|
||||
ImGui::BeginDisabled(m_diffTask.isRunning());
|
||||
{
|
||||
// Draw settings button
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGuiExt::DimmedIconButton(ICON_VS_SETTINGS_GEAR, ImGui::GetStyleColorVec4(ImGuiCol_Text)))
|
||||
ImGui::OpenPopup("DiffingAlgorithmSettings");
|
||||
buttonPos = ImGui::GetCursorScreenPos();
|
||||
RequestOpenPopup::post("##DiffingAlgorithmSettings");
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
@@ -212,41 +210,6 @@ namespace hex::plugin::diffing {
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SetNextWindowPos(buttonPos);
|
||||
if (ImGui::BeginPopup("DiffingAlgorithmSettings")) {
|
||||
ImGuiExt::Header("hex.diffing.view.diff.algorithm"_lang, true);
|
||||
ImGui::PushItemWidth(300_scaled);
|
||||
if (ImGui::BeginCombo("##Algorithm", m_algorithm == nullptr ? "" : Lang(m_algorithm->getUnlocalizedName()))) {
|
||||
for (const auto &algorithm : ContentRegistry::Diffing::impl::getAlgorithms()) {
|
||||
ImGui::PushID(algorithm.get());
|
||||
if (ImGui::Selectable(Lang(algorithm->getUnlocalizedName()))) {
|
||||
m_algorithm = algorithm.get();
|
||||
m_analyzed = false;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (m_algorithm != nullptr) {
|
||||
ImGuiExt::TextFormattedWrapped("{}", Lang(m_algorithm->getUnlocalizedDescription()));
|
||||
}
|
||||
|
||||
ImGuiExt::Header("hex.diffing.view.diff.settings"_lang);
|
||||
if (m_algorithm != nullptr) {
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
auto prevIdx = drawList->_VtxCurrentIdx;
|
||||
m_algorithm->drawSettings();
|
||||
auto currIdx = drawList->_VtxCurrentIdx;
|
||||
|
||||
if (prevIdx == currIdx)
|
||||
ImGuiExt::TextFormatted("hex.diffing.view.diff.settings.no_settings"_lang);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Draw first hex editor column
|
||||
@@ -337,4 +300,42 @@ namespace hex::plugin::diffing {
|
||||
}
|
||||
}
|
||||
|
||||
void ViewDiff::drawAlwaysVisibleContent() {
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(400_scaled, 600_scaled));
|
||||
if (ImGui::BeginPopup("##DiffingAlgorithmSettings")) {
|
||||
ImGuiExt::Header("hex.diffing.view.diff.algorithm"_lang, true);
|
||||
ImGui::PushItemWidth(300_scaled);
|
||||
if (ImGui::BeginCombo("##Algorithm", m_algorithm == nullptr ? "" : Lang(m_algorithm->getUnlocalizedName()))) {
|
||||
for (const auto &algorithm : ContentRegistry::Diffing::impl::getAlgorithms()) {
|
||||
ImGui::PushID(algorithm.get());
|
||||
if (ImGui::Selectable(Lang(algorithm->getUnlocalizedName()))) {
|
||||
m_algorithm = algorithm.get();
|
||||
m_analyzed = false;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (m_algorithm != nullptr) {
|
||||
ImGuiExt::TextFormattedWrapped("{}", Lang(m_algorithm->getUnlocalizedDescription()));
|
||||
}
|
||||
|
||||
ImGuiExt::Header("hex.diffing.view.diff.settings"_lang);
|
||||
if (m_algorithm != nullptr) {
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
auto prevIdx = drawList->_VtxCurrentIdx;
|
||||
m_algorithm->drawSettings();
|
||||
auto currIdx = drawList->_VtxCurrentIdx;
|
||||
|
||||
if (prevIdx == currIdx)
|
||||
ImGuiExt::TextFormatted("hex.diffing.view.diff.settings.no_settings"_lang);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
namespace hex::plugin::disasm {
|
||||
|
||||
@@ -18,15 +18,15 @@ namespace hex::fonts {
|
||||
* efficient when packing the glyphs into the font atlas and therefor make the atlas much smaller.
|
||||
*/
|
||||
|
||||
ImHexApi::Fonts::loadFont("Blender Icons", romfs::get("fonts/blendericons.ttf").span<u8>(),{ { ICON_MIN_BI, ICON_MAX_BI } }, { -1_scaled, -1_scaled });
|
||||
ImHexApi::Fonts::loadFont("Blender Icons", romfs::get("fonts/blendericons.ttf").span<u8>(),{ { ICON_MIN_BI, ICON_MAX_BI } }, { -1_scaled, -1_scaled }, 0, 13);
|
||||
|
||||
ImHexApi::Fonts::loadFont("VS Codicons", romfs::get("fonts/codicons.ttf").span<u8>(),
|
||||
{
|
||||
{ ICON_MIN_VS, ICON_MAX_VS }
|
||||
},
|
||||
{ -1_scaled, -1_scaled });
|
||||
{ -1_scaled, -1_scaled }, 0, 13);
|
||||
|
||||
ImHexApi::Fonts::loadFont("Unifont", romfs::get("fonts/unifont.otf").span<u8>());
|
||||
ImHexApi::Fonts::loadFont("Unifont", romfs::get("fonts/unifont.otf").span<u8>(), {}, {}, 0, 16);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -183,6 +183,9 @@ namespace hex::plugin::hashes {
|
||||
Function create(std::string name) override {
|
||||
return Hash::create(name, [hash = *this](const Region& region, prv::Provider *provider) -> std::vector<u8> {
|
||||
auto crc = HashFactory::Checksum::CreateCRC(hash.m_width, hash.m_polynomial, hash.m_initialValue, hash.m_reflectIn, hash.m_reflectOut, hash.m_xorOut, 0, { "CRC" });
|
||||
|
||||
crc->Initialize();
|
||||
|
||||
auto bytes = hashProviderRegionWithHashLib(region, provider, crc);
|
||||
|
||||
return bytes;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <fonts/codicons_font.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <popups/popup_notification.hpp>
|
||||
|
||||
@@ -48,18 +49,24 @@ namespace hex::ui {
|
||||
}
|
||||
|
||||
struct ToastInfo : impl::ToastNotification<ToastInfo> {
|
||||
ToastInfo(std::string message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerInfo), ICON_VS_INFO, "hex.ui.common.info", std::move(message)) {}
|
||||
explicit ToastInfo(std::string message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerInfo), ICON_VS_INFO, "hex.ui.common.info", message) {
|
||||
log::info("{}", message);
|
||||
}
|
||||
};
|
||||
|
||||
struct ToastWarning : impl::ToastNotification<ToastWarning> {
|
||||
ToastWarning(std::string message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerWarning), ICON_VS_WARNING, "hex.ui.common.warning", std::move(message)) {}
|
||||
explicit ToastWarning(std::string message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerWarning), ICON_VS_WARNING, "hex.ui.common.warning", message) {
|
||||
log::warn("{}", message);
|
||||
}
|
||||
};
|
||||
|
||||
struct ToastError : impl::ToastNotification<ToastError> {
|
||||
ToastError(std::string message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerError), ICON_VS_ERROR, "hex.ui.common.error", std::move(message)) {}
|
||||
explicit ToastError(std::string message)
|
||||
: ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerError), ICON_VS_ERROR, "hex.ui.common.error", message) {
|
||||
log::error("{}", message);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -206,11 +206,11 @@ namespace hex::ui {
|
||||
constexpr auto roundingCorners = ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomRight;
|
||||
constexpr auto axis = ImGuiAxis_Y;
|
||||
|
||||
constexpr static u64 RowCount = 256;
|
||||
const auto rowHeight = innerRect.GetSize().y / RowCount;
|
||||
const auto rowHeight = 4_scaled;
|
||||
const u64 rowCount = innerRect.GetSize().y / rowHeight;
|
||||
const ImS64 scrollPos = m_scrollPosition.get();
|
||||
const auto grabSize = rowHeight * m_visibleRowCount;
|
||||
const ImS64 grabPos = (RowCount - m_visibleRowCount) * (double(scrollPos) / numRows);
|
||||
const ImS64 grabPos = (rowCount - m_visibleRowCount) * (double(scrollPos) / numRows);
|
||||
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace hex::ui {
|
||||
|
||||
std::vector<u8> rowData(m_bytesPerRow);
|
||||
const auto drawStart = std::max<ImS64>(0, scrollPos - grabPos);
|
||||
for (ImS64 y = drawStart; y < std::min<ImS64>(drawStart + RowCount, m_provider->getSize() / m_bytesPerRow); y += 1) {
|
||||
for (ImS64 y = drawStart; y < std::min<ImS64>(drawStart + rowCount, m_provider->getSize() / m_bytesPerRow); y += 1) {
|
||||
const auto rowStart = bb.Min + ImVec2(0, (y - drawStart) * rowHeight);
|
||||
const auto rowEnd = rowStart + ImVec2(bb.GetSize().x, rowHeight);
|
||||
|
||||
@@ -1050,7 +1050,7 @@ namespace hex::ui {
|
||||
this->setSelection(selectionStart.value_or(address), endAddress);
|
||||
this->scrollToSelection();
|
||||
}
|
||||
else if (ImGui::IsMouseDown(ImGuiMouseButton_Left) || (ImGui::IsMouseDown(ImGuiMouseButton_Right) && (address < m_selectionStart || address > m_selectionEnd))) {
|
||||
else if (ImGui::IsMouseDown(ImGuiMouseButton_Left) || (ImGui::IsMouseDown(ImGuiMouseButton_Right) && (address < std::min(m_selectionStart, m_selectionEnd) || address > std::max(m_selectionStart, m_selectionEnd)))) {
|
||||
if (ImGui::GetIO().KeyShift)
|
||||
this->setSelection(selectionStart.value_or(address), endAddress);
|
||||
else
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"language": "German",
|
||||
"translations": {
|
||||
"hex.yara.information_section.advanced_data_info": "Erweiterte Dateninformationen",
|
||||
"hex.yara.information_section.advanced_data_info.no_information": "Kein erweiterten Informationen gefunden",
|
||||
"hex.yara_rules.view.yara.error": "Yara Kompilerfehler: {0}",
|
||||
"hex.yara_rules.view.yara.header.matches": "Treffer",
|
||||
"hex.yara_rules.view.yara.header.rules": "Regeln",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"fallback": true,
|
||||
"translations": {
|
||||
"hex.yara.information_section.advanced_data_info": "Advanced Data Information",
|
||||
"hex.yara.information_section.advanced_data_info.no_information": "No further information found",
|
||||
"hex.yara_rules.view.yara.error": "Yara Compiler error: {0}",
|
||||
"hex.yara_rules.view.yara.header.matches": "Matches",
|
||||
"hex.yara_rules.view.yara.header.rules": "Rules",
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/magic.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
@@ -29,9 +27,11 @@ namespace hex::plugin::yara {
|
||||
void process(Task &task, prv::Provider *provider, Region region) override {
|
||||
for (const auto &yaraSignaturePath : fs::getDefaultPaths(fs::ImHexPath::YaraAdvancedAnalysis)) {
|
||||
for (const auto &ruleFilePath : std::fs::recursive_directory_iterator(yaraSignaturePath)) {
|
||||
const std::string fileContent = romfs::get(ruleFilePath).data<const char>();
|
||||
wolv::io::File file(ruleFilePath.path(), wolv::io::File::Mode::Read);
|
||||
if (!file.isValid())
|
||||
continue;
|
||||
|
||||
YaraRule yaraRule(fileContent);
|
||||
YaraRule yaraRule(file.readString());
|
||||
task.setInterruptCallback([&yaraRule] {
|
||||
yaraRule.interrupt();
|
||||
});
|
||||
@@ -57,28 +57,39 @@ namespace hex::plugin::yara {
|
||||
}
|
||||
|
||||
void drawContent() override {
|
||||
if (ImGui::BeginTable("information", 2, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_NoKeepColumnsVisible)) {
|
||||
ImGui::TableSetupColumn("Left", ImGuiTableColumnFlags_WidthStretch, 0.5F);
|
||||
ImGui::TableSetupColumn("Right", ImGuiTableColumnFlags_WidthStretch, 0.5F);
|
||||
const auto empty = !std::ranges::any_of(m_categories, [](const auto &entry) {
|
||||
const auto &[categoryName, category] = entry;
|
||||
return !category.matchedRules.empty();
|
||||
});
|
||||
|
||||
ImGui::TableNextRow();
|
||||
if (!empty) {
|
||||
if (ImGui::BeginTable("information", 2, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_NoKeepColumnsVisible)) {
|
||||
ImGui::TableSetupColumn("Left", ImGuiTableColumnFlags_WidthStretch, 0.5F);
|
||||
ImGui::TableSetupColumn("Right", ImGuiTableColumnFlags_WidthStretch, 0.5F);
|
||||
|
||||
for (auto &[categoryName, category] : m_categories) {
|
||||
if (category.matchedRules.empty())
|
||||
continue;
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::BeginSubWindow(categoryName.c_str());
|
||||
{
|
||||
for (const auto &match : category.matchedRules) {
|
||||
const auto &ruleName = match.metadata.contains("name") ? match.metadata.at("name") : match.identifier;
|
||||
ImGui::TextUnformatted(ruleName.c_str());
|
||||
for (auto &[categoryName, category] : m_categories) {
|
||||
if (category.matchedRules.empty())
|
||||
continue;
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::BeginSubWindow(categoryName.c_str());
|
||||
{
|
||||
for (const auto &match : category.matchedRules) {
|
||||
const auto &ruleName = match.metadata.contains("name") ? match.metadata.at("name") : match.identifier;
|
||||
ImGui::TextUnformatted(ruleName.c_str());
|
||||
}
|
||||
}
|
||||
ImGuiExt::EndSubWindow();
|
||||
}
|
||||
ImGuiExt::EndSubWindow();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else {
|
||||
ImGui::NewLine();
|
||||
ImGuiExt::TextFormattedCenteredHorizontal("{}", "hex.yara.information_section.advanced_data_info.no_information"_lang);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user