Compare commits

...

4 commits

Author SHA1 Message Date
0b46403ede
Application: add menu entries for the new folders. 2024-03-09 18:36:53 +01:00
efc3fe0dc7
Application: move folder management to Configuration.
Also, add new folders for the upcoming weapon/armour/style
export/import mechanism.
2024-03-09 18:09:27 +01:00
43420d2277
main: improve an error message. 2024-03-09 17:38:52 +01:00
ba3769404d
Configuration: update formatting. 2024-03-08 21:53:12 +01:00
8 changed files with 184 additions and 150 deletions

View file

@ -103,20 +103,6 @@ Application::Application(const Arguments& arguments):
initialiseGui(); initialiseGui();
if(!initialiseToolDirectories()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
_lastError.data(), window());
exit(EXIT_FAILURE);
return;
}
if(!findGameDataDirectory()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
_lastError.data(), window());
exit(EXIT_FAILURE);
return;
}
checkGameState(); checkGameState();
_gameCheckTimerId = SDL_AddTimer(2000, _gameCheckTimerId = SDL_AddTimer(2000,
[](std::uint32_t interval, void* param)->std::uint32_t{ [](std::uint32_t interval, void* param)->std::uint32_t{

View file

@ -102,8 +102,6 @@ class Application: public Platform::Sdl2Application, public efsw::FileWatchListe
void initialiseConfiguration(); void initialiseConfiguration();
void initialiseGui(); void initialiseGui();
void initialiseManager(); void initialiseManager();
bool initialiseToolDirectories();
bool findGameDataDirectory();
void initialiseMassManager(); void initialiseMassManager();
void initialiseFileWatcher(); void initialiseFileWatcher();
@ -230,18 +228,6 @@ class Application: public Platform::Sdl2Application, public efsw::FileWatchListe
Containers::String _lastError; Containers::String _lastError;
Containers::String _gameDataDir;
Containers::String _configDir;
Containers::String _saveDir;
Containers::String _screenshotsDir;
Containers::String _backupsDir;
Containers::String _stagingDir;
//Containers::String _armouryDir;
//Containers::String _armoursDir;
//Containers::String _weaponsDir;
//Containers::String _stylesDir;
enum class GameState : std::uint8_t { enum class GameState : std::uint8_t {
Unknown, NotRunning, Running Unknown, NotRunning, Running
} _gameState{GameState::Unknown}; } _gameState{GameState::Unknown};

View file

@ -14,15 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Utility/Path.h>
#include <Corrade/Utility/Unicode.h>
#include <SDL_events.h> #include <SDL_events.h>
#include <SDL_messagebox.h> #include <SDL_messagebox.h>
#include <shlobj.h>
#include "../Configuration/Configuration.h" #include "../Configuration/Configuration.h"
#include "../FontAwesome/IconsFontAwesome5.h" #include "../FontAwesome/IconsFontAwesome5.h"
#include "../FontAwesome/IconsFontAwesome5Brands.h" #include "../FontAwesome/IconsFontAwesome5Brands.h"
@ -115,7 +109,7 @@ Application::initialiseManager() {
SDL_zero(event); SDL_zero(event);
event.type = _initEventId; event.type = _initEventId;
_profileManager.emplace(_saveDir, _backupsDir); _profileManager.emplace(conf().directories().gameSaves, conf().directories().backups);
if(!_profileManager->ready()) { if(!_profileManager->ready()) {
event.user.code = ProfileManagerFailure; event.user.code = ProfileManagerFailure;
SDL_PushEvent(&event); SDL_PushEvent(&event);
@ -126,115 +120,19 @@ Application::initialiseManager() {
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
auto
Application::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
Application::findGameDataDirectory() -> bool {
LOG_INFO("Searching for the game's save directory.");
wchar_t* localappdata_path = nullptr;
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
auto result = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &localappdata_path);
if(result != S_OK)
{
char* message_buffer = nullptr;
auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, result, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
reinterpret_cast<char*>(&message_buffer), 0, nullptr);
String message{message_buffer, size};
LocalFree(message_buffer);
_lastError = Utility::format("SHGetKnownFolderPath() failed with error code {}: {}", result, message);
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 void
Application::initialiseMassManager() { Application::initialiseMassManager() {
LOG_INFO("Initialising the M.A.S.S. manager."); LOG_INFO("Initialising the M.A.S.S. manager.");
_massManager.emplace(_saveDir, _currentProfile->account(), _currentProfile->isDemo(), _stagingDir); _massManager.emplace(conf().directories().gameSaves, _currentProfile->account(), _currentProfile->isDemo(),
conf().directories().staging);
} }
void void
Application::initialiseFileWatcher() { Application::initialiseFileWatcher() {
LOG_INFO("Initialising the file watcher."); LOG_INFO("Initialising the file watcher.");
_fileWatcher.emplace(); _fileWatcher.emplace();
_watchIDs[SaveDir] = _fileWatcher->addWatch(_saveDir, this, false); _watchIDs[SaveDir] = _fileWatcher->addWatch(conf().directories().gameSaves, this, false);
_watchIDs[StagingDir] = _fileWatcher->addWatch(_stagingDir, this, false); _watchIDs[StagingDir] = _fileWatcher->addWatch(conf().directories().staging, this, false);
_fileWatcher->watch(); _fileWatcher->watch();
} }

View file

@ -538,7 +538,7 @@ Application::drawMassManager() {
ImGui::TextUnformatted("Staging area"); ImGui::TextUnformatted("Staging area");
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::SmallButton(ICON_FA_FOLDER_OPEN " Open staging folder")) { if(ImGui::SmallButton(ICON_FA_FOLDER_OPEN " Open staging folder")) {
openUri(Utility::Path::toNativeSeparators(_stagingDir)); openUri(Utility::Path::toNativeSeparators(conf().directories().staging));
} }
for(const auto& pair : _massManager->stagedMasses()) { for(const auto& pair : _massManager->stagedMasses()) {

View file

@ -31,29 +31,61 @@ Application::drawMainMenu() {
} }
if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) { if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) {
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open game data directory", Utility::Path::exists(_gameDataDir))) { if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open game data directory")) {
if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, Utility::Path::exists(_configDir))) { if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false,
openUri(Utility::Path::toNativeSeparators(_configDir)); Utility::Path::exists(conf().directories().gameConfig)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().gameConfig));
} }
if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, Utility::Path::exists(_saveDir))) { if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false,
openUri(Utility::Path::toNativeSeparators(_saveDir)); Utility::Path::exists(conf().directories().gameSaves)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().gameSaves));
} }
if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, Utility::Path::exists(_screenshotsDir))) { if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false,
openUri(Utility::Path::toNativeSeparators(_screenshotsDir)); Utility::Path::exists(conf().directories().gameScreenshots)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().gameScreenshots));
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
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::Path::exists(_backupsDir))) { if(ImGui::MenuItem(ICON_FA_FILE_ARCHIVE " Profile backups", nullptr, false,
openUri(Utility::Path::toNativeSeparators(_backupsDir)); Utility::Path::exists(conf().directories().backups)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().backups));
} }
if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false, Utility::Path::exists(_stagingDir))) { if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false,
openUri(Utility::Path::toNativeSeparators(_stagingDir)); Utility::Path::exists(conf().directories().staging)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().staging));
}
if(ImGui::BeginMenu(ICON_FA_BOXES " Armoury")) {
if(ImGui::MenuItem(ICON_FA_SHIELD_ALT " Armour parts", nullptr, false,
Utility::Path::exists(conf().directories().armours)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().armours));
}
if(ImGui::MenuItem(ICON_FA_ROCKET " Weapons", nullptr, false,
Utility::Path::exists(conf().directories().weapons)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().weapons));
}
if(ImGui::MenuItem(ICON_FA_PALETTE " Custom styles", nullptr, false,
Utility::Path::exists(conf().directories().styles)))
{
openUri(Utility::Path::toNativeSeparators(conf().directories().styles));
}
ImGui::EndMenu();
} }
ImGui::EndMenu(); ImGui::EndMenu();

