Compare commits

..

1 commit

Author SHA1 Message Date
9349b840a4 ResearchTree: add basic functionality and some nodes. 2021-07-21 21:01:02 +02:00
15 changed files with 57 additions and 582 deletions

8
.gitmodules vendored
View file

@ -26,11 +26,3 @@
path = third-party/efsw path = third-party/efsw
url = https://github.com/SpartanJ/efsw url = https://github.com/SpartanJ/efsw
branch = master branch = master
[submodule "third-party/cpr"]
path = third-party/cpr
url = https://github.com/whoshuu/cpr
branch = master
[submodule "json.hpp"]
path = third-party/json
url = https://github.com/nlohmann/json
branch = master

View file

@ -88,11 +88,4 @@ set(BUILD_TEST_APP OFF CACHE BOOL "" FORCE)
set(EFSW_INSTALL OFF CACHE BOOL "" FORCE) set(EFSW_INSTALL OFF CACHE BOOL "" FORCE)
add_subdirectory(third-party/efsw EXCLUDE_FROM_ALL) add_subdirectory(third-party/efsw EXCLUDE_FROM_ALL)
set(CPR_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(CMAKE_USE_LIBSSH2 OFF CACHE BOOL "" FORCE) # For some reason, even when HTTP_ONLY is set to ON, libcurl will try to link to libssh2.
add_subdirectory(third-party/cpr EXCLUDE_FROM_ALL)
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
add_subdirectory(third-party/json)
add_subdirectory(src) add_subdirectory(src)

View file

@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(SAVETOOL_PROJECT_VERSION 1.1.0) set(SAVETOOL_PROJECT_VERSION 1.0.0)
find_package(Corrade REQUIRED Main Containers Utility Interconnect) find_package(Corrade REQUIRED Main Containers Utility Interconnect)
find_package(Magnum REQUIRED GL Sdl2Application) find_package(Magnum REQUIRED GL Sdl2Application)
@ -46,8 +46,6 @@ add_executable(MassBuilderSaveTool WIN32
Mass/Mass.cpp Mass/Mass.cpp
Maps/LastMissionId.h Maps/LastMissionId.h
Maps/StoryProgress.h Maps/StoryProgress.h
ToastQueue/ToastQueue.h
ToastQueue/ToastQueue.cpp
ResearchTree/NodeIDs.h ResearchTree/NodeIDs.h
ResearchTree/ResearchTree.h ResearchTree/ResearchTree.h
ResearchTree/ResearchTree.cpp ResearchTree/ResearchTree.cpp
@ -60,7 +58,7 @@ if(CMAKE_BUILD_TYPE STREQUAL Debug)
add_compile_definitions(SAVETOOL_DEBUG_BUILD) add_compile_definitions(SAVETOOL_DEBUG_BUILD)
endif() endif()
add_compile_definitions(SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}" add_compile_definitions(SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}"
SAVETOOL_CODENAME="Beautiful Reina" SAVETOOL_CODENAME="Agonising Quark"
SUPPORTED_GAME_VERSION="0.7.6") SUPPORTED_GAME_VERSION="0.7.6")
if(CMAKE_BUILD_TYPE STREQUAL Release) if(CMAKE_BUILD_TYPE STREQUAL Release)
@ -80,7 +78,5 @@ target_link_libraries(MassBuilderSaveTool PRIVATE
MagnumIntegration::ImGui MagnumIntegration::ImGui
efsw efsw
zip zip
cpr::cpr
nlohmann_json::nlohmann_json
imm32 imm32
wtsapi32) wtsapi32)

View file

