2022-04-01 09:36:33 +02:00
|
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include <Corrade/Containers/Pair.h>
|
|
|
|
#include <Corrade/Containers/ScopeGuard.h>
|
|
|
|
#include <Corrade/Utility/Path.h>
|
|
|
|
#include <Corrade/Utility/Unicode.h>
|
|
|
|
|
2022-11-21 09:49:01 +01:00
|
|
|
#include <SDL_events.h>
|
|
|
|
#include <SDL_messagebox.h>
|
|
|
|
|
2022-04-01 09:36:33 +02:00
|
|
|
#include <shlobj.h>
|
|
|
|
|
2022-12-01 23:42:39 +01:00
|
|
|
#include "../Configuration/Configuration.h"
|
2022-04-01 09:36:33 +02:00
|
|
|
#include "../FontAwesome/IconsFontAwesome5.h"
|
|
|
|
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
2022-11-22 10:34:25 +01:00
|
|
|
#include "../Logger/Logger.h"
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
#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:
|
2022-11-21 20:37:28 +01:00
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error ",
|
|
|
|
_profileManager->lastError().data(), window());
|
2022-04-01 09:36:33 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SaveTool::initialiseConfiguration() {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_INFO("Reading configuration file.");
|
2022-04-01 09:36:33 +02:00
|
|
|
|
2022-12-01 23:42:39 +01:00
|
|
|
setSwapInterval(conf().swapInterval());
|
|
|
|
setMinimalLoopPeriod(0);
|
2022-04-01 09:36:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void SaveTool::initialiseGui() {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_INFO("Initialising Dear ImGui.");
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
ImGui::CreateContext();
|
|
|
|
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
|
2022-11-25 11:46:14 +01:00
|
|
|
const auto size = Vector2{windowSize()}/dpiScaling();
|
|
|
|
|
2022-04-01 09:36:33 +02:00
|
|
|
auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf"_s);
|
|
|
|
ImFontConfig font_config;
|
|
|
|
font_config.FontDataOwnedByAtlas = false;
|
|
|
|
std::strcpy(font_config.Name, "Source Sans Pro");
|
2022-11-21 20:37:28 +01:00
|
|
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(reg_font.data()), int(reg_font.size()),
|
2022-11-25 11:46:14 +01:00
|
|
|
20.0f * Float(framebufferSize().x()) / size.x(), &font_config);
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
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;
|
2022-11-21 20:37:28 +01:00
|
|
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(icon_font.data()), int(icon_font.size()),
|
2022-11-25 11:46:14 +01:00
|
|
|
16.0f * Float(framebufferSize().x()) / size.x(), &icon_config, icon_range);
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
auto brand_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAB);
|
|
|
|
static const ImWchar brand_range[] = { ICON_MIN_FAB, ICON_MAX_FAB, 0 };
|
2022-11-21 20:37:28 +01:00
|
|
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(brand_font.data()), int(brand_font.size()),
|
2022-11-25 11:46:14 +01:00
|
|
|
16.0f * Float(framebufferSize().x()) / size.x(), &icon_config, brand_range);
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf"_s);
|
|
|
|
ImVector<ImWchar> 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);
|
2022-11-21 20:37:28 +01:00
|
|
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(mono_font.data()), int(mono_font.size()),
|
2022-11-25 11:46:14 +01:00
|
|
|
18.0f * Float(framebufferSize().x()) / size.x(), &font_config, range.Data);
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
_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() {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_INFO("Initialising the profile manager.");
|
|
|
|
|
2022-04-01 09:36:33 +02:00
|
|
|
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 {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_INFO("Initialising Save Tool directories.");
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
_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)) {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_WARNING("Backups directory not found, creating...");
|
2022-04-01 09:36:33 +02:00
|
|
|
if(!Utility::Path::make(_backupsDir)) {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_ERROR(_lastError = "Couldn't create the backups directory.");
|
2022-04-01 09:36:33 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!Utility::Path::exists(_stagingDir)) {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_WARNING("Staging directory not found, creating...");
|
2022-04-01 09:36:33 +02:00
|
|
|
if(!Utility::Path::make(_stagingDir)) {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_ERROR(_lastError = "Couldn't create the staging directory.");
|
2022-04-01 09:36:33 +02:00
|
|
|
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 {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_INFO("Searching for the game's save directory.");
|
2022-04-01 09:36:33 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2022-11-22 10:34:25 +01:00
|
|
|
_lastError = Utility::format("SHGetKnownFolderPath() failed with error code {}.", GetLastError());
|
|
|
|
LOG_ERROR(_lastError);
|
2022-04-01 09:36:33 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_gameDataDir = Utility::Path::join(Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder"_s);
|
|
|
|
|
|
|
|
if(!Utility::Path::exists(_gameDataDir)) {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_ERROR(_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s);
|
2022-04-01 09:36:33 +02:00
|
|
|
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() {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_INFO("Initialising the M.A.S.S. manager.");
|
2022-04-01 09:36:33 +02:00
|
|
|
_massManager.emplace(_saveDir, _currentProfile->account(), _currentProfile->isDemo(), _stagingDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SaveTool::initialiseFileWatcher() {
|
2022-11-22 10:34:25 +01:00
|
|
|
LOG_INFO("Initialising the file watcher.");
|
2022-04-01 09:36:33 +02:00
|
|
|
_fileWatcher.emplace();
|
|
|
|
_watchIDs[SaveDir] = _fileWatcher->addWatch(_saveDir, this, false);
|
|
|
|
_watchIDs[StagingDir] = _fileWatcher->addWatch(_stagingDir, this, false);
|
|
|
|
_fileWatcher->watch();
|
|
|
|
}
|