mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
@@ -503,7 +506,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 +614,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 +762,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()
|
||||
@@ -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 &);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ()
|
||||
@@ -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;
|
||||
}
|
||||
@@ -567,29 +588,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,10 +649,7 @@ namespace hex {
|
||||
m_unlockFrameRate = true;
|
||||
}
|
||||
|
||||
GLFWwindow *backupContext = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backupContext);
|
||||
glfwPollEvents();
|
||||
|
||||
// Process layout load requests
|
||||
// NOTE: This needs to be done before a new frame is started, otherwise ImGui won't handle docking correctly
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
@@ -40,6 +49,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 +104,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 +121,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 +250,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopup))
|
||||
return;
|
||||
if (ImGui::IsAnyItemHovered())
|
||||
return;
|
||||
|
||||
static ImGuiWindow *lastFocusedWindow = nullptr;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1305,7 +1305,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 +1392,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());
|
||||
}
|
||||
|
||||
|
||||
@@ -207,7 +207,11 @@ namespace hex::plugin::builtin {
|
||||
continue;
|
||||
|
||||
m_download.wait();
|
||||
this->handleDownloadFinished(category, entry);
|
||||
|
||||
while (m_download.valid()) {
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
task.update(progress);
|
||||
}
|
||||
}
|
||||
@@ -369,7 +373,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>());
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
namespace hex::plugin::disasm {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -13,6 +13,7 @@ add_imhex_plugin(
|
||||
INCLUDES
|
||||
include
|
||||
LIBRARIES
|
||||
libpl
|
||||
libpl-gen
|
||||
fonts
|
||||
LIBRARY_PLUGIN
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -29,9 +29,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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user