@ -30,10 +30,6 @@
#include <Magnum/ImGuiIntegration/Integration.h> #include <Magnum/ImGuiIntegration/Integration.h>
#include <Magnum/ImGuiIntegration/Context.hpp> #include <Magnum/ImGuiIntegration/Context.hpp>
#include <cpr/cpr.h>
#include <nlohmann/json.hpp>
#include <windef.h> #include <windef.h>
#include <winuser.h> #include <winuser.h>
#include <processthreadsapi.h> #include <processthreadsapi.h>
@ -85,15 +81,12 @@ SaveTool::SaveTool(const Arguments& arguments):
initialiseGui(); initialiseGui();
if((_initEventId = SDL_RegisterEvents(2)) == UnsignedInt(-1)) { if((_initEventId = SDL_RegisterEvents(1)) == UnsignedInt(-1)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
"SDL_RegisterEvents failed in SaveTool::SaveTool(). Exiting...", window()); "SDL_RegisterEvents failed in SaveTool::SaveTool(). Exiting...", window());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
return;
} }
_updateEventId = _initEventId + 1;
_backupsDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "backups"); _backupsDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "backups");
_stagingDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging"); _stagingDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging");
@ -108,7 +101,6 @@ SaveTool::SaveTool(const Arguments& arguments):
if(!findGameDataDirectory()) { if(!findGameDataDirectory()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", _lastError.c_str(), window()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", _lastError.c_str(), window());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
return;
} }
_configDir = Utility::Directory::join(_gameDataDir, "Saved/Config/WindowsNoEditor"); _configDir = Utility::Directory::join(_gameDataDir, "Saved/Config/WindowsNoEditor");
@ -118,7 +110,6 @@ SaveTool::SaveTool(const Arguments& arguments):
if(SDL_InitSubSystem(SDL_INIT_TIMER) != 0) { if(SDL_InitSubSystem(SDL_INIT_TIMER) != 0) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", SDL_GetError(), window()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", SDL_GetError(), window());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
return;
} }
checkGameState(); checkGameState();
@ -131,24 +122,11 @@ SaveTool::SaveTool(const Arguments& arguments):
if(_gameCheckTimerId == 0) { if(_gameCheckTimerId == 0) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
return;
}
initialiseConfiguration();
if(_checkUpdatesOnStartup) {
_thread = std::thread{[this]{ checkForUpdates(); }};
_queue.addToast(Toast::Type::Default, "Checking for updates...");
} }
} }
SaveTool::~SaveTool() { SaveTool::~SaveTool() {
SDL_RemoveTimer(_gameCheckTimerId); SDL_RemoveTimer(_gameCheckTimerId);
_conf.setValue("cheat_mode", _cheatMode);
_conf.setValue("unsafe_mode", _unsafeMode);
_conf.setValue("startup_update_check", _checkUpdatesOnStartup);
_conf.save();
} }
void SaveTool::handleFileAction(efsw::WatchID watch_id, void SaveTool::handleFileAction(efsw::WatchID watch_id,
@ -272,9 +250,6 @@ void SaveTool::anyEvent(SDL_Event& event) {
if(event.type == _initEventId) { if(event.type == _initEventId) {
initEvent(event); initEvent(event);
} }
else if(event.type == _updateEventId) {
updateCheckEvent(event);
}
} }
void SaveTool::initEvent(SDL_Event& event) { void SaveTool::initEvent(SDL_Event& event) {
@ -294,97 +269,6 @@ void SaveTool::initEvent(SDL_Event& event) {
} }
} }
void SaveTool::updateCheckEvent(SDL_Event& event) {
_thread.join();
cpr::Response r{std::move(*static_cast<cpr::Response*>(event.user.data1))};
delete static_cast<cpr::Response*>(event.user.data1);
if(r.elapsed > 10.0) {
_queue.addToast(Toast::Type::Error, "The request timed out.");
return;
}
if(r.status_code != 200) {
_queue.addToast(Toast::Type::Error, Utility::formatString("The request failed with error code {}: {}", r.status_code, r.reason));
return;
}
using json = nlohmann::json;
json response = json::parse(r.text);
struct Version {
explicit Version(const std::string& str) {
std::size_t start_point = 0;
if(str[0] == 'v') {
start_point++;
}
major = std::atoi(str.c_str() + start_point);
start_point = str.find('.', start_point) + 1;
minor = std::atoi(str.c_str() + start_point);
start_point = str.find('.', start_point) + 1;
patch = std::atoi(str.c_str() + start_point);
}
Int major;
Int minor;
Int patch;
bool operator==(const Version& other) const {
return (major == other.major) && (minor == other.minor) && (patch == other.patch);
}
bool operator>(const Version& other) const {
return ( major * 10000 + minor * 100 + patch) >
(other.major * 10000 + other.minor * 100 + other.patch);
}
};
static const Version current_ver{SAVETOOL_VERSION};
Version latest_ver{response[0]["tag_name"]};
if(latest_ver == current_ver) {
_queue.addToast(Toast::Type::Success, "The application is already up to date.");
}
else if(latest_ver > current_ver) {
_queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information.",
std::chrono::milliseconds{5000});
_updateAvailable = true;
_latestVersion = Utility::formatString("{}.{}.{}", latest_ver.major, latest_ver.minor, latest_ver.patch);
_releaseLink = response[0]["html_url"];
_downloadLink = response[0]["assets"][0]["browser_download_url"];
}
else if(current_ver > latest_ver) {
_queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???");
}
}
void SaveTool::initialiseConfiguration() {
if(_conf.hasValue("cheat_mode")) {
_cheatMode = _conf.value<bool>("cheat_mode");
}
else {
_conf.setValue("cheat_mode", _cheatMode);
}
if(_conf.hasValue("unsafe_mode")) {
_unsafeMode = _conf.value<bool>("unsafe_mode");
}
else {
_conf.setValue("unsafe_mode", _unsafeMode);
}
if(_conf.hasValue("startup_update_check")) {
_checkUpdatesOnStartup = _conf.value<bool>("startup_update_check");
}
else {
_conf.setValue("startup_update_check", _checkUpdatesOnStartup);
}
_conf.save();
}
void SaveTool::initialiseGui() { void SaveTool::initialiseGui() {
ImGui::CreateContext(); ImGui::CreateContext();
@ -534,15 +418,13 @@ void SaveTool::drawGui() {
ImGui::ShowMetricsWindow(&_metricsWindow); ImGui::ShowMetricsWindow(&_metricsWindow);
} }
#endif #endif
_queue.draw(windowSize());
} }
void SaveTool::drawDisclaimer() { void SaveTool::drawDisclaimer() {
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot); ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
if(ImGui::Begin("Disclaimer##DisclaimerWindow", nullptr, if(ImGui::Begin("Disclaimer##DisclaimerWindow", nullptr,
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoBringToFrontOnFocus| ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|
ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_MenuBar)) ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_MenuBar))
{ {
if(ImGui::BeginMenuBar()) { if(ImGui::BeginMenuBar()) {
@ -670,15 +552,3 @@ void SaveTool::checkGameState() {
_gameState = GameState::Unknown; _gameState = GameState::Unknown;
} }
} }
void SaveTool::checkForUpdates() {
cpr::Response r = cpr::Get(cpr::Url{"https://williamjcm.ovh/git/api/v1/repos/williamjcm/MassBuilderSaveTool/releases"},
cpr::Parameters{{"limit", "1"}}, cpr::Timeout{10000});
SDL_Event event;
SDL_zero(event);
event.type = _updateEventId;
event.user.code = r.status_code;
event.user.data1 = new cpr::Response{std::move(r)};
SDL_PushEvent(&event);
}

View file

@ -19,7 +19,6 @@
#include <thread> #include <thread>
#include <Corrade/Containers/Pointer.h> #include <Corrade/Containers/Pointer.h>
#include <Corrade/Utility/Configuration.h>
#include <Corrade/Utility/Resource.h> #include <Corrade/Utility/Resource.h>
#include <Magnum/Platform/Sdl2Application.h> #include <Magnum/Platform/Sdl2Application.h>
@ -34,7 +33,6 @@
#include "../ProfileManager/ProfileManager.h" #include "../ProfileManager/ProfileManager.h"
#include "../MassManager/MassManager.h" #include "../MassManager/MassManager.h"
#include "../ToastQueue/ToastQueue.h"
#include "../ResearchTree/ResearchTree.h" #include "../ResearchTree/ResearchTree.h"
class Node; class Node;
@ -75,10 +73,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
ProfileManagerFailure ProfileManagerFailure
}; };
void initEvent(SDL_Event& event); void initEvent(SDL_Event& event);
void updateCheckEvent(SDL_Event& event);
// Initialisation methods // Initialisation methods
void initialiseConfiguration();
void initialiseGui(); void initialiseGui();
void initialiseManager(); void initialiseManager();
auto findGameDataDirectory() -> bool; auto findGameDataDirectory() -> bool;
@ -145,9 +141,6 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
void checkGameState(); void checkGameState();
void checkForUpdates();
Utility::Configuration _conf{"MassBuilderSaveTool.ini"};
Utility::Resource _rs{"assets"}; Utility::Resource _rs{"assets"};
// GUI-related members // GUI-related members
@ -167,12 +160,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
bool _metricsWindow{false}; bool _metricsWindow{false};
#endif #endif
ToastQueue _queue; std::thread _thread;
std::thread _thread; // used for threaded operations such as profile manager init or update checking.
UnsignedInt _initEventId; UnsignedInt _initEventId;
UnsignedInt _updateEventId;
std::string _lastError; std::string _lastError;
@ -204,13 +193,5 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
Containers::Pointer<ResearchTree> _tree; Containers::Pointer<ResearchTree> _tree;
bool _checkUpdatesOnStartup{true};
bool _unsafeMode{false}; bool _unsafeMode{false};
bool _updateAvailable{false};
std::string _latestVersion;
std::string _releaseLink;
std::string _downloadLink;
bool _cheatMode{false};
}; };

