#include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(OS_WINDOWS) #include #else #include #include #endif namespace hex { namespace ImHexApi::HexEditor { Highlighting::Highlighting(Region region, color_t color) : m_region(region), m_color(color) { } Tooltip::Tooltip(Region region, std::string value, color_t color) : m_region(region), m_value(std::move(value)), m_color(color) { } namespace impl { static AutoReset> s_backgroundHighlights; const std::map& getBackgroundHighlights() { return *s_backgroundHighlights; } static AutoReset> s_backgroundHighlightingFunctions; const std::map& getBackgroundHighlightingFunctions() { return *s_backgroundHighlightingFunctions; } static AutoReset> s_foregroundHighlights; const std::map& getForegroundHighlights() { return *s_foregroundHighlights; } static AutoReset> s_foregroundHighlightingFunctions; const std::map& getForegroundHighlightingFunctions() { return *s_foregroundHighlightingFunctions; } static AutoReset> s_tooltips; const std::map& getTooltips() { return *s_tooltips; } static AutoReset> s_tooltipFunctions; const std::map& getTooltipFunctions() { return *s_tooltipFunctions; } static AutoReset> s_hoveringFunctions; const std::map& getHoveringFunctions() { return *s_hoveringFunctions; } static AutoReset> s_currentSelection; void setCurrentSelection(const std::optional ®ion) { if (region == Region::Invalid()) { clearSelection(); } else { *s_currentSelection = region; } } static PerProvider> s_hoveredRegion; void setHoveredRegion(const prv::Provider *provider, const Region ®ion) { if (region == Region::Invalid()) s_hoveredRegion.get(provider).reset(); else s_hoveredRegion.get(provider) = region; } } u32 addBackgroundHighlight(const Region ®ion, color_t color) { static u32 id = 0; id++; impl::s_backgroundHighlights->insert({ id, Highlighting { region, color } }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeBackgroundHighlight(u32 id) { impl::s_backgroundHighlights->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) { static u32 id = 0; id++; impl::s_backgroundHighlightingFunctions->insert({ id, function }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeBackgroundHighlightingProvider(u32 id) { impl::s_backgroundHighlightingFunctions->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addForegroundHighlight(const Region ®ion, color_t color) { static u32 id = 0; id++; impl::s_foregroundHighlights->insert({ id, Highlighting { region, color } }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeForegroundHighlight(u32 id) { impl::s_foregroundHighlights->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) { static u32 id = 0; id++; impl::s_foregroundHighlightingFunctions->insert({ id, function }); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); return id; } void removeForegroundHighlightingProvider(u32 id) { impl::s_foregroundHighlightingFunctions->erase(id); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); }); } u32 addHoverHighlightProvider(const impl::HoveringFunction &function) { static u32 id = 0; id++; impl::s_hoveringFunctions->insert({ id, function }); return id; } void removeHoverHighlightProvider(u32 id) { impl::s_hoveringFunctions->erase(id); } static u32 tooltipId = 0; u32 addTooltip(Region region, std::string value, color_t color) { tooltipId++; impl::s_tooltips->insert({ tooltipId, { region, std::move(value), color } }); return tooltipId; } void removeTooltip(u32 id) { impl::s_tooltips->erase(id); } static u32 tooltipFunctionId; u32 addTooltipProvider(TooltipFunction function) { tooltipFunctionId++; impl::s_tooltipFunctions->insert({ tooltipFunctionId, std::move(function) }); return tooltipFunctionId; } void removeTooltipProvider(u32 id) { impl::s_tooltipFunctions->erase(id); } bool isSelectionValid() { auto selection = getSelection(); return selection.has_value() && selection->provider != nullptr; } std::optional getSelection() { return impl::s_currentSelection; } void clearSelection() { impl::s_currentSelection->reset(); } void setSelection(const Region ®ion, prv::Provider *provider) { setSelection(ProviderRegion { region, provider == nullptr ? Provider::get() : provider }); } void setSelection(const ProviderRegion ®ion) { RequestHexEditorSelectionChange::post(region); } void setSelection(u64 address, size_t size, prv::Provider *provider) { setSelection({ { address, size }, provider == nullptr ? Provider::get() : provider }); } void addVirtualFile(const std::fs::path &path, std::vector data, Region region) { RequestAddVirtualFile::post(path, std::move(data), region); } const std::optional& getHoveredRegion(const prv::Provider *provider) { return impl::s_hoveredRegion.get(provider); } } namespace ImHexApi::Bookmarks { u64 add(Region region, const std::string &name, const std::string &comment, u32 color) { u64 id = 0; RequestAddBookmark::post(region, name, comment, color, &id); return id; } u64 add(u64 address, size_t size, const std::string &name, const std::string &comment, u32 color) { return add(Region { address, size }, name, comment, color); } void remove(u64 id) { RequestRemoveBookmark::post(id); } } namespace ImHexApi::Provider { static i64 s_currentProvider = -1; static AutoReset>> s_providers; namespace impl { static std::vector s_closingProviders; void resetClosingProvider() { s_closingProviders.clear(); } const std::vector& getClosingProviders() { return s_closingProviders; } } prv::Provider *get() { if (!ImHexApi::Provider::isValid()) return nullptr; return (*s_providers)[s_currentProvider].get(); } std::vector getProviders() { std::vector result; result.reserve(s_providers->size()); for (const auto &provider : *s_providers) result.push_back(provider.get()); return result; } void setCurrentProvider(i64 index) { if (TaskManager::getRunningTaskCount() > 0) return; if (std::cmp_less(index, s_providers->size()) && s_currentProvider != index) { auto oldProvider = get(); s_currentProvider = index; EventProviderChanged::post(oldProvider, get()); } RequestUpdateWindowTitle::post(); } void setCurrentProvider(NonNull provider) { if (TaskManager::getRunningTaskCount() > 0) return; const auto providers = getProviders(); auto it = std::ranges::find(providers, provider.get()); auto index = std::distance(providers.begin(), it); setCurrentProvider(index); } i64 getCurrentProviderIndex() { return s_currentProvider; } bool isValid() { return !s_providers->empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers->size()); } void markDirty() { get()->markDirty(); } void resetDirty() { for (const auto &provider : *s_providers) provider->markDirty(false); } bool isDirty() { return std::ranges::any_of(*s_providers, [](const auto &provider) { return provider->isDirty(); }); } void add(std::unique_ptr &&provider, bool skipLoadInterface, bool select) { if (TaskManager::getRunningTaskCount() > 0) return; if (skipLoadInterface) provider->skipLoadInterface(); EventProviderCreated::post(provider.get()); s_providers->emplace_back(std::move(provider)); if (select || s_providers->size() == 1) setCurrentProvider(s_providers->size() - 1); } void remove(prv::Provider *provider, bool noQuestions) { if (provider == nullptr) return; if (TaskManager::getRunningTaskCount() > 0) return; if (!noQuestions) { impl::s_closingProviders.push_back(provider); bool shouldClose = true; EventProviderClosing::post(provider, &shouldClose); if (!shouldClose) return; } const auto it = std::ranges::find_if(*s_providers, [provider](const auto &p) { return p.get() == provider; }); if (it == s_providers->end()) return; if (!s_providers->empty()) { if (it == s_providers->begin()) { // If the first provider is being closed, select the one that's the first one now setCurrentProvider(0); if (s_providers->size() > 1) EventProviderChanged::post(s_providers->at(0).get(), s_providers->at(1).get()); } else if (std::distance(s_providers->begin(), it) == s_currentProvider) { // If the current provider is being closed, select the one that's before it setCurrentProvider(s_currentProvider - 1); } else { // If any other provider is being closed, find the current provider in the list again and select it again const auto currentProvider = get(); const auto currentIt = std::ranges::find_if(*s_providers, [currentProvider](const auto &p) { return p.get() == currentProvider; }); if (currentIt != s_providers->end()) { auto newIndex = std::distance(s_providers->begin(), currentIt); if (s_currentProvider == newIndex && newIndex != 0) newIndex -= 1; setCurrentProvider(newIndex); } else { // If the current provider is not in the list anymore, select the first one setCurrentProvider(0); } } } provider->close(); EventProviderClosed::post(provider); RequestUpdateWindowTitle::post(); TaskManager::runWhenTasksFinished([it, provider] { EventProviderDeleted::post(provider); std::erase(impl::s_closingProviders, provider); s_providers->erase(it); if (s_currentProvider >= i64(s_providers->size())) setCurrentProvider(0); if (s_providers->empty()) EventProviderChanged::post(provider, nullptr); }); } prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) { prv::Provider* result = nullptr; RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result); return result; } } namespace ImHexApi::System { namespace impl { // Default to true means we forward to ourselves by default static bool s_isMainInstance = true; void setMainInstanceStatus(bool status) { s_isMainInstance = status; } static ImVec2 s_mainWindowPos; static ImVec2 s_mainWindowSize; void setMainWindowPosition(i32 x, i32 y) { s_mainWindowPos = ImVec2(float(x), float(y)); } void setMainWindowSize(u32 width, u32 height) { s_mainWindowSize = ImVec2(float(width), float(height)); } static ImGuiID s_mainDockSpaceId; void setMainDockSpaceId(ImGuiID id) { s_mainDockSpaceId = id; } static GLFWwindow *s_mainWindowHandle; void setMainWindowHandle(GLFWwindow *window) { s_mainWindowHandle = window; } static float s_globalScale = 1.0; void setGlobalScale(float scale) { s_globalScale = scale; } static float s_nativeScale = 1.0; void setNativeScale(float scale) { s_nativeScale = scale; } static bool s_borderlessWindowMode; void setBorderlessWindowMode(bool enabled) { s_borderlessWindowMode = enabled; } static bool s_multiWindowMode = false; void setMultiWindowMode(bool enabled) { s_multiWindowMode = enabled; } static std::optional s_initialWindowProperties; void setInitialWindowProperties(InitialWindowProperties properties) { s_initialWindowProperties = properties; } static AutoReset s_gpuVendor; void setGPUVendor(const std::string &vendor) { s_gpuVendor = vendor; } static AutoReset> s_initArguments; void addInitArgument(const std::string &key, const std::string &value) { static std::mutex initArgumentsMutex; std::scoped_lock lock(initArgumentsMutex); (*s_initArguments)[key] = value; } static double s_lastFrameTime; void setLastFrameTime(double time) { s_lastFrameTime = time; } static bool s_windowResizable = true; bool isWindowResizable() { return s_windowResizable; } static std::vector s_autoResetObjects; void addAutoResetObject(hex::impl::AutoResetBase *object) { s_autoResetObjects.emplace_back(object); } void cleanup() { for (const auto &object : s_autoResetObjects) object->reset(); } } bool isMainInstance() { return impl::s_isMainInstance; } void closeImHex(bool noQuestions) { RequestCloseImHex::post(noQuestions); } void restartImHex() { RequestRestartImHex::post(); RequestCloseImHex::post(false); } void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress) { EventSetTaskBarIconState::post(u32(state), u32(type), progress); } static float s_targetFPS = 14.0F; float getTargetFPS() { return s_targetFPS; } void setTargetFPS(float fps) { s_targetFPS = fps; } float getGlobalScale() { return impl::s_globalScale; } float getNativeScale() { return impl::s_nativeScale; } ImVec2 getMainWindowPosition() { if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != ImGuiConfigFlags_None) return impl::s_mainWindowPos; else return { 0, 0 }; } ImVec2 getMainWindowSize() { return impl::s_mainWindowSize; } ImGuiID getMainDockSpaceId() { return impl::s_mainDockSpaceId; } GLFWwindow* getMainWindowHandle() { return impl::s_mainWindowHandle; } bool isBorderlessWindowModeEnabled() { return impl::s_borderlessWindowMode; } bool isMutliWindowModeEnabled() { return impl::s_multiWindowMode; } std::optional getInitialWindowProperties() { return impl::s_initialWindowProperties; } void* getLibImHexModuleHandle() { return hex::getContainingModule((void*)&getLibImHexModuleHandle); } const std::map& getInitArguments() { return *impl::s_initArguments; } std::string getInitArgument(const std::string &key) { if (impl::s_initArguments->contains(key)) return impl::s_initArguments->at(key); else return ""; } static bool s_systemThemeDetection; void enableSystemThemeDetection(bool enabled) { s_systemThemeDetection = enabled; EventOSThemeChanged::post(); } bool usesSystemThemeDetection() { return s_systemThemeDetection; } static AutoReset> s_additionalFolderPaths; const std::vector& getAdditionalFolderPaths() { return *s_additionalFolderPaths; } void setAdditionalFolderPaths(const std::vector &paths) { s_additionalFolderPaths = paths; } const std::string &getGPUVendor() { return impl::s_gpuVendor; } bool isPortableVersion() { static std::optional portable; if (portable.has_value()) return portable.value(); if (const auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) { const auto flagFile = executablePath->parent_path() / "PORTABLE"; portable = wolv::io::fs::exists(flagFile) && wolv::io::fs::isRegularFile(flagFile); } else { portable = false; } return portable.value(); } std::string getOSName() { #if defined(OS_WINDOWS) return "Windows"; #elif defined(OS_LINUX) #if defined(OS_FREEBSD) return "FreeBSD"; #else return "Linux"; #endif #elif defined(OS_MACOS) return "macOS"; #elif defined(OS_WEB) return "Web"; #else return "Unknown"; #endif } std::string getOSVersion() { #if defined(OS_WINDOWS) OSVERSIONINFOA info; info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); ::GetVersionExA(&info); return hex::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber); #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) struct utsname details = { }; if (uname(&details) != 0) { return "Unknown"; } return std::string(details.release) + " " + std::string(details.version); #else return "Unknown"; #endif } std::string getArchitecture() { #if defined(OS_WINDOWS) SYSTEM_INFO info; ::GetNativeSystemInfo(&info); switch (info.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64"; case PROCESSOR_ARCHITECTURE_ARM: return "ARM"; case PROCESSOR_ARCHITECTURE_ARM64: return "ARM64"; case PROCESSOR_ARCHITECTURE_IA64: return "IA64"; case PROCESSOR_ARCHITECTURE_INTEL: return "x86"; default: return "Unknown"; } #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) struct utsname details = { }; if (uname(&details) != 0) { return "Unknown"; } return { details.machine }; #else return "Unknown"; #endif } std::string getImHexVersion(bool withBuildType) { #if defined IMHEX_VERSION if (withBuildType) { return IMHEX_VERSION; } else { auto version = std::string(IMHEX_VERSION); return version.substr(0, version.find('-')); } #else return "Unknown"; #endif } std::string getCommitHash(bool longHash) { #if defined GIT_COMMIT_HASH_LONG if (longHash) { return GIT_COMMIT_HASH_LONG; } else { return std::string(GIT_COMMIT_HASH_LONG).substr(0, 7); } #else hex::unused(longHash); return "Unknown"; #endif } std::string getCommitBranch() { #if defined GIT_BRANCH return GIT_BRANCH; #else return "Unknown"; #endif } bool isDebugBuild() { #if defined DEBUG return true; #else return false; #endif } bool updateImHex(UpdateType updateType) { // Get the path of the updater executable std::fs::path executablePath; for (const auto &entry : std::fs::directory_iterator(wolv::io::fs::getExecutablePath()->parent_path())) { if (entry.path().filename().string().starts_with("imhex-updater")) { executablePath = entry.path(); break; } } if (executablePath.empty() || !wolv::io::fs::exists(executablePath)) return false; std::string updateTypeString; switch (updateType) { case UpdateType::Stable: updateTypeString = "latest"; break; case UpdateType::Nightly: updateTypeString = "nightly"; break; } EventImHexClosing::subscribe([executablePath, updateTypeString] { hex::executeCommand( hex::format("{} {}", wolv::util::toUTF8String(executablePath), updateTypeString ) ); }); ImHexApi::System::closeImHex(); return true; } void addStartupTask(const std::string &name, bool async, const std::function &function) { RequestAddInitTask::post(name, async, function); } double getLastFrameTime() { return impl::s_lastFrameTime; } void setWindowResizable(bool resizable) { glfwSetWindowAttrib(impl::s_mainWindowHandle, GLFW_RESIZABLE, int(resizable)); impl::s_windowResizable = resizable; } } namespace ImHexApi::Messaging { namespace impl { static AutoReset> s_handlers; const std::map& getHandlers() { return *s_handlers; } void runHandler(const std::string &eventName, const std::vector &args) { const auto& handlers = getHandlers(); const auto matchHandler = handlers.find(eventName); if (matchHandler == handlers.end()) { log::error("Forward event handler {} not found", eventName); } else { matchHandler->second(args); } } } void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler) { log::debug("Registered new forward event handler: {}", eventName); impl::s_handlers->insert({ eventName, handler }); } } namespace ImHexApi::Fonts { namespace impl { static AutoReset> s_fonts; const std::vector& getFonts() { return *s_fonts; } static AutoReset s_customFontPath; void setCustomFontPath(const std::fs::path &path) { s_customFontPath = path; } static float s_fontSize = DefaultFontSize; void setFontSize(float size) { s_fontSize = size; } static AutoReset> s_fontAtlas; void setFontAtlas(ImFontAtlas* fontAtlas) { s_fontAtlas = std::unique_ptr(fontAtlas, IM_DELETE); } static ImFont *s_boldFont = nullptr; static ImFont *s_italicFont = nullptr; void setFonts(ImFont *bold, ImFont *italic) { s_boldFont = bold; s_italicFont = italic; } } GlyphRange glyph(const char *glyph) { u32 codepoint; ImTextCharFromUtf8(&codepoint, glyph, nullptr); return { .begin = u16(codepoint), .end = u16(codepoint) }; } GlyphRange glyph(u32 codepoint) { return { .begin = u16(codepoint), .end = u16(codepoint) }; } GlyphRange range(const char *glyphBegin, const char *glyphEnd) { u32 codepointBegin, codepointEnd; ImTextCharFromUtf8(&codepointBegin, glyphBegin, nullptr); ImTextCharFromUtf8(&codepointEnd, glyphEnd, nullptr); return { .begin = u16(codepointBegin), .end = u16(codepointEnd) }; } GlyphRange range(u32 codepointBegin, u32 codepointEnd) { return { .begin = u16(codepointBegin), .end = u16(codepointEnd) }; } void loadFont(const std::fs::path &path, const std::vector &glyphRanges, Offset offset, u32 flags, std::optional 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)); return; } impl::s_fonts->emplace_back(Font { wolv::util::toUTF8String(path.filename()), fontFile.readVector(), glyphRanges, offset, flags, defaultSize }); } void loadFont(const std::string &name, const std::span &data, const std::vector &glyphRanges, Offset offset, u32 flags, std::optional defaultSize) { impl::s_fonts->emplace_back(Font { name, { data.begin(), data.end() }, glyphRanges, offset, flags, defaultSize }); } const std::fs::path& getCustomFontPath() { return impl::s_customFontPath; } float getFontSize() { return impl::s_fontSize; } ImFontAtlas* getFontAtlas() { return impl::s_fontAtlas->get(); } ImFont* Bold() { return impl::s_boldFont; } ImFont* Italic() { return impl::s_italicFont; } } }