#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace hex::plugin::builtin { namespace { ImGuiExt::Texture s_bannerTexture, s_nightlyTexture, s_backdropTexture, s_infoBannerTexture; std::string s_tipOfTheDay; bool s_simplifiedWelcomeScreen = false; class PopupRestoreBackup : public Popup { private: std::fs::path m_logFilePath; std::function m_restoreCallback; std::function m_deleteCallback; bool m_reportError = true; public: PopupRestoreBackup(std::fs::path logFilePath, const std::function &restoreCallback, const std::function &deleteCallback) : Popup("hex.builtin.popup.safety_backup.title"), m_logFilePath(std::move(logFilePath)), m_restoreCallback(restoreCallback), m_deleteCallback(deleteCallback) { m_reportError = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", true); } void drawContent() override { ImGui::TextUnformatted("hex.builtin.popup.safety_backup.desc"_lang); if (!m_logFilePath.empty()) { ImGui::NewLine(); ImGui::TextUnformatted("hex.builtin.popup.safety_backup.log_file"_lang); ImGui::SameLine(0, 2_scaled); if (ImGuiExt::Hyperlink(m_logFilePath.filename().string().c_str())) { fs::openFolderWithSelectionExternal(m_logFilePath); } ImGui::Checkbox("hex.builtin.popup.safety_backup.report_error"_lang, &m_reportError); ImGui::NewLine(); } auto width = ImGui::GetWindowWidth(); ImGui::SetCursorPosX(width / 9); if (ImGui::Button("hex.builtin.popup.safety_backup.restore"_lang, ImVec2(width / 3, 0))) { m_restoreCallback(); m_deleteCallback(); if (m_reportError) { wolv::io::File logFile(m_logFilePath, wolv::io::File::Mode::Read); if (logFile.isValid()) { // Read current log file data auto data = logFile.readString(); // Anonymize the log file { for (const auto &paths : paths::All) { for (auto &folder : paths->all()) { auto parent = wolv::util::toUTF8String(folder.parent_path()); data = wolv::util::replaceStrings(data, parent, "<*****>"); } } } TaskManager::createBackgroundTask("Upload Crash report"_lang, [path = m_logFilePath, data](auto&){ HttpRequest request("POST", ImHexApiURL + std::string("/crash_upload")); request.uploadFile(std::vector(data.begin(), data.end()), "file", path.filename()).wait(); }); } } ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", m_reportError); this->close(); } ImGui::SameLine(); ImGui::SetCursorPosX(width / 9 * 5); if (ImGui::Button("hex.builtin.popup.safety_backup.delete"_lang, ImVec2(width / 3, 0)) || ImGui::IsKeyPressed(ImGuiKey_Escape)) { m_deleteCallback(); this->close(); } } }; class PopupTipOfTheDay : public Popup { public: PopupTipOfTheDay() : Popup("hex.builtin.popup.tip_of_the_day.title", true, false) { } void drawContent() override { ImGuiExt::Header("hex.builtin.welcome.tip_of_the_day"_lang, true); ImGuiExt::TextFormattedWrapped("{}", s_tipOfTheDay.c_str()); ImGui::NewLine(); static bool dontShowAgain = false; if (ImGui::Checkbox("hex.ui.common.dont_show_again"_lang, &dontShowAgain)) { ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.show_tips", !dontShowAgain); } ImGui::SameLine((ImGui::GetMainViewport()->Size / 3 - ImGui::CalcTextSize("hex.ui.common.close"_lang) - ImGui::GetStyle().FramePadding).x); if (ImGui::Button("hex.ui.common.close"_lang) || ImGui::IsKeyPressed(ImGuiKey_Escape)) Popup::close(); } }; void loadDefaultLayout() { LayoutManager::loadFromString(std::string(romfs::get("layouts/default.hexlyt").string())); } bool isAnyViewOpen() { const auto &views = ContentRegistry::Views::impl::getEntries(); return std::any_of(views.begin(), views.end(), [](const auto &entry) { return entry.second->getWindowOpenState(); }); } void drawWelcomeScreenBackground() { const auto position = ImGui::GetWindowPos(); const auto size = ImGui::GetWindowSize(); auto drawList = ImGui::GetWindowDrawList(); const auto lineDistance = 20_scaled; const auto lineColor = ImGui::GetColorU32(ImGuiCol_Text, 0.03F); for (auto x = position.x; x < position.x + size.x + lineDistance; x += lineDistance) { drawList->AddLine({ x, position.y }, { x, position.y + size.y }, lineColor); } for (auto y = position.y; y < position.y + size.y + lineDistance; y += lineDistance) { drawList->AddLine({ position.x, y }, { position.x + size.x, y }, lineColor); } } void drawWelcomeScreenContentSimplified() { const ImVec2 backdropSize = scaled({ 350, 350 }); ImGui::SetCursorPos((ImGui::GetContentRegionAvail() - backdropSize) / 2); ImGui::Image(s_backdropTexture, backdropSize); ImGuiExt::TextFormattedCentered("hex.builtin.welcome.drop_file"_lang); } void drawWelcomeScreenContentFull() { const ImVec2 margin = scaled({ 30, 20 }); ImGui::SetCursorPos(margin); if (ImGui::BeginTable("Welcome Outer", 1, ImGuiTableFlags_None, ImGui::GetContentRegionAvail() - margin)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Image(s_bannerTexture, s_bannerTexture.getSize()); if (ImHexApi::System::isNightlyBuild()) { auto cursor = ImGui::GetCursorPos(); ImGui::SameLine(0); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 15_scaled); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled); ImGui::Image(s_nightlyTexture, s_nightlyTexture.getSize()); ImGuiExt::InfoTooltip("hex.builtin.welcome.nightly_build"_lang); ImGui::SetCursorPos(cursor); } ImGui::NewLine(); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_PopupBg)); ON_SCOPE_EXIT { ImGui::PopStyleColor(); }; ImGui::TableNextRow(); ImGui::TableNextColumn(); const auto availableSpace = ImGui::GetContentRegionAvail(); if (ImGui::BeginTable("Welcome Left", 1, ImGuiTableFlags_NoBordersInBody, ImVec2(availableSpace.x / 2, 0))) { ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 6); ImGui::TableNextColumn(); static bool otherProvidersVisible = false; ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled); { auto startPos = ImGui::GetCursorPos(); if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.start"_lang, nullptr, ImVec2(), ImGuiChildFlags_AutoResizeX)) { if (ImGuiExt::IconHyperlink(ICON_VS_NEW_FILE, "hex.builtin.welcome.start.create_file"_lang)) { auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true); if (newProvider != nullptr && !newProvider->open()) hex::ImHexApi::Provider::remove(newProvider); else EventProviderOpened::post(newProvider); } if (ImGuiExt::IconHyperlink(ICON_VS_GO_TO_FILE, "hex.builtin.welcome.start.open_file"_lang)) RequestOpenWindow::post("Open File"); if (ImGuiExt::IconHyperlink(ICON_VS_NOTEBOOK, "hex.builtin.welcome.start.open_project"_lang)) RequestOpenWindow::post("Open Project"); if (ImGuiExt::IconHyperlink(ICON_VS_TELESCOPE, "hex.builtin.welcome.start.open_other"_lang)) otherProvidersVisible = !otherProvidersVisible; } ImGuiExt::EndSubWindow(); auto endPos = ImGui::GetCursorPos(); if (otherProvidersVisible) { ImGui::SameLine(0, 2_scaled); ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2(0, (endPos - startPos).y / 2)); ImGui::TextUnformatted(ICON_VS_ARROW_RIGHT); ImGui::SameLine(0, 2_scaled); if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.start.open_other"_lang, nullptr, ImVec2(200_scaled, ImGui::GetTextLineHeightWithSpacing() * 6), ImGuiChildFlags_AutoResizeX)) { for (const auto &unlocalizedProviderName : ContentRegistry::Provider::impl::getEntries()) { if (ImGuiExt::Hyperlink(Lang(unlocalizedProviderName))) { ImHexApi::Provider::createProvider(unlocalizedProviderName); otherProvidersVisible = false; } } } ImGuiExt::EndSubWindow(); } } // Draw recent entries ImGui::Dummy({}); recent::draw(); ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 6); ImGui::TableNextColumn(); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled); if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.help"_lang, nullptr, ImVec2(), ImGuiChildFlags_AutoResizeX)) { if (ImGuiExt::IconHyperlink(ICON_VS_GITHUB, "hex.builtin.welcome.help.repo"_lang)) hex::openWebpage("hex.builtin.welcome.help.repo.link"_lang); if (ImGuiExt::IconHyperlink(ICON_VS_ORGANIZATION, "hex.builtin.welcome.help.gethelp"_lang)) hex::openWebpage("hex.builtin.welcome.help.gethelp.link"_lang); if (ImGuiExt::IconHyperlink(ICON_VS_COMMENT_DISCUSSION, "hex.builtin.welcome.help.discord"_lang)) hex::openWebpage("hex.builtin.welcome.help.discord.link"_lang); } ImGuiExt::EndSubWindow(); if (ImHexApi::System::getInitArguments().contains("update-available")) { ImGui::TableNextRow(); ImGui::TableNextColumn(); if (ImGuiExt::DescriptionButton("hex.builtin.welcome.update.title"_lang, hex::format("hex.builtin.welcome.update.desc"_lang, ImHexApi::System::getInitArgument("update-available")).c_str(), ImVec2(ImGui::GetContentRegionAvail().x * 0.8F, 0))) ImHexApi::System::updateImHex(ImHexApi::System::UpdateType::Stable); } ImGui::EndTable(); } ImGui::SameLine(); if (ImGui::BeginTable("Welcome Right", 1, ImGuiTableFlags_NoBordersInBody, ImVec2(availableSpace.x / 2, 0))) { ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 5); ImGui::TableNextColumn(); auto windowPadding = ImGui::GetStyle().WindowPadding.x * 3; if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.customize"_lang, nullptr, ImVec2(ImGui::GetContentRegionAvail().x - windowPadding, 0), ImGuiChildFlags_AutoResizeX)) { if (ImGuiExt::DescriptionButton("hex.builtin.welcome.customize.settings.title"_lang, "hex.builtin.welcome.customize.settings.desc"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) RequestOpenWindow::post("Settings"); } ImGuiExt::EndSubWindow(); ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 5); ImGui::TableNextColumn(); if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.learn"_lang, nullptr, ImVec2(ImGui::GetContentRegionAvail().x - windowPadding, 0), ImGuiChildFlags_AutoResizeX)) { const auto size = ImVec2(ImGui::GetContentRegionAvail().x, 0); if (ImGuiExt::DescriptionButton("hex.builtin.welcome.learn.latest.title"_lang, "hex.builtin.welcome.learn.latest.desc"_lang, size)) hex::openWebpage("hex.builtin.welcome.learn.latest.link"_lang); if (ImGuiExt::DescriptionButton("hex.builtin.welcome.learn.imhex.title"_lang, "hex.builtin.welcome.learn.imhex.desc"_lang, size)) { AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.docs.name"); hex::openWebpage("hex.builtin.welcome.learn.imhex.link"_lang); } if (ImGuiExt::DescriptionButton("hex.builtin.welcome.learn.pattern.title"_lang, "hex.builtin.welcome.learn.pattern.desc"_lang, size)) hex::openWebpage("hex.builtin.welcome.learn.pattern.link"_lang); if (ImGuiExt::DescriptionButton("hex.builtin.welcome.learn.plugins.title"_lang, "hex.builtin.welcome.learn.plugins.desc"_lang, size)) hex::openWebpage("hex.builtin.welcome.learn.plugins.link"_lang); ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, 3_scaled); if (ImGuiExt::DescriptionButton("hex.builtin.welcome.learn.interactive_tutorial.title"_lang, "hex.builtin.welcome.learn.interactive_tutorial.desc"_lang, size)) { RequestOpenWindow::post("Tutorials"); } if (auto [unlocked, total] = AchievementManager::getProgress(); unlocked != total) { if (ImGuiExt::DescriptionButtonProgress("hex.builtin.welcome.learn.achievements.title"_lang, "hex.builtin.welcome.learn.achievements.desc"_lang, float(unlocked) / float(total), size)) { RequestOpenWindow::post("Achievements"); } } } ImGuiExt::EndSubWindow(); auto extraWelcomeScreenEntries = ContentRegistry::Interface::impl::getWelcomeScreenEntries(); if (!extraWelcomeScreenEntries.empty()) { ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 5); ImGui::TableNextColumn(); if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.various"_lang, nullptr, ImVec2(ImGui::GetContentRegionAvail().x - windowPadding, 0))) { for (const auto &callback : extraWelcomeScreenEntries) callback(); } ImGuiExt::EndSubWindow(); } if (s_infoBannerTexture.isValid()) { static bool hovered = false; ImGui::PushStyleColor(ImGuiCol_Border, ImGui::GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Border)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.info"_lang, nullptr, ImVec2(), ImGuiChildFlags_AutoResizeX)) { const auto height = 80_scaled; ImGui::Image(s_infoBannerTexture, ImVec2(height * s_infoBannerTexture.getAspectRatio(), height)); hovered = ImGui::IsItemHovered(); if (ImGui::IsItemClicked()) { hex::openWebpage(ImHexApiURL + hex::format("/info/{}/link", hex::toLower(ImHexApi::System::getOSName()))); } } ImGuiExt::EndSubWindow(); ImGui::PopStyleVar(); ImGui::PopStyleColor(); } ImGui::EndTable(); } ImGui::EndTable(); } ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x * 2, ImGui::GetStyle().FramePadding.y * 2 - 1)); if (ImGuiExt::DimmedIconButton(ICON_VS_CLOSE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.null"); if (provider != nullptr) if (provider->open()) EventProviderOpened::post(provider); } } void drawWelcomeScreen() { ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0); ImGui::PushStyleColor(ImGuiCol_WindowShadow, 0x00); if (ImGui::Begin("ImHexDockSpace", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus)) { if (!ImHexApi::Provider::isValid()) { static auto title = []{ std::array title = {}; ImFormatString(title.data(), title.size(), "%s/DockSpace_%08X", ImGui::GetCurrentWindowRead()->Name, ImGui::GetID("ImHexMainDock")); return title; }(); if (ImGui::Begin(title.data(), nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus)) { ImGui::Dummy({}); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, scaled({ 10, 10 })); ImGui::SetNextWindowScroll({ 0.0F, -1.0F }); ImGui::SetNextWindowSize(ImGui::GetContentRegionAvail() + scaled({ 0, 10 })); ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos() - ImVec2(0, ImGui::GetStyle().FramePadding.y + 2_scaled)); ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); if (ImGui::Begin("Welcome Screen", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { ImGui::BringWindowToDisplayBack(ImGui::GetCurrentWindowRead()); drawWelcomeScreenBackground(); if (s_simplifiedWelcomeScreen) drawWelcomeScreenContentSimplified(); else drawWelcomeScreenContentFull(); static bool hovered = false; ImGui::PushStyleVar(ImGuiStyleVar_Alpha, hovered ? 1.0F : 0.3F); { const ImVec2 windowSize = scaled({ 150, 60 }); ImGui::SetCursorScreenPos(ImGui::GetWindowPos() + ImGui::GetWindowSize() - windowSize - ImGui::GetStyle().WindowPadding); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg)); if (ImGuiExt::BeginSubWindow("hex.builtin.welcome.header.quick_settings"_lang, nullptr, windowSize, ImGuiChildFlags_AutoResizeY)) { if (ImGuiExt::ToggleSwitch("hex.builtin.welcome.quick_settings.simplified"_lang, &s_simplifiedWelcomeScreen)) { ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.simplified_welcome_screen", s_simplifiedWelcomeScreen); WorkspaceManager::switchWorkspace(s_simplifiedWelcomeScreen ? "Minimal" : "Default"); } } ImGuiExt::EndSubWindow(); ImGui::PopStyleColor(); hovered = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } ImGui::PopStyleVar(); } ImGui::End(); ImGui::PopStyleVar(); } ImGui::End(); ImGui::BringWindowToDisplayBack(ImGui::GetCurrentWindowRead()); } } ImGui::End(); ImGui::PopStyleColor(); ImGui::PopStyleVar(); } /** * @brief Draw some default background if there are no views available in the current layout */ void drawNoViewsBackground() { ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0); ImGui::PushStyleColor(ImGuiCol_WindowShadow, 0x00); if (ImGui::Begin("ImHexDockSpace", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus)) { static std::array title; ImFormatString(title.data(), title.size(), "%s/DockSpace_%08X", ImGui::GetCurrentWindowRead()->Name, ImGui::GetID("ImHexMainDock")); if (ImGui::Begin(title.data())) { ImGui::Dummy({}); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, scaled({ 10, 10 })); ImGui::SetNextWindowScroll({ 0.0F, -1.0F }); ImGui::SetNextWindowSize(ImGui::GetContentRegionAvail() + scaled({ 0, 10 })); ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos() - ImVec2(0, ImGui::GetStyle().FramePadding.y + 2_scaled)); ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); if (ImGui::Begin("Welcome Screen", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { auto imageSize = scaled(ImVec2(350, 350)); auto imagePos = (ImGui::GetContentRegionAvail() - imageSize) / 2; ImGui::SetCursorPos(imagePos); ImGui::Image(s_backdropTexture, imageSize); auto loadDefaultText = "hex.builtin.layouts.none.restore_default"_lang; auto textSize = ImGui::CalcTextSize(loadDefaultText); auto textPos = ImVec2( (ImGui::GetContentRegionAvail().x - textSize.x) / 2, imagePos.y + imageSize.y ); ImGui::SetCursorPos(textPos); if (ImGuiExt::DimmedButton(loadDefaultText)){ loadDefaultLayout(); } } ImGui::End(); ImGui::PopStyleVar(); } ImGui::End(); } ImGui::End(); ImGui::PopStyleColor(); ImGui::PopStyleVar(); } } /** * @brief Registers the event handlers related to the welcome screen * should only be called once, at startup */ void createWelcomeScreen() { recent::registerEventHandlers(); recent::updateRecentEntries(); EventFrameBegin::subscribe(drawWelcomeScreen); // Sets a background when they are no views EventFrameBegin::subscribe([]{ if (ImHexApi::Provider::isValid() && !isAnyViewOpen()) drawNoViewsBackground(); }); ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", [](const ContentRegistry::Settings::SettingsValue &value) { auto theme = value.get("Dark"); if (theme != ThemeManager::NativeTheme) { static std::string lastTheme; if (theme != lastTheme) { RequestChangeTheme::post(theme); lastTheme = theme; } } }); ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.simplified_welcome_screen", [](const ContentRegistry::Settings::SettingsValue &value) { s_simplifiedWelcomeScreen = value.get(false); }); ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", [](const ContentRegistry::Settings::SettingsValue &value) { auto language = value.get("en-US"); if (language != LocalizationManager::getSelectedLanguage()) LocalizationManager::loadLanguage(language); }); ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.fps", [](const ContentRegistry::Settings::SettingsValue &value) { ImHexApi::System::setTargetFPS(static_cast(value.get(14))); }); RequestChangeTheme::subscribe([](const std::string &theme) { auto changeTexture = [&](const std::string &path) { return ImGuiExt::Texture::fromImage(romfs::get(path).span(), ImGuiExt::Texture::Filter::Nearest); }; auto changeTextureSvg = [&](const std::string &path, float width) { return ImGuiExt::Texture::fromSVG(romfs::get(path).span(), width, 0, ImGuiExt::Texture::Filter::Linear); }; ThemeManager::changeTheme(theme); s_bannerTexture = changeTextureSvg(hex::format("assets/{}/banner.svg", ThemeManager::getImageTheme()), 300_scaled); s_nightlyTexture = changeTextureSvg(hex::format("assets/{}/nightly.svg", "common"), 35_scaled); s_backdropTexture = changeTexture(hex::format("assets/{}/backdrop.png", ThemeManager::getImageTheme())); if (!s_bannerTexture.isValid()) { log::error("Failed to load banner texture!"); } }); // Clear project context if we go back to the welcome screen EventProviderChanged::subscribe([](const hex::prv::Provider *oldProvider, const hex::prv::Provider *newProvider) { hex::unused(oldProvider); if (newProvider == nullptr) { ProjectFile::clearPath(); RequestUpdateWindowTitle::post(); } }); recent::addMenuItems(); // Check for crash backup constexpr static auto CrashFileName = "crash.json"; constexpr static auto BackupFileName = "crash_backup.hexproj"; bool hasCrashed = false; for (const auto &path : paths::Config.read()) { if (auto crashFilePath = std::fs::path(path) / CrashFileName; wolv::io::fs::exists(crashFilePath)) { hasCrashed = true; log::info("Found crash.json file at {}", wolv::util::toUTF8String(crashFilePath)); wolv::io::File crashFile(crashFilePath, wolv::io::File::Mode::Read); nlohmann::json crashFileData; try { crashFileData = nlohmann::json::parse(crashFile.readString()); } catch (nlohmann::json::exception &e) { log::error("Failed to parse crash.json file: {}", e.what()); crashFile.remove(); continue; } bool hasProject = !crashFileData.value("project", "").empty(); auto backupFilePath = path / BackupFileName; auto backupFilePathOld = path / BackupFileName; backupFilePathOld.replace_extension(".hexproj.old"); bool hasBackupFile = wolv::io::fs::exists(backupFilePath); if (!hasProject && !hasBackupFile) { log::warn("No project file or backup file found in crash.json file"); crashFile.close(); // Delete crash.json file wolv::io::fs::remove(crashFilePath); // Delete old backup file wolv::io::fs::remove(backupFilePathOld); // Try to move current backup file to the old backup location if (wolv::io::fs::copyFile(backupFilePath, backupFilePathOld)) { wolv::io::fs::remove(backupFilePath); } continue; } PopupRestoreBackup::open( // Path of log file crashFileData.value("logFile", ""), // Restore callback [crashFileData, backupFilePath, hasProject, hasBackupFile] { if (hasBackupFile) { if (ProjectFile::load(backupFilePath)) { if (hasProject) { ProjectFile::setPath(crashFileData["project"].get()); } else { ProjectFile::setPath(""); } RequestUpdateWindowTitle::post(); } else { ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(backupFilePath))); } } else { if (hasProject) { ProjectFile::setPath(crashFileData["project"].get()); } } }, // Delete callback (also executed after restore) [crashFilePath, backupFilePath] { wolv::io::fs::remove(crashFilePath); wolv::io::fs::remove(backupFilePath); } ); } } // Tip of the day auto tipsData = romfs::get("tips.json"); if (!hasCrashed && tipsData.valid()) { auto tipsCategories = nlohmann::json::parse(tipsData.string()); auto now = std::chrono::system_clock::now(); auto daysSinceEpoch = std::chrono::duration_cast(now.time_since_epoch()); std::mt19937 random(daysSinceEpoch.count()); auto chosenCategory = tipsCategories[random()%tipsCategories.size()].at("tips"); auto chosenTip = chosenCategory[random()%chosenCategory.size()]; s_tipOfTheDay = chosenTip.get(); bool showTipOfTheDay = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.show_tips", false); if (showTipOfTheDay) PopupTipOfTheDay::open(); } if (hasCrashed) { TaskManager::doLater([]{ AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.crash.name"); }); } // Load info banner texture either locally or from the server TaskManager::doLater([] { for (const auto &defaultPath : paths::Resources.read()) { const auto infoBannerPath = defaultPath / "info_banner.png"; if (wolv::io::fs::exists(infoBannerPath)) { s_infoBannerTexture = ImGuiExt::Texture::fromImage(infoBannerPath, ImGuiExt::Texture::Filter::Linear); if (s_infoBannerTexture.isValid()) break; } } auto allowNetworking = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.network_interface", false) && ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.server_contact", 0) != 0; if (!s_infoBannerTexture.isValid() && allowNetworking) { TaskManager::createBackgroundTask("Load banner"_lang, [](auto&) { HttpRequest request("GET", ImHexApiURL + hex::format("/info/{}/image", hex::toLower(ImHexApi::System::getOSName()))); auto response = request.downloadFile().get(); if (response.isSuccess()) { const auto &data = response.getData(); if (!data.empty()) { TaskManager::doLater([data] { s_infoBannerTexture = ImGuiExt::Texture::fromImage(data.data(), data.size(), ImGuiExt::Texture::Filter::Linear); }); } } }); } }); } }