View file

@ -42,6 +42,12 @@ void SaveTool::drawManager() {
return; return;
} }
if(ImGui::BeginTable("##TopRow", 2)) {
ImGui::TableSetupColumn("##ProfileInfo", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Unsafe", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
ImGui::Text("Current profile: %s (%s)", ImGui::Text("Current profile: %s (%s)",
_currentProfile->companyName().c_str(), _currentProfile->companyName().c_str(),
@ -54,6 +60,14 @@ void SaveTool::drawManager() {
_uiState = UiState::ProfileManager; _uiState = UiState::ProfileManager;
} }
ImGui::TableSetColumnIndex(1);
ImGui::Checkbox("Unsafe mode", &_unsafeMode);
drawTooltip("Enabling this allows interactions deemed to be unsafe to do while the game is running.",
Float(windowSize().x()) * 0.35f);
ImGui::EndTable();
}
if(ImGui::BeginChild("##ProfileInfo", if(ImGui::BeginChild("##ProfileInfo",
{ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f}, {ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f},
true, ImGuiWindowFlags_MenuBar)) true, ImGuiWindowFlags_MenuBar))
@ -236,10 +250,6 @@ void SaveTool::drawGeneralInfo() {
} }
} }
if(!_cheatMode) {
return;
}
ImGui::SameLine(); ImGui::SameLine();
static Int credits; static Int credits;
@ -310,8 +320,7 @@ void SaveTool::drawResearchInventory() {
ImGui::TextUnformatted((name)); \ ImGui::TextUnformatted((name)); \
ImGui::TableSetColumnIndex(1); \ ImGui::TableSetColumnIndex(1); \
if(_currentProfile->getter() != -1) { \ if(_currentProfile->getter() != -1) { \
ImGui::Text("%i", _currentProfile->getter()); \ drawUnsafeText("%i", _currentProfile->getter()); \
if(_cheatMode) { \
ImGui::TableSetColumnIndex(2); \ ImGui::TableSetColumnIndex(2); \
ImGui::PushID(#setter); \ ImGui::PushID(#setter); \
static Int var = _currentProfile->getter(); \ static Int var = _currentProfile->getter(); \
@ -327,7 +336,6 @@ void SaveTool::drawResearchInventory() {
} \ } \
ImGui::PopID(); \ ImGui::PopID(); \
} \ } \
} \
else { \ else { \
ImGui::TextDisabled("Not found in the save file"); \ ImGui::TextDisabled("Not found in the save file"); \
} }

View file

@ -27,7 +27,7 @@ void SaveTool::drawProfileManager() {
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot); ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
if(ImGui::Begin("Profile management##ProfileManager", nullptr, if(ImGui::Begin("Profile management##ProfileManager", nullptr,
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoBringToFrontOnFocus| ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_AlwaysAutoResize|
ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_MenuBar)) ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_MenuBar))
{ {
if(ImGui::BeginMenuBar()) { if(ImGui::BeginMenuBar()) {

View file

@ -251,58 +251,6 @@ void SaveTool::drawAbout() {
ImGui::TreePop(); ImGui::TreePop();
} }
if(ImGui::TreeNodeEx("C++ Requests (cpr)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* cpr_website = "https://whoshuu.github.io/cpr/";
ImGui::Text(ICON_FA_GLOBE " %s", cpr_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(cpr_website);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(cpr_website);
}
ImGui::TextUnformatted("Licence: MIT");
static const auto cpr_licence = _rs.get("LICENSE.cpr");
if(ImGui::BeginChild("##cprLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(cpr_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("JSON for Modern C++ (aka json.hpp)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* json_website = "https://json.nlohmann.me/";
ImGui::Text(ICON_FA_GLOBE " %s", json_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(json_website);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(json_website);
}
ImGui::TextUnformatted("Licence: MIT");
static const auto json_licence = _rs.get("LICENSE.json");
if(ImGui::BeginChild("##jsonLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(json_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("Font Awesome", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("Font Awesome", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::TextUnformatted("Version used: 5.15.3"); ImGui::TextUnformatted("Version used: 5.15.3");
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();

View file

@ -41,11 +41,11 @@ void SaveTool::drawMainMenu() {
} }
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open manager directory")) { if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open manager directory")) {
if(ImGui::MenuItem(ICON_FA_FILE_ARCHIVE " Profile backups", nullptr, false, Utility::Directory::exists(_backupsDir))) { if(ImGui::MenuItem(ICON_FA_FILE_ARCHIVE " Profile backups", nullptr, false, _profileManager != nullptr)) {
openUri(Utility::Directory::toNativeSeparators(_backupsDir)); openUri(Utility::Directory::toNativeSeparators(_backupsDir));
} }
if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false, Utility::Directory::exists(_stagingDir))) { if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false, _massManager != nullptr)) {
openUri(Utility::Directory::toNativeSeparators(_stagingDir)); openUri(Utility::Directory::toNativeSeparators(_stagingDir));
} }
@ -54,43 +54,6 @@ void SaveTool::drawMainMenu() {
ImGui::Separator(); ImGui::Separator();
if(ImGui::BeginMenu(ICON_FA_COG " Settings")) {
ImGui::Checkbox("Cheat mode", &_cheatMode);
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
drawHelpMarker("This gives access to save edition features that can be considered cheats.",
Float(windowSize().x()) * 0.4f);
ImGui::Checkbox("Unsafe mode", &_unsafeMode);
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
drawHelpMarker("This allows changing the state of save files in the game's save folder even when the game is running.",
Float(windowSize().x()) * 0.4f);
ImGui::Checkbox("Check for updates on startup", &_checkUpdatesOnStartup);
ImGui::SameLine();
if(ImGui::Button(ICON_FA_SYNC_ALT " Check now")) {
_queue.addToast(Toast::Type::Default, "Checking for updates...");
_thread = std::thread{[this]{ checkForUpdates(); }};
}
if(_updateAvailable) {
ImGui::AlignTextToFramePadding();
ImGui::Text("Version %s is available.", _latestVersion.c_str());
if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) {
openUri(_releaseLink);
}
ImGui::SameLine();
if(ImGui::Button(ICON_FA_DOWNLOAD " Download now")) {
openUri(_downloadLink);
}
}
ImGui::EndMenu();
}
ImGui::Separator();
if(ImGui::MenuItem(ICON_FA_SIGN_OUT_ALT " Quit##QuitMenuItem")) { if(ImGui::MenuItem(ICON_FA_SIGN_OUT_ALT " Quit##QuitMenuItem")) {
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

View file

@ -1,162 +0,0 @@
// MassBuilderSaveTool
// Copyright (C) 2021 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/Utility/FormatStl.h>
#include <Magnum/Math/Functions.h>
#include <imgui.h>
#include "../FontAwesome/IconsFontAwesome5.h"
#include "ToastQueue.h"
using namespace Corrade;
constexpr UnsignedInt success_colour = 0xff67d23bu;
constexpr UnsignedInt info_colour = 0xffcc832fu;
constexpr UnsignedInt warning_colour = 0xff2fcfc7u;
constexpr UnsignedInt error_colour = 0xff3134cdu;
constexpr UnsignedInt fade_time = 150;
constexpr Float base_opacity = 1.0f;
constexpr Vector2 padding{20.0f, 20.0f};
constexpr Float toast_spacing = 10.0f;
Toast::Toast(Type type, const std::string& message, std::chrono::milliseconds timeout):
_type{type}, _message{message}, _timeout{timeout}, _creationTime{std::chrono::steady_clock::now()}
{
_phaseTrack = Animation::Track<UnsignedInt, Phase>{{
{0, Phase::FadeIn},
{fade_time, Phase::Wait},
{fade_time + timeout.count(), Phase::FadeOut},
{(fade_time * 2) + timeout.count(), Phase::TimedOut}
}, Math::select, Animation::Extrapolation::Constant};
}
auto Toast::type() -> Type {
return _type;
}
auto Toast::message() -> const std::string& {
return _message;
}
auto Toast::timeout() -> std::chrono::milliseconds {
return _timeout;
}
auto Toast::creationTime() -> std::chrono::steady_clock::time_point {
return _creationTime;
}
auto Toast::elapsedTime() -> std::chrono::milliseconds {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - _creationTime);
}
auto Toast::phase() -> Phase {
return _phaseTrack.at(elapsedTime().count());
}
auto Toast::opacity() -> Float {
Phase phase = this->phase();
Long elapsed_time = elapsedTime().count();
if(phase == Phase::FadeIn) {
return Float(elapsed_time) / Float(fade_time);
}
else if(phase == Phase::FadeOut) {
return 1.0f - ((Float(elapsed_time) - Float(fade_time) - Float(_timeout.count())) / Float(fade_time));
}
return 1.0f;
}
void ToastQueue::addToast(Toast&& toast) {
_toasts.push_back(std::move(toast));
}
void ToastQueue::addToast(Toast::Type type, const std::string& message, std::chrono::milliseconds timeout) {
_toasts.emplace_back(type, message, timeout);
}
void ToastQueue::draw(Vector2i viewport_size) {
Float height = 0.0f;
for(UnsignedInt i = 0; i < _toasts.size(); i++) {
Toast* current = &_toasts[i];
if(current->phase() == Toast::Phase::TimedOut) {
removeToast(i);
continue;
}
std::string win_id = Utility::formatString("##Toast{}", i);
Float opacity = base_opacity * current->opacity();
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, opacity);
ImGui::SetNextWindowPos({viewport_size.x() - padding.x(), viewport_size.y() - padding.y() - height}, ImGuiCond_Always, {1.0f, 1.0f});
if(ImGui::Begin(win_id.c_str(), nullptr,
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoDecoration|
ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoNav|ImGuiWindowFlags_NoFocusOnAppearing))
{
ImColor colour = 0xffffffff;
switch(current->type()) {
case Toast::Type::Default:
break;
case Toast::Type::Success:
colour = success_colour;
ImGui::TextColored(colour, ICON_FA_CHECK_CIRCLE);
break;
case Toast::Type::Info:
colour = info_colour;
ImGui::TextColored(colour, ICON_FA_INFO_CIRCLE);
break;
case Toast::Type::Warning:
colour = warning_colour;
ImGui::TextColored(colour, ICON_FA_EXCLAMATION_TRIANGLE);
break;
case Toast::Type::Error:
colour = error_colour;
ImGui::TextColored(colour, ICON_FA_TIMES_CIRCLE);
break;
}
if(current->type() != Toast::Type::Default) {
ImGui::SameLine();
}
if(current->message().length() > 127) {
ImGui::TextColored(colour, "%.*s...", 127, current->message().c_str());
}
else {
ImGui::TextColored(colour, current->message().c_str());
}
height += ImGui::GetWindowHeight() + toast_spacing;
}
ImGui::End();
ImGui::PopStyleVar();
}
}
void ToastQueue::removeToast(Long index) {
_toasts.erase(_toasts.begin() + index);
}

View file

@ -1,82 +0,0 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 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 <chrono>
#include <string>
#include <vector>
#include <Magnum/Magnum.h>
#include <Magnum/Animation/Track.h>
using namespace Magnum;
class Toast {
public:
enum class Type : UnsignedByte {
Default, Success, Info, Warning, Error
};
enum class Phase : UnsignedByte {
FadeIn, Wait, FadeOut, TimedOut
};
explicit Toast(Type type, const std::string& message,
std::chrono::milliseconds timeout = std::chrono::milliseconds{3000});
Toast(const Toast& other) = delete;
Toast& operator=(const Toast& other) = delete;
Toast(Toast&& other) = default;
Toast& operator=(Toast&& other) = default;
auto type() -> Type;
auto message() -> std::string const&;
auto timeout() -> std::chrono::milliseconds;
auto creationTime() -> std::chrono::steady_clock::time_point;
auto elapsedTime() -> std::chrono::milliseconds;
auto phase() -> Phase;
auto opacity() -> Float;
private:
Type _type{Type::Default};
std::string _message;
std::chrono::milliseconds _timeout;
std::chrono::steady_clock::time_point _creationTime;
Animation::Track<UnsignedInt, Phase> _phaseTrack;
};
class ToastQueue {
public:
void addToast(Toast&& toast);
void addToast(Toast::Type type, const std::string& message,
std::chrono::milliseconds timeout = std::chrono::milliseconds{3000});
void draw(Vector2i viewport_size);
private:
void removeToast(Long index);
std::vector<Toast> _toasts;
};

View file

@ -43,11 +43,3 @@ alias=LICENSE.libzip
[file] [file]
filename=../third-party/efsw/LICENSE filename=../third-party/efsw/LICENSE
alias=LICENSE.efsw alias=LICENSE.efsw
[file]
filename=../third-party/cpr/LICENSE
alias=LICENSE.cpr
[file]
filename=../third-party/json/LICENSE.MIT
alias=LICENSE.json

View file

@ -18,25 +18,7 @@
#include <fstream> #include <fstream>
#include <errhandlingapi.h>
#include <synchapi.h>
#include <winerror.h>
int main(int argc, char** argv) { int main(int argc, char** argv) {
void* mutex_handle = CreateMutexW(nullptr, 0, L"MassBuilderSaveTool");
if(!mutex_handle) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
"There was an error initialising the mutex.",nullptr);
return EXIT_FAILURE;
}
if(GetLastError() == ERROR_ALREADY_EXISTS) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
"There can be only one running instance of the application.",nullptr);
return EXIT_FAILURE;
}
#ifndef SAVETOOL_DEBUG_BUILD #ifndef SAVETOOL_DEBUG_BUILD
std::ofstream output{"SaveToolLog.txt", std::ios::trunc|std::ios::out}; std::ofstream output{"SaveToolLog.txt", std::ios::trunc|std::ios::out};
@ -46,9 +28,5 @@ int main(int argc, char** argv) {
#endif #endif
SaveTool app({argc, argv}); SaveTool app({argc, argv});
Int result = app.exec(); return app.exec();
ReleaseMutex(mutex_handle);
return result;
} }

1
third-party/cpr vendored

@ -1 +0,0 @@
Subproject commit 814bd0926379cfc786dd1d9df9ce266a4bb6cf59

1
third-party/json vendored

@ -1 +0,0 @@
Subproject commit 350ff4f7ced7c4117eae2fb93df02823c8021fcb