View file

@ -16,7 +16,15 @@
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h> #include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Utility/Path.h> #include <Corrade/Utility/Path.h>
#include <Corrade/Utility/Unicode.h>
#include <combaseapi.h>
#include <knownfolders.h>
#include <shlobj.h>
#include "../Logger/Logger.h"
#include "Configuration.h" #include "Configuration.h"
@ -75,12 +83,100 @@ Configuration::Configuration() {
else { else {
_conf.setValue("skip_disclaimer", _skipDisclaimer); _conf.setValue("skip_disclaimer", _skipDisclaimer);
} }
LOG_INFO("Searching for the game's save directory.");
wchar_t* localappdata_path = nullptr;
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
auto result = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &localappdata_path);
if(result != S_OK)
{
char* message_buffer = nullptr;
auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, result, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
reinterpret_cast<char*>(&message_buffer), 0, nullptr);
String message{message_buffer, size};
LocalFree(message_buffer);
_lastError = Utility::format("SHGetKnownFolderPath() failed with error code {}: {}", result, message);
LOG_ERROR(_lastError);
return;
}
auto game_data_dir = Utility::Path::join(
Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)),
"MASS_Builder/Saved"_s
);
if(!Utility::Path::exists(game_data_dir)) {
LOG_ERROR(_lastError = game_data_dir + " wasn't found. Make sure to play the game at least once."_s);
return;
}
_directories.gameConfig = Utility::Path::join(game_data_dir, "Config/WindowsNoEditor"_s);
_directories.gameSaves = Utility::Path::join(game_data_dir, "SaveGames"_s);
_directories.gameScreenshots = Utility::Path::join(game_data_dir, "Screenshots/WindowsNoEditor"_s);
LOG_INFO("Initialising Save Tool directories.");
Containers::String executable_location = Utility::Path::split(*Utility::Path::executableLocation()).first();
_directories.backups = Utility::Path::join(executable_location, "backups");
_directories.staging = Utility::Path::join(executable_location, "staging");
auto armoury_dir = Utility::Path::join(executable_location, "armoury");
_directories.armours = Utility::Path::join(armoury_dir, "armours");
_directories.weapons = Utility::Path::join(armoury_dir, "weapons");
_directories.styles = Utility::Path::join(armoury_dir, "styles");
if(!Utility::Path::exists(_directories.backups)) {
LOG_WARNING("Backups directory not found, creating...");
if(!Utility::Path::make(_directories.backups)) {
LOG_ERROR(_lastError = "Couldn't create the backups directory.");
return;
}
}
if(!Utility::Path::exists(_directories.staging)) {
LOG_WARNING("Staging directory not found, creating...");
if(!Utility::Path::make(_directories.staging)) {
LOG_ERROR(_lastError = "Couldn't create the staging directory.");
return;
}
}
if(!Utility::Path::exists(_directories.armours)) {
LOG_WARNING("Armours directory not found, creating...");
if(!Utility::Path::make(_directories.armours)) {
LOG_ERROR(_lastError = "Couldn't create the armours directory.");
return;
}
}
if(!Utility::Path::exists(_directories.weapons)) {
LOG_WARNING("Weapons directory not found, creating...");
if(!Utility::Path::make(_directories.weapons)) {
LOG_ERROR(_lastError = "Couldn't create the weapons directory.");
return;
}
}
if(!Utility::Path::exists(_directories.styles)) {
LOG_WARNING("Styles directory not found, creating...");
if(!Utility::Path::make(_directories.styles)) {
LOG_ERROR(_lastError = "Couldn't create the styles directory.");
return;
}
}
_valid = true;
} }
Configuration::~Configuration() { Configuration::~Configuration() {
save(); save();
} }
bool
Configuration::valid() const {
return _valid;
}
Containers::StringView
Configuration::lastError() const {
return _lastError;
}
void void
Configuration::save() { Configuration::save() {
_conf.save(); _conf.save();
@ -158,14 +254,21 @@ Configuration::setSkipDisclaimer(bool mode) {
_conf.save(); _conf.save();
} }
bool Configuration::isRunningInWine() const { bool
Configuration::isRunningInWine() const {
return _isRunningInWine; return _isRunningInWine;
} }
void Configuration::setRunningInWine(bool wine) { void
Configuration::setRunningInWine(bool wine) {
_isRunningInWine = wine; _isRunningInWine = wine;
} }
Configuration::Directories const&
Configuration::directories() const {
return _directories;
}
Configuration& Configuration&
Configuration::instance() { Configuration::instance() {
static Configuration conf{}; static Configuration conf{};

View file

@ -28,6 +28,10 @@ class Configuration {
~Configuration(); ~Configuration();
bool valid() const;
auto lastError() const -> Containers::StringView;
void save(); void save();
auto swapInterval() const -> int; auto swapInterval() const -> int;
@ -51,11 +55,25 @@ class Configuration {
bool isRunningInWine() const; bool isRunningInWine() const;
void setRunningInWine(bool wine); void setRunningInWine(bool wine);
struct Directories {
Containers::String gameSaves;
Containers::String gameConfig;
Containers::String gameScreenshots;
Containers::String backups;
Containers::String staging;
Containers::String armours;
Containers::String weapons;
Containers::String styles;
};
auto directories() const -> Directories const&;
private: private:
explicit Configuration(); explicit Configuration();
Utility::Configuration _conf; Utility::Configuration _conf;
Containers::String _lastError;
int _swapInterval = 1; int _swapInterval = 1;
float _fpsCap = 60.0f; float _fpsCap = 60.0f;
bool _cheatMode = false; bool _cheatMode = false;
@ -64,6 +82,10 @@ class Configuration {
bool _skipDisclaimer = false; bool _skipDisclaimer = false;
bool _isRunningInWine = false; bool _isRunningInWine = false;
bool _valid = false;
Directories _directories;
}; };
Configuration& conf(); Configuration& conf();

View file

@ -41,6 +41,13 @@ int main(int argc, char** argv) {
LOG_INFO("Initialising M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION_STRING "."); LOG_INFO("Initialising M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION_STRING ".");
if(!mbst::conf().valid()) {
LOG_ERROR_FORMAT("There was an error initialising the app: {}", mbst::conf().lastError());
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
mbst::conf().lastError().cbegin(), nullptr);
return EXIT_FAILURE;
}
auto str = setlocale(LC_ALL, ".utf-8"); auto str = setlocale(LC_ALL, ".utf-8");
if(str) { if(str) {
Containers::StringView locale{str}; Containers::StringView locale{str};
@ -48,7 +55,7 @@ int main(int argc, char** argv) {
if(!locale.hasSuffix(".utf8") && !locale.hasSuffix(".65001")) { if(!locale.hasSuffix(".utf8") && !locale.hasSuffix(".65001")) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
"Your system doesn't support UTF-8.", nullptr); "Your system doesn't support the UTF-8 codepage.", nullptr);
return EXIT_FAILURE; return EXIT_FAILURE;
} }