// MassBuilderSaveTool // Copyright (C) 2021-2022 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include #include #include #include #include #include "../FontAwesome/IconsFontAwesome5.h" #include "../FontAwesome/IconsFontAwesome5Brands.h" #include "../Logger/Logger.h" #include "SaveTool.h" void SaveTool::initEvent(SDL_Event& event) { _initThread.join(); switch(event.user.code) { case InitSuccess: _uiState = UiState::ProfileManager; ImGui::CloseCurrentPopup(); break; case ProfileManagerFailure: SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error ", _profileManager->lastError().data(), window()); exit(EXIT_FAILURE); break; default: break; } } void SaveTool::initialiseConfiguration() { LOG_INFO("Reading configuration file."); if(_conf.hasValue("cheat_mode"_s)) { _cheatMode = _conf.value("cheat_mode"_s); } else { _conf.setValue("cheat_mode"_s, _cheatMode); } if(_conf.hasValue("advanced_mode"_s)) { _advancedMode = _conf.value("advanced_mode"_s); } else { _conf.setValue("advanced_mode"_s, _advancedMode); } if(_conf.hasValue("startup_update_check"_s)) { _checkUpdatesOnStartup = _conf.value("startup_update_check"_s); } else { _conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup); } if(_conf.hasValue("skip_disclaimer"_s)) { _skipDisclaimer = _conf.value("skip_disclaimer"_s); } else { _conf.setValue("skip_disclaimer"_s, _skipDisclaimer); } if(_conf.hasValue("swap_interval"_s)) { _swapInterval = _conf.value("swap_interval"_s); } else { _conf.setValue("swap_interval"_s, 1); } if(_conf.hasValue("fps_cap"_s)) { _fpsCap = _conf.value("fps_cap"); } else { _conf.setValue("fps_cap", 60.0f); } if(_conf.hasValue("frame_limit"_s)) { std::string frame_limit = _conf.value("frame_limit"_s); if(frame_limit == "half_vsync"_s) { _swapInterval = 2; } _conf.removeValue("frame_limit"_s); } setSwapInterval(_swapInterval); if(_swapInterval == 0) { setMinimalLoopPeriod(0); } _conf.save(); } void SaveTool::initialiseGui() { LOG_INFO("Initialising Dear ImGui."); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); const auto size = Vector2{windowSize()}/dpiScaling(); auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf"_s); ImFontConfig font_config; font_config.FontDataOwnedByAtlas = false; std::strcpy(font_config.Name, "Source Sans Pro"); io.Fonts->AddFontFromMemoryTTF(const_cast(reg_font.data()), int(reg_font.size()), 20.0f * Float(framebufferSize().x()) / size.x(), &font_config); auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS); static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig icon_config; icon_config.FontDataOwnedByAtlas = false; icon_config.MergeMode = true; icon_config.PixelSnapH = true; icon_config.OversampleH = icon_config.OversampleV = 1; icon_config.GlyphMinAdvanceX = 18.0f; io.Fonts->AddFontFromMemoryTTF(const_cast(icon_font.data()), int(icon_font.size()), 16.0f * Float(framebufferSize().x()) / size.x(), &icon_config, icon_range); auto brand_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAB); static const ImWchar brand_range[] = { ICON_MIN_FAB, ICON_MAX_FAB, 0 }; io.Fonts->AddFontFromMemoryTTF(const_cast(brand_font.data()), int(brand_font.size()), 16.0f * Float(framebufferSize().x()) / size.x(), &icon_config, brand_range); auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf"_s); ImVector range; ImFontGlyphRangesBuilder builder; builder.AddRanges(io.Fonts->GetGlyphRangesDefault()); builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences. builder.BuildRanges(&range); io.Fonts->AddFontFromMemoryTTF(const_cast(mono_font.data()), int(mono_font.size()), 18.0f * Float(framebufferSize().x()) / size.x(), &font_config, range.Data); _imgui = ImGuiIntegration::Context(*ImGui::GetCurrentContext(), windowSize()); io.IniFilename = nullptr; ImGuiStyle& style = ImGui::GetStyle(); style.WindowTitleAlign = {0.5f, 0.5f}; style.FrameRounding = 3.2f; style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f); } void SaveTool::initialiseManager() { LOG_INFO("Initialising the profile manager."); SDL_Event event; SDL_zero(event); event.type = _initEventId; _profileManager.emplace(_saveDir, _backupsDir); if(!_profileManager->ready()) { event.user.code = ProfileManagerFailure; SDL_PushEvent(&event); return; } event.user.code = InitSuccess; SDL_PushEvent(&event); } auto SaveTool::initialiseToolDirectories() -> bool { LOG_INFO("Initialising Save Tool directories."); _backupsDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "backups"); _stagingDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "staging"); //_armouryDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "armoury"); //_armoursDir = Utility::Directory::join(_armouryDir, "armours"); //_weaponsDir = Utility::Directory::join(_armouryDir, "weapons"); //_stylesDir = Utility::Directory::join(_armouryDir, "styles"); if(!Utility::Path::exists(_backupsDir)) { LOG_WARNING("Backups directory not found, creating..."); if(!Utility::Path::make(_backupsDir)) { LOG_ERROR(_lastError = "Couldn't create the backups directory."); return false; } } if(!Utility::Path::exists(_stagingDir)) { LOG_WARNING("Staging directory not found, creating..."); if(!Utility::Path::make(_stagingDir)) { LOG_ERROR(_lastError = "Couldn't create the staging directory."); return false; } } //if(!Utility::Directory::exists(_armouryDir)) { // Utility::Debug{} << "Armoury directory not found, creating..."; // if(!Utility::Path::make(_armouryDir)) { // Utility::Error{} << (_lastError = "Couldn't create the armoury directory."); // return false; // } //} //if(!Utility::Directory::exists(_armoursDir)) { // Utility::Debug{} << "Armours directory not found, creating..."; // if(!Utility::Path::make(_armoursDir)) { // Utility::Error{} << (_lastError = "Couldn't create the armours directory."); // return false; // } //} //if(!Utility::Directory::exists(_weaponsDir)) { // Utility::Debug{} << "Weapons directory not found, creating..."; // if(!Utility::Path::make(_weaponsDir)) { // Utility::Error{} << (_lastError = "Couldn't create the weapons directory."); // return false; // } //} //if(!Utility::Directory::exists(_stylesDir)) { // Utility::Debug{} << "Styles directory not found, creating..."; // if(!Utility::Path::make(_stylesDir)) { // Utility::Error{} << (_lastError = "Couldn't create the styles directory."); // return false; // } //} return true; } auto SaveTool::findGameDataDirectory() -> bool { LOG_INFO("Searching for the game's save directory."); wchar_t* localappdata_path = nullptr; Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree}; if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK) { _lastError = Utility::format("SHGetKnownFolderPath() failed with error code {}.", GetLastError()); LOG_ERROR(_lastError); return false; } _gameDataDir = Utility::Path::join(Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder"_s); if(!Utility::Path::exists(_gameDataDir)) { LOG_ERROR(_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s); return false; } _configDir = Utility::Path::join(_gameDataDir, "Saved/Config/WindowsNoEditor"_s); _saveDir = Utility::Path::join(_gameDataDir, "Saved/SaveGames"_s); _screenshotsDir = Utility::Path::join(_gameDataDir, "Saved/Screenshots/WindowsNoEditor"_s); return true; } void SaveTool::initialiseMassManager() { LOG_INFO("Initialising the M.A.S.S. manager."); _massManager.emplace(_saveDir, _currentProfile->account(), _currentProfile->isDemo(), _stagingDir); } void SaveTool::initialiseFileWatcher() { LOG_INFO("Initialising the file watcher."); _fileWatcher.emplace(); _watchIDs[SaveDir] = _fileWatcher->addWatch(_saveDir, this, false); _watchIDs[StagingDir] = _fileWatcher->addWatch(_stagingDir, this, false); _fileWatcher->watch(); }