1
0
mirror of synced 2024-12-13 08:21:07 +01:00
ImHex/plugins/libimhex/include/hex/helpers/utils.hpp

365 lines
10 KiB
C++
Raw Normal View History

2020-12-27 14:12:53 +01:00
#pragma once
#include <hex.hpp>
#include <array>
2021-01-07 01:58:56 +01:00
#include <cstring>
2020-12-27 14:12:53 +01:00
#include <functional>
#include <memory>
#include <optional>
#include <string>
2021-01-02 20:49:55 +01:00
#include <type_traits>
2020-12-27 14:12:53 +01:00
#include <vector>
#include <fmt/format.h>
#include <fmt/chrono.h>
2020-12-27 14:12:53 +01:00
#ifdef __MINGW32__
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
#if defined(__APPLE__) || defined(__FreeBSD__)
#define off64_t off_t
2020-12-27 15:54:12 +01:00
#define fopen64 fopen
#define fseeko64 fseek
#define ftello64 ftell
#endif
namespace hex {
template<typename>
struct is_integral_helper : public std::false_type { };
template<>
struct is_integral_helper<u8> : public std::true_type { };
template<>
struct is_integral_helper<s8> : public std::true_type { };
template<>
struct is_integral_helper<u16> : public std::true_type { };
template<>
struct is_integral_helper<s16> : public std::true_type { };
template<>
struct is_integral_helper<u32> : public std::true_type { };
template<>
struct is_integral_helper<s32> : public std::true_type { };
template<>
struct is_integral_helper<u64> : public std::true_type { };
template<>
struct is_integral_helper<s64> : public std::true_type { };
template<>
struct is_integral_helper<u128> : public std::true_type { };
template<>
struct is_integral_helper<s128> : public std::true_type { };
template<>
struct is_integral_helper<bool> : public std::true_type { };
template<>
struct is_integral_helper<char> : public std::true_type { };
template<>
struct is_integral_helper<char8_t> : public std::true_type { };
template<>
struct is_integral_helper<char16_t> : public std::true_type { };
template<>
struct is_integral_helper<char32_t> : public std::true_type { };
template<>
struct is_integral_helper<wchar_t> : public std::true_type { };
template<typename T>
struct is_integral : public is_integral_helper<std::remove_cvref_t<T>>::type { };
template<typename>
struct is_signed_helper : public std::false_type { };
template<>
struct is_signed_helper<s8> : public std::true_type { };
template<>
struct is_signed_helper<s16> : public std::true_type { };
template<>
struct is_signed_helper<s32> : public std::true_type { };
template<>
struct is_signed_helper<s64> : public std::true_type { };
template<>
struct is_signed_helper<s128> : public std::true_type { };
template<>
struct is_signed_helper<char> : public std::true_type { };
template<>
struct is_signed_helper<float> : public std::true_type { };
template<>
struct is_signed_helper<double> : public std::true_type { };
template<>
struct is_signed_helper<long double> : public std::true_type { };
template<typename T>
struct is_signed : public is_signed_helper<std::remove_cvref_t<T>>::type { };
template<typename>
struct is_floating_point_helper : public std::false_type { };
template<>
struct is_floating_point_helper<float> : public std::true_type { };
template<>
struct is_floating_point_helper<double> : public std::true_type { };
template<>
struct is_floating_point_helper<long double> : public std::true_type { };
template<typename T>
struct is_floating_point : public is_floating_point_helper<std::remove_cvref_t<T>>::type { };
}
2020-12-27 15:54:12 +01:00
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 12000
#if __has_include(<concepts>)
// Make sure we break when derived_from is implemented in libc++. Then we can fix a compatibility version above
#include <concepts>
#endif
// libcxx 12 still doesn't have many default concepts implemented, as a result we need to define it ourself using clang built-ins.
2020-12-27 15:54:12 +01:00
// [concept.derived] (patch from https://reviews.llvm.org/D74292)
namespace hex {
template<class _Dp, class _Bp>
concept derived_from =
__is_base_of(_Bp, _Dp) && __is_convertible_to(const volatile _Dp*, const volatile _Bp*);
}
#else
// Assume supported
#include <concepts>
namespace hex {
using std::derived_from;
}
#endif
// [concepts.arithmetic]
namespace hex {
template<class _Tp>
concept integral = hex::is_integral<_Tp>::value;
template<class _Tp>
concept signed_integral = integral<_Tp> && hex::is_signed<_Tp>::value;
template<class _Tp>
concept unsigned_integral = integral<_Tp> && !signed_integral<_Tp>;
template<class _Tp>
concept floating_point = std::is_floating_point<_Tp>::value;
2020-12-27 15:54:12 +01:00
}
#define TOKEN_CONCAT_IMPL(x, y) x ## y
#define TOKEN_CONCAT(x, y) TOKEN_CONCAT_IMPL(x, y)
2021-03-31 22:54:43 +02:00
#define ANONYMOUS_VARIABLE(prefix) TOKEN_CONCAT(prefix, __COUNTER__)
2020-12-27 14:12:53 +01:00
namespace hex {
std::string to_string(u128 value);
std::string to_string(s128 value);
2021-01-21 14:53:44 +01:00
std::string toByteString(u64 bytes);
std::string makePrintable(char c);
2021-01-21 14:53:44 +01:00
void openWebpage(std::string_view url);
2021-01-21 14:53:44 +01:00
2020-12-27 14:12:53 +01:00
template<typename ... Args>
inline std::string format(std::string_view format, Args ... args) {
return fmt::format(format, args...);
}
2020-12-27 14:12:53 +01:00
template<typename ... Args>
inline void print(std::string_view format, Args ... args) {
fmt::print(format, args...);
2020-12-27 14:12:53 +01:00
}
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const hex::unsigned_integral auto &value) {
using ValueType = std::remove_cvref_t<decltype(value)>;
ValueType mask = (std::numeric_limits<ValueType>::max() >> (((sizeof(value) * 8) - 1) - (from - to))) << to;
2020-12-27 14:12:53 +01:00
return (value & mask) >> to;
}
2021-04-14 09:03:41 +02:00
2020-12-27 14:12:53 +01:00
template<typename T>
struct always_false : std::false_type {};
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
2020-12-27 14:12:53 +01:00
template<typename T>
constexpr T changeEndianess(T value, std::endian endian) {
if (endian == std::endian::native)
return value;
if constexpr (sizeof(T) == 1)
return value;
else if constexpr (sizeof(T) == 2)
return __builtin_bswap16(value);
else if constexpr (sizeof(T) == 4)
return __builtin_bswap32(value);
else if constexpr (sizeof(T) == 8)
return __builtin_bswap64(value);
2021-01-21 22:55:04 +01:00
else if constexpr (sizeof(T) == 16)
return T(__builtin_bswap64(value & 0xFFFF'FFFF'FFFF'FFFF)) << 64 | __builtin_bswap64(value >> 64);
2020-12-27 14:12:53 +01:00
else
2021-01-21 22:55:04 +01:00
static_assert(always_false<T>::value, "Invalid type provided!");
2020-12-27 14:12:53 +01:00
}
template<typename T>
constexpr T changeEndianess(T value, size_t size, std::endian endian) {
if (endian == std::endian::native)
return value;
if (size == 1)
return value;
else if (size == 2)
return __builtin_bswap16(value);
else if (size == 4)
return __builtin_bswap32(value);
else if (size == 8)
return __builtin_bswap64(value);
2021-01-21 22:55:04 +01:00
else if (size == 16)
return u128(__builtin_bswap64(u128(value) & 0xFFFF'FFFF'FFFF'FFFF)) << 64 | __builtin_bswap64(u128(value) >> 64);
2020-12-27 14:12:53 +01:00
else
throw std::invalid_argument("Invalid value size!");
}
template< class T >
constexpr T bit_width(T x) noexcept {
return std::numeric_limits<T>::digits - std::countl_zero(x);
}
template<typename T>
constexpr T bit_ceil(T x) noexcept {
if (x <= 1u)
return T(1);
return T(1) << bit_width(T(x - 1));
}
std::vector<std::string> splitString(std::string_view string, std::string_view delimiter);
std::string combineStrings(const std::vector<std::string> &strings, std::string_view delimiter = "");
std::string toEngineeringString(double value);
2020-12-27 14:12:53 +01:00
std::vector<u8> readFile(std::string_view path);
template<typename T>
std::vector<u8> toBytes(T value) {
std::vector<u8> bytes(sizeof(T));
std::memcpy(bytes.data(), &value, sizeof(T));
return bytes;
}
inline std::vector<u8> parseByteString(std::string_view string) {
auto byteString = std::string(string);
byteString.erase(std::remove(byteString.begin(), byteString.end(), ' '), byteString.end());
if ((byteString.length() % 2) != 0) return { };
std::vector<u8> result;
for (u32 i = 0; i < byteString.length(); i += 2) {
if (!std::isxdigit(byteString[i]) || !std::isxdigit(byteString[i + 1]))
return { };
result.push_back(std::strtoul(byteString.substr(i, 2).c_str(), nullptr, 16));
}
return result;
}
inline std::string toBinaryString(hex::unsigned_integral auto number) {
2021-02-07 22:57:34 +01:00
if (number == 0) return "0";
std::string result;
for (s16 bit = hex::bit_width(number) - 1; bit >= 0; bit--)
2021-02-07 22:57:34 +01:00
result += (number & (0b1 << bit)) == 0 ? '0' : '1';
return result;
}
inline void trimLeft(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
inline void trimRight(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}
inline void trim(std::string &s) {
trimLeft(s);
trimRight(s);
}
sys/build: Properly support per-system metadata file paths (#181) * sys: Move away from metadata paths next to executable in the application Build system doesn't properly install / pack stuff yet * build: Updated README to contain better install instructions * sys: Search for imhex resource files in ~/Application Support * sys: MAX_PATH -> PATH_MAX * sys: Seach for imhex resource files in Application Support using NSFileManager (#180) * sys: Allow for multiple file search paths Also use install prefix instead of just /usr on Linux * build: Fixed IMHEX_INSTALL_PREFIX macro definition * build: Fix duplicate switch entry on Linux * docs: Updated readme to properly reflect new paths and dependencies * sys: Install files in their proper paths on linux (#183) * Install files in their proper paths on linux * Only create user directories * Follow the XDG specification on linux XDG specification specifies how to find config and data directories on linux systems. Specifically, it says this: - Data should be written to $XDG_DATA_HOME - Config should be written to $XDG_CONFIG_HOME - Data should be read from $XDG_DATA_HOME:$XDG_DATA_DIRS - Config should be read from $XDG_CONFIG_HOME:$XDG_CONFIG_DIRS The default values are this: - XDG_DATA_HOME: $HOME/.local/share - XDG_CONFIG_HOME: $HOME/.config - XDG_DATA_DIRS: /usr/share:/usr/local/share - XDG_CONFIG_DIRS: /etc/xdg Platforms with non-standard filesystems (like NixOS) will correctly set up those environment variables, allowing softwares to work unmodified. In order to make integration as simple as possible, we use a simple header-only dependency called XDGPP which does all the hard work for us to find the default directories. * Look for plugins in all Plugin Paths If the plugin folder was missing from one of the PluginPaths, we would immediately stop loading plugins. We now keep looking even if one of the path is missing. Co-authored-by: Nichole Mattera <me@nicholemattera.com> Co-authored-by: Robin Lambertz <unfiltered@roblab.la>
2021-03-01 08:56:49 +01:00
enum class ImHexPath {
Patterns,
PatternsInclude,
Magic,
Python,
Plugins,
Yara,
Config,
2021-06-26 01:18:33 +02:00
Resources,
Constants
sys/build: Properly support per-system metadata file paths (#181) * sys: Move away from metadata paths next to executable in the application Build system doesn't properly install / pack stuff yet * build: Updated README to contain better install instructions * sys: Search for imhex resource files in ~/Application Support * sys: MAX_PATH -> PATH_MAX * sys: Seach for imhex resource files in Application Support using NSFileManager (#180) * sys: Allow for multiple file search paths Also use install prefix instead of just /usr on Linux * build: Fixed IMHEX_INSTALL_PREFIX macro definition * build: Fix duplicate switch entry on Linux * docs: Updated readme to properly reflect new paths and dependencies * sys: Install files in their proper paths on linux (#183) * Install files in their proper paths on linux * Only create user directories * Follow the XDG specification on linux XDG specification specifies how to find config and data directories on linux systems. Specifically, it says this: - Data should be written to $XDG_DATA_HOME - Config should be written to $XDG_CONFIG_HOME - Data should be read from $XDG_DATA_HOME:$XDG_DATA_DIRS - Config should be read from $XDG_CONFIG_HOME:$XDG_CONFIG_DIRS The default values are this: - XDG_DATA_HOME: $HOME/.local/share - XDG_CONFIG_HOME: $HOME/.config - XDG_DATA_DIRS: /usr/share:/usr/local/share - XDG_CONFIG_DIRS: /etc/xdg Platforms with non-standard filesystems (like NixOS) will correctly set up those environment variables, allowing softwares to work unmodified. In order to make integration as simple as possible, we use a simple header-only dependency called XDGPP which does all the hard work for us to find the default directories. * Look for plugins in all Plugin Paths If the plugin folder was missing from one of the PluginPaths, we would immediately stop loading plugins. We now keep looking even if one of the path is missing. Co-authored-by: Nichole Mattera <me@nicholemattera.com> Co-authored-by: Robin Lambertz <unfiltered@roblab.la>
2021-03-01 08:56:49 +01:00
};
std::vector<std::string> getPath(ImHexPath path);
2021-03-31 22:54:43 +02:00
#define SCOPE_GUARD ::hex::ScopeGuardOnExit() + [&]()
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_) = SCOPE_GUARD
template<class F>
class ScopeGuard {
private:
F m_func;
bool m_active;
2020-12-27 14:12:53 +01:00
public:
2021-03-31 22:54:43 +02:00
constexpr ScopeGuard(F func) : m_func(std::move(func)), m_active(true) { }
~ScopeGuard() { if (this->m_active) { this->m_func(); } }
void release() { this->m_active = false; }
2020-12-27 14:12:53 +01:00
2021-03-31 22:54:43 +02:00
ScopeGuard(ScopeGuard &&other) noexcept : m_func(std::move(other.m_func)), m_active(other.m_active) {
other.cancel();
2020-12-27 14:12:53 +01:00
}
2021-03-31 22:54:43 +02:00
ScopeGuard& operator=(ScopeGuard &&) = delete;
2020-12-27 14:12:53 +01:00
};
2021-03-31 22:54:43 +02:00
enum class ScopeGuardOnExit { };
template <typename F>
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
return ScopeGuard<F>(std::forward<F>(f));
}
2020-12-27 14:12:53 +01:00
struct Region {
u64 address;
size_t size;
};
}