Add MassManager and move backend stuff to it.
This is a big commit, but a necessary one, as too many things were intertwined. As a result, the code is now cleaner.
This commit is contained in:
parent
cf3c7305c8
commit
fc9d898c54
6 changed files with 620 additions and 250 deletions
|
@ -39,6 +39,8 @@ add_executable(wxMASSManager WIN32
|
|||
GUI/MainFrame.cpp
|
||||
GUI/EvtMainFrame.h
|
||||
GUI/EvtMainFrame.cpp
|
||||
MassManager/MassManager.h
|
||||
MassManager/MassManager.cpp
|
||||
resource.rc)
|
||||
|
||||
target_link_libraries(wxMASSManager PRIVATE
|
||||
|
|
|
@ -14,43 +14,26 @@
|
|||
// 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 <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/numdlg.h>
|
||||
#include <wx/regex.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <shlobj.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/Optional.h>
|
||||
#include <Corrade/Utility/Directory.h>
|
||||
#include <Corrade/Utility/FormatStl.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
#include <Corrade/Utility/Unicode.h>
|
||||
|
||||
#include "EvtMainFrame.h"
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
constexpr unsigned char mass_name_locator[] = { 'N', 'a', 'm', 'e', '_', '4', '5', '_', 'A', '0', '3', '7', 'C', '5', 'D', '5', '4', 'E', '5', '3', '4', '5', '6', '4', '0', '7', 'B', 'D', 'F', '0', '9', '1', '3', '4', '4', '5', '2', '9', 'B', 'B', '\0', 0x0C, '\0', '\0', '\0', 'S', 't', 'r', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y', '\0' };
|
||||
|
||||
constexpr unsigned char steamid_locator[] = { 'A', 'c', 'c', 'o', 'u', 'n', 't', '\0', 0x0C, '\0', '\0', '\0', 'S', 't', 'r', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y', '\0' };
|
||||
|
||||
constexpr unsigned char active_slot_locator[] = { 'A', 'c', 't', 'i', 'v', 'e', 'F', 'r', 'a', 'm', 'e', 'S', 'l', 'o', 't', '\0', 0x0C, '\0', '\0', '\0', 'I', 'n', 't', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y', '\0' };
|
||||
|
||||
EvtMainFrame::EvtMainFrame(wxWindow* parent): MainFrame(parent) {
|
||||
SetIcon(wxIcon("MAINICON"));
|
||||
|
||||
getSaveDirectory();
|
||||
getLocalSteamId();
|
||||
if(!_manager.ready()) {
|
||||
errorMessage("There was an issue initialising the manager:\n\n" + _manager.lastError());
|
||||
return;
|
||||
}
|
||||
|
||||
initialiseListView();
|
||||
|
||||
isGameRunning();
|
||||
|
@ -62,13 +45,12 @@ EvtMainFrame::EvtMainFrame(wxWindow* parent): MainFrame(parent) {
|
|||
|
||||
warningMessage(wxString::FromUTF8("Before you start using this app, a few things you should know:\n\n"
|
||||
"For this application to work properly, Steam Cloud syncing needs to be disabled for the game.\nTo disable it, right-click the game in your Steam library, click \"Properties\", go to the \"Updates\" tab, and uncheck \"Enable Steam Cloud synchronization for M.A.S.S. Builder\".\n\n"
|
||||
"Please avoid using this application while the game is running. Bad Things™ could happen to your data.\n\n"
|
||||
"DISCLAIMER: The developer of this application cannot be held responsible for data loss or corruption. PLEASE USE AT YOUR OWN RISK!\n\n"
|
||||
"Last but not least, this application is released under the terms of the GNU General Public Licence version 3. Please see the COPYING file for more details."));
|
||||
|
||||
_watcher.Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(EvtMainFrame::fileUpdateEvent), nullptr, this);
|
||||
_watcher.AddTree(wxFileName(Utility::Directory::toNativeSeparators(_saveDirectory), wxPATH_WIN),
|
||||
wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME, wxString::Format("*%s.sav", _localSteamId));
|
||||
_watcher.AddTree(wxFileName(Utility::Directory::toNativeSeparators(_manager.saveDirectory()), wxPATH_WIN),
|
||||
wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME, wxString::Format("*%s.sav", _manager.steamId()));
|
||||
|
||||
_gameCheckTimer.Start(3000);
|
||||
}
|
||||
|
@ -81,11 +63,19 @@ EvtMainFrame::~EvtMainFrame() {
|
|||
_watcher.Disconnect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(EvtMainFrame::fileUpdateEvent), nullptr, this);
|
||||
}
|
||||
|
||||
void EvtMainFrame::importEvent(wxCommandEvent&) {
|
||||
std::string slot_state = _installedListView->GetItemText(_installedListView->GetFirstSelected(), 1).ToStdString();
|
||||
bool EvtMainFrame::ready() {
|
||||
return _manager.ready();
|
||||
}
|
||||
|
||||
if(slot_state != "<Empty>" && slot_state != "<Invalid data>" &&
|
||||
wxMessageBox(wxString::Format("Hangar %.2d is already occupied by the M.A.S.S. named \"%s\". Are you sure you want to import a M.A.S.S. to this hangar ?", _installedListView->GetFirstSelected() + 1, slot_state),
|
||||
void EvtMainFrame::importEvent(wxCommandEvent&) {
|
||||
const static std::string error_prefix = "Importing failed:\n\n";
|
||||
|
||||
long selected_hangar = _installedListView->GetFirstSelected();
|
||||
HangarState hangar_state = _manager.hangarState(selected_hangar);
|
||||
|
||||
if(hangar_state == HangarState::Filled &&
|
||||
wxMessageBox(wxString::Format("Hangar %.2d is already occupied by the M.A.S.S. named \"%s\". Are you sure you want to import a M.A.S.S. to this hangar ?",
|
||||
selected_hangar + 1, *(_manager.massName(selected_hangar))),
|
||||
"Question", wxYES_NO|wxCENTRE|wxICON_QUESTION, this) == wxNO) {
|
||||
return;
|
||||
}
|
||||
|
@ -98,132 +88,107 @@ void EvtMainFrame::importEvent(wxCommandEvent&) {
|
|||
|
||||
const std::string source_file = dialog.GetPath().ToUTF8().data();
|
||||
|
||||
const std::string mass_name = getMassName(source_file);
|
||||
Containers::Optional<std::string> mass_name = _manager.getMassName(source_file);
|
||||
|
||||
if(mass_name == "") {
|
||||
if(!mass_name) {
|
||||
errorMessage(error_prefix + _manager.lastError());
|
||||
return;
|
||||
}
|
||||
|
||||
if(wxMessageBox(wxString::Format("Are you sure you want to import the M.A.S.S. named \"%s\" to hangar %.2d ?", mass_name, _installedListView->GetFirstSelected() + 1),
|
||||
if(wxMessageBox(wxString::Format("Are you sure you want to import the M.A.S.S. named \"%s\" to hangar %.2d ?", *mass_name, selected_hangar + 1),
|
||||
"Question", wxYES_NO|wxCENTRE|wxICON_QUESTION, this) == wxNO) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_isGameRunning) {
|
||||
errorMessage("The game is running. Aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string dest_file = _saveDirectory + Utility::formatString("/Unit{:.2d}{}.sav", _installedListView->GetFirstSelected(), _localSteamId);
|
||||
|
||||
if(Utility::Directory::exists(dest_file)) {
|
||||
Utility::Directory::rm(dest_file);
|
||||
}
|
||||
|
||||
Utility::Directory::copy(source_file, dest_file);
|
||||
|
||||
{
|
||||
auto mmap = Utility::Directory::map(dest_file);
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &steamid_locator[0], &steamid_locator[23]);
|
||||
|
||||
if(iter == mmap.end()) {
|
||||
errorMessage("Couldn't find the SteamID in the unit file at " + source_file + ". Aborting...");
|
||||
Utility::Directory::rm(dest_file);
|
||||
return;
|
||||
}
|
||||
|
||||
iter += 37;
|
||||
|
||||
for(int i = 0; i < 17; ++i) {
|
||||
*(iter + i) = _localSteamId[i];
|
||||
}
|
||||
switch(_manager.gameState()) {
|
||||
case GameState::Unknown:
|
||||
errorMessage(error_prefix + "For security reasons, importing is disabled if the game's status is unknown.");
|
||||
break;
|
||||
case GameState::NotRunning:
|
||||
if(!_manager.importMass(source_file, selected_hangar)) {
|
||||
errorMessage(error_prefix + _manager.lastError());
|
||||
}
|
||||
break;
|
||||
case GameState::Running:
|
||||
errorMessage(error_prefix + "Importing a M.A.S.S. is disabled while the game is running.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EvtMainFrame::moveEvent(wxCommandEvent&) {
|
||||
const static std::string error_prefix = "Move failed:\n\n";
|
||||
|
||||
long source_slot = _installedListView->GetFirstSelected();
|
||||
|
||||
long choice = wxGetNumberFromUser(wxString::Format("Which hangar do you want to move the M.A.S.S. named \"%s\" to ?\nNotes:\n- If the destination hangar is the same as the source, nothing will happen.\n- If the destination already contains a M.A.S.S., the two will be swapped.\n- If the destination contains invalid data, it will be cleared first.", _installedListView->GetItemText(source_slot, 1)),
|
||||
long choice = wxGetNumberFromUser(wxString::Format("Which hangar do you want to move the M.A.S.S. named \"%s\" to ?\nNotes:\n"
|
||||
"- If the destination hangar is the same as the source, nothing will happen.\n"
|
||||
"- If the destination already contains a M.A.S.S., the two will be swapped.\n"
|
||||
"- If the destination contains invalid data, it will be cleared first.",
|
||||
*(_manager.massName(source_slot))),
|
||||
"Slot", "Choose a slot", source_slot + 1, 1, 32, this) - 1;
|
||||
|
||||
if(choice == -1 || choice == source_slot) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_isGameRunning) {
|
||||
errorMessage("The game is running. Aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string orig_file = Utility::formatString("{}/Unit{:.2d}{}.sav", _saveDirectory, source_slot, _localSteamId);
|
||||
std::string dest_status = _installedListView->GetItemText(choice, 1).ToStdString();
|
||||
std::string dest_file = Utility::formatString("{}/Unit{:.2d}{}.sav", _saveDirectory, choice, _localSteamId);
|
||||
|
||||
if(dest_status == "<Invalid data>") {
|
||||
Utility::Directory::rm(dest_file);
|
||||
}
|
||||
else if(dest_status != "<Empty>") {
|
||||
Utility::Directory::move(dest_file, dest_file + ".tmp");
|
||||
}
|
||||
|
||||
Utility::Directory::move(orig_file, dest_file);
|
||||
|
||||
if(dest_status != "<Empty>") {
|
||||
Utility::Directory::move(dest_file + ".tmp", orig_file);
|
||||
switch(_manager.gameState()) {
|
||||
case GameState::Unknown:
|
||||
errorMessage(error_prefix + "For security reasons, moving a M.A.S.S. is disabled if the game's status is unknown.");
|
||||
break;
|
||||
case GameState::NotRunning:
|
||||
if(!_manager.moveMass(source_slot, choice)) {
|
||||
errorMessage(error_prefix + _manager.lastError());
|
||||
}
|
||||
break;
|
||||
case GameState::Running:
|
||||
errorMessage(error_prefix + "Moving a M.A.S.S. is disabled while the game is running.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EvtMainFrame::deleteEvent(wxCommandEvent&) {
|
||||
const static std::string error_prefix = "Deletion failed:\n\n";
|
||||
|
||||
if(wxMessageBox(wxString::Format("Are you sure you want to delete the data in hangar %.2d ? This operation cannot be undone.", _installedListView->GetFirstSelected() + 1),
|
||||
"Are you sure ?", wxYES_NO|wxCENTRE|wxICON_QUESTION, this) == wxNO) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_isGameRunning) {
|
||||
errorMessage("The game is running. Aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string file = Utility::formatString("{}/Unit{:.2d}{}.sav", _saveDirectory, _installedListView->GetFirstSelected(), _localSteamId);
|
||||
|
||||
if(Utility::Directory::exists(file)) {
|
||||
Utility::Directory::rm(file);
|
||||
switch(_manager.gameState()) {
|
||||
case GameState::Unknown:
|
||||
errorMessage(error_prefix + "For security reasons, deleting a M.A.S.S. is disabled if the game's status is unknown.");
|
||||
break;
|
||||
case GameState::NotRunning:
|
||||
if(!_manager.deleteMass(_installedListView->GetFirstSelected())) {
|
||||
errorMessage(error_prefix + _manager.lastError());
|
||||
}
|
||||
break;
|
||||
case GameState::Running:
|
||||
errorMessage(error_prefix + "Deleting a M.A.S.S. is disabled while the game is running.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EvtMainFrame::backupEvent(wxCommandEvent&) {
|
||||
const static std::string error_prefix = "Backup failed:\n\n";
|
||||
|
||||
wxString current_timestamp = wxDateTime::Now().Format("%Y-%m-%d_%H-%M-%S");
|
||||
|
||||
wxFileDialog save_dialog{this, "Choose output location", _saveDirectory,
|
||||
wxString::Format("backup_%s_%s.zip", _localSteamId, current_timestamp), "Zip archive (*zip)|*zip",
|
||||
wxFileDialog save_dialog{this, "Choose output location", _manager.saveDirectory(),
|
||||
wxString::Format("backup_%s_%s.zip", _manager.steamId(), current_timestamp), "Zip archive (*zip)|*zip",
|
||||
wxFD_SAVE|wxFD_OVERWRITE_PROMPT};
|
||||
|
||||
if(save_dialog.ShowModal() == wxID_CANCEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
wxFFileOutputStream out{save_dialog.GetPath()};
|
||||
wxZipOutputStream zip{out};
|
||||
|
||||
{
|
||||
zip.PutNextEntry(wxString::Format("Profile%s.sav", _localSteamId));
|
||||
wxFFileInputStream profile_stream{wxString::Format("%s\\Profile%s.sav", Utility::Directory::toNativeSeparators(_saveDirectory), _localSteamId), "rb"};
|
||||
zip.Write(profile_stream);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 32; ++i) {
|
||||
std::string unit_file = Utility::formatString("Unit{:.2d}{}.sav", i, _localSteamId);
|
||||
if(Utility::Directory::exists(Utility::Directory::join(_saveDirectory, unit_file))) {
|
||||
zip.PutNextEntry(unit_file);
|
||||
wxFFileInputStream unit_stream{Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, unit_file))};
|
||||
zip.Write(unit_stream);
|
||||
}
|
||||
if(!_manager.backupSaves(save_dialog.GetPath().ToStdString())) {
|
||||
errorMessage(error_prefix + _manager.lastError());
|
||||
}
|
||||
}
|
||||
|
||||
void EvtMainFrame::openSaveDirEvent(wxCommandEvent&) {
|
||||
wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_saveDirectory));
|
||||
wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_manager.saveDirectory()));
|
||||
}
|
||||
|
||||
void EvtMainFrame::installedSelectionEvent(wxListEvent&) {
|
||||
|
@ -236,37 +201,71 @@ void EvtMainFrame::listColumnDragEvent(wxListEvent& event) {
|
|||
|
||||
void EvtMainFrame::fileUpdateEvent(wxFileSystemWatcherEvent& event) {
|
||||
int event_type = event.GetChangeType();
|
||||
wxString event_file = event.GetPath().GetFullName();
|
||||
|
||||
if(event_type == wxFSW_EVENT_MODIFY && _lastWatcherEventType == wxFSW_EVENT_RENAME) {
|
||||
_lastWatcherEventType = event_type;
|
||||
return;
|
||||
}
|
||||
|
||||
_lastWatcherEventType = event_type;
|
||||
|
||||
wxString event_file = event.GetNewPath().GetFullName();
|
||||
|
||||
if(!event_file.EndsWith(".sav")) {
|
||||
return;
|
||||
}
|
||||
|
||||
wxMilliSleep(50);
|
||||
|
||||
if(event_file == wxString::Format("Profile%s.sav", _localSteamId)) {
|
||||
getActiveSlot();
|
||||
}
|
||||
else {
|
||||
wxRegEx regex(wxString::Format("Unit([0-3][0-9])%s.sav", _localSteamId), wxRE_ADVANCED);
|
||||
wxRegEx regex;
|
||||
|
||||
if(regex.Matches(event_file)) {
|
||||
long slot;
|
||||
switch (event_type) {
|
||||
case wxFSW_EVENT_CREATE:
|
||||
case wxFSW_EVENT_DELETE:
|
||||
regex.Compile(wxString::Format("Unit([0-3][0-9])%s\\.sav", _manager.steamId()), wxRE_ADVANCED);
|
||||
if(regex.Matches(event_file)) {
|
||||
long slot;
|
||||
|
||||
if(regex.GetMatch(event_file, 1).ToLong(&slot)) {
|
||||
_installedListView->SetItem(slot, 1, getSlotMassName(slot));
|
||||
if(regex.GetMatch(event_file, 1).ToLong(&slot) && slot >= 0 && slot < 32) {
|
||||
refreshHangar(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wxFSW_EVENT_MODIFY:
|
||||
if(_lastWatcherEventType == wxFSW_EVENT_RENAME) {
|
||||
break;
|
||||
}
|
||||
if(event_file == _manager.profileSaveName()) {
|
||||
getActiveSlot();
|
||||
}
|
||||
else {
|
||||
regex.Compile(wxString::Format("Unit([0-3][0-9])%s\\.sav", _manager.steamId()), wxRE_ADVANCED);
|
||||
if(regex.Matches(event_file)) {
|
||||
long slot;
|
||||
|
||||
if(regex.GetMatch(event_file, 1).ToLong(&slot) && slot >= 0 && slot < 32) {
|
||||
refreshHangar(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wxFSW_EVENT_RENAME:
|
||||
wxString new_name = event.GetNewPath().GetFullName();
|
||||
|
||||
long slot;
|
||||
if(regex.Compile(wxString::Format("Unit([0-3][0-9])%s\\.sav\\.tmp", _manager.steamId()), wxRE_ADVANCED), regex.Matches(new_name)) {
|
||||
if(regex.GetMatch(new_name, 1).ToLong(&slot) && slot >= 0 && slot < 32) {
|
||||
refreshHangar(slot);
|
||||
}
|
||||
}
|
||||
else if(regex.Compile(wxString::Format("Unit([0-3][0-9])%s\\.sav", _manager.steamId()), wxRE_ADVANCED), regex.Matches(new_name)) {
|
||||
if(regex.GetMatch(new_name, 1).ToLong(&slot) && slot >= 0 && slot < 32) {
|
||||
refreshHangar(slot);
|
||||
if(regex.Matches(event_file)) {
|
||||
if(regex.GetMatch(event_file, 1).ToLong(&slot) && slot >= 0 && slot < 32) {
|
||||
refreshHangar(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_lastWatcherEventType = event_type;
|
||||
|
||||
updateCommandsState();
|
||||
}
|
||||
|
||||
|
@ -274,43 +273,6 @@ void EvtMainFrame::gameCheckTimerEvent(wxTimerEvent&) {
|
|||
isGameRunning();
|
||||
}
|
||||
|
||||
void EvtMainFrame::getSaveDirectory() {
|
||||
wchar_t h[MAX_PATH];
|
||||
if(!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, h))) {
|
||||
errorMessage("Couldn't get the path for %LOCALAPPDATA%. :/");
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
_saveDirectory = Utility::Directory::join(Utility::Directory::fromNativeSeparators(Utility::Unicode::narrow(h)), "MASS_Builder/Saved/SaveGames");
|
||||
|
||||
if(!Utility::Directory::exists(_saveDirectory)) {
|
||||
errorMessage("Couldn't find the M.A.S.S. Builder save directory at " + _saveDirectory + ". Please run the game at least once to create it.");
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
void EvtMainFrame::getLocalSteamId() {
|
||||
std::vector<std::string> listing = Utility::Directory::list(_saveDirectory);
|
||||
|
||||
wxRegEx regex;
|
||||
if(!regex.Compile("Profile([0-9]{17}).sav", wxRE_ADVANCED)) {
|
||||
errorMessage("Couldn't compile the regex.");
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
for(const std::string& s : listing) {
|
||||
if(regex.Matches(s)) {
|
||||
_localSteamId = regex.GetMatch(s, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
errorMessage("Couldn't find your save files. Please play at least once.");
|
||||
Close();
|
||||
}
|
||||
|
||||
void EvtMainFrame::initialiseListView() {
|
||||
for(long i = 0; i < 32; i++) {
|
||||
_installedListView->InsertItem(i, wxString::Format("%.2i", i + 1));
|
||||
|
@ -325,110 +287,76 @@ void EvtMainFrame::initialiseListView() {
|
|||
}
|
||||
|
||||
void EvtMainFrame::isGameRunning() {
|
||||
WTS_PROCESS_INFOW* process_infos = nullptr;
|
||||
unsigned long process_count = 0;
|
||||
GameState state = _manager.checkGameState();
|
||||
|
||||
if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &process_infos, &process_count)) {
|
||||
for(unsigned long i = 0; i < process_count; ++i) {
|
||||
if(std::wcscmp(process_infos[i].pProcessName, L"MASS_Builder-Win64-Shipping.exe") == 0) {
|
||||
_isGameRunning = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
_isGameRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_isGameRunning) {
|
||||
_gameStatus->SetLabel("running");
|
||||
_gameStatus->SetForegroundColour(wxColour("red"));
|
||||
}
|
||||
else {
|
||||
switch(state) {
|
||||
case GameState::Unknown:
|
||||
_gameStatus->SetLabel("unknown");
|
||||
_gameStatus->SetForegroundColour(wxColour("orange"));
|
||||
break;
|
||||
case GameState::NotRunning:
|
||||
_gameStatus->SetLabel("not running");
|
||||
_gameStatus->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_CAPTIONTEXT));
|
||||
}
|
||||
}
|
||||
else {
|
||||
_isGameRunning = false;
|
||||
_gameStatus->SetLabel("unknown");
|
||||
_gameStatus->SetForegroundColour(wxColour("orange"));
|
||||
}
|
||||
|
||||
if(process_infos != nullptr) {
|
||||
WTSFreeMemory(process_infos);
|
||||
process_infos = nullptr;
|
||||
break;
|
||||
case GameState::Running:
|
||||
_gameStatus->SetLabel("running");
|
||||
_gameStatus->SetForegroundColour(wxColour("red"));
|
||||
break;
|
||||
}
|
||||
|
||||
updateCommandsState();
|
||||
}
|
||||
|
||||
void EvtMainFrame::refreshListView() {
|
||||
for(long i = 0; i < 32; i++) {
|
||||
_installedListView->SetItem(i, 1, getSlotMassName(i));
|
||||
for(int i = 0; i < 32; i++) {
|
||||
refreshHangar(i);
|
||||
}
|
||||
|
||||
updateCommandsState();
|
||||
}
|
||||
|
||||
void EvtMainFrame::getActiveSlot() {
|
||||
auto mmap = Utility::Directory::mapRead(Utility::formatString("{}/Profile{}.sav", _saveDirectory, _localSteamId));
|
||||
char slot = _manager.activeSlot();
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &active_slot_locator[0], &active_slot_locator[31]);
|
||||
|
||||
wxFont tmp_font = _installedListView->GetItemFont(_activeSlot);
|
||||
wxFont tmp_font = _installedListView->GetItemFont(slot);
|
||||
tmp_font.SetWeight(wxFONTWEIGHT_NORMAL);
|
||||
_installedListView->SetItemFont(_activeSlot, tmp_font);
|
||||
_installedListView->SetItemFont(slot, tmp_font);
|
||||
|
||||
if(iter == mmap.end()) {
|
||||
if(std::strncmp(&mmap[0x3F6], "Credit", 6) == 0) {
|
||||
_activeSlot = 0;
|
||||
}
|
||||
else {
|
||||
_activeSlot = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_activeSlot = *(iter + 41);
|
||||
}
|
||||
slot = _manager.getActiveSlot();
|
||||
|
||||
if(_activeSlot != -1) {
|
||||
_installedListView->SetItemFont(_activeSlot, _installedListView->GetItemFont(_activeSlot).Bold());
|
||||
if(slot != -1) {
|
||||
_installedListView->SetItemFont(slot, _installedListView->GetItemFont(slot).Bold());
|
||||
}
|
||||
}
|
||||
|
||||
void EvtMainFrame::updateCommandsState() {
|
||||
long selection = _installedListView->GetFirstSelected();
|
||||
wxString state = "";
|
||||
if(selection != -1) {
|
||||
state = _installedListView->GetItemText(selection, 1);
|
||||
}
|
||||
GameState game_state = _manager.gameState();
|
||||
HangarState hangar_state = _manager.hangarState(selection);
|
||||
|
||||
_importButton->Enable(selection != -1 && !_isGameRunning);
|
||||
_moveButton->Enable(selection != -1 && !_isGameRunning && state != "<Empty>" && state != "<Invalid data>");
|
||||
_deleteButton->Enable(selection != -1 && !_isGameRunning && state != "<Empty>");
|
||||
_importButton->Enable(selection != -1 && game_state != GameState::Running);
|
||||
_moveButton->Enable(selection != -1 && game_state != GameState::Running && hangar_state != HangarState::Empty && hangar_state != HangarState::Invalid);
|
||||
_deleteButton->Enable(selection != -1 && game_state != GameState::Running && hangar_state != HangarState::Empty);
|
||||
}
|
||||
|
||||
std::string EvtMainFrame::getSlotMassName(int index) {
|
||||
std::string unit_file = Utility::formatString("{}/Unit{:.2d}{}.sav", _saveDirectory, index, _localSteamId);
|
||||
if(Utility::Directory::exists(unit_file)) {
|
||||
std::string mass_name = getMassName(unit_file);
|
||||
return (mass_name == "" ? "<Invalid data>" : mass_name);
|
||||
}
|
||||
else {
|
||||
return "<Empty>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string EvtMainFrame::getMassName(const std::string& filename) {
|
||||
auto mmap = Utility::Directory::mapRead(filename);
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
|
||||
|
||||
if(iter == mmap.end()) {
|
||||
return "";
|
||||
void EvtMainFrame::refreshHangar(int slot) {
|
||||
if(slot < 0 && slot >= 32) {
|
||||
return;
|
||||
}
|
||||
|
||||
return iter + 70;
|
||||
_manager.refreshHangar(slot);
|
||||
|
||||
switch(_manager.hangarState(slot)) {
|
||||
case HangarState::Empty:
|
||||
_installedListView->SetItem(slot, 1, "<Empty>");
|
||||
break;
|
||||
case HangarState::Invalid:
|
||||
_installedListView->SetItem(slot, 1, "<Invalid>");
|
||||
break;
|
||||
case HangarState::Filled:
|
||||
_installedListView->SetItem(slot, 1, *(_manager.massName(slot)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EvtMainFrame::infoMessage(const wxString& message) {
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include <wx/fswatcher.h>
|
||||
|
||||
#include "../MassManager/MassManager.h"
|
||||
|
||||
#include "MainFrame.h"
|
||||
|
||||
class EvtMainFrame: public MainFrame {
|
||||
|
@ -28,6 +30,8 @@ class EvtMainFrame: public MainFrame {
|
|||
EvtMainFrame(wxWindow* parent);
|
||||
~EvtMainFrame();
|
||||
|
||||
auto ready() -> bool;
|
||||
|
||||
protected:
|
||||
void importEvent(wxCommandEvent&);
|
||||
void moveEvent(wxCommandEvent&);
|
||||
|
@ -40,24 +44,18 @@ class EvtMainFrame: public MainFrame {
|
|||
void gameCheckTimerEvent(wxTimerEvent&);
|
||||
|
||||
private:
|
||||
void getSaveDirectory();
|
||||
void getLocalSteamId();
|
||||
void initialiseListView();
|
||||
void isGameRunning();
|
||||
void refreshListView();
|
||||
void getActiveSlot();
|
||||
void updateCommandsState();
|
||||
std::string getSlotMassName(int index);
|
||||
std::string getMassName(const std::string& filename);
|
||||
void refreshHangar(int slot);
|
||||
|
||||
void infoMessage(const wxString& message);
|
||||
void warningMessage(const wxString& message);
|
||||
void errorMessage(const wxString& message);
|
||||
|
||||
std::string _saveDirectory;
|
||||
std::string _localSteamId;
|
||||
bool _isGameRunning = false;
|
||||
char _activeSlot = 0;
|
||||
MassManager _manager;
|
||||
|
||||
wxFileSystemWatcher _watcher;
|
||||
int _lastWatcherEventType = 0;
|
||||
|
|
347
MassManager/MassManager.cpp
Normal file
347
MassManager/MassManager.cpp
Normal file
|
@ -0,0 +1,347 @@
|
|||
// wxMASSManager
|
||||
// Copyright (C) 2020 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 <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/regex.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <shlobj.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Utility/Directory.h>
|
||||
#include <Corrade/Utility/FormatStl.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
#include <Corrade/Utility/Unicode.h>
|
||||
|
||||
#include "MassManager.h"
|
||||
|
||||
constexpr unsigned char mass_name_locator[] = { 'N', 'a', 'm', 'e', '_', '4', '5', '_', 'A', '0', '3', '7', 'C', '5', 'D', '5', '4', 'E', '5', '3', '4', '5', '6', '4', '0', '7', 'B', 'D', 'F', '0', '9', '1', '3', '4', '4', '5', '2', '9', 'B', 'B', '\0', 0x0C, '\0', '\0', '\0', 'S', 't', 'r', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y', '\0' };
|
||||
|
||||
constexpr unsigned char steamid_locator[] = { 'A', 'c', 'c', 'o', 'u', 'n', 't', '\0', 0x0C, '\0', '\0', '\0', 'S', 't', 'r', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y', '\0' };
|
||||
|
||||
constexpr unsigned char active_slot_locator[] = { 'A', 'c', 't', 'i', 'v', 'e', 'F', 'r', 'a', 'm', 'e', 'S', 'l', 'o', 't', '\0', 0x0C, '\0', '\0', '\0', 'I', 'n', 't', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y', '\0' };
|
||||
|
||||
MassManager::MassManager() {
|
||||
_ready = findSaveDirectory() && findSteamId();
|
||||
|
||||
if(!_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
_profileSaveName = Utility::formatString("Profile{}.sav", _steamId);
|
||||
|
||||
for(int i = 0; i < 32; ++i) {
|
||||
_hangars[i]._filename = Utility::formatString("Unit{:.2d}{}.sav", i, _steamId);
|
||||
refreshHangar(i);
|
||||
}
|
||||
}
|
||||
|
||||
auto MassManager::ready() -> bool {
|
||||
return _ready;
|
||||
}
|
||||
|
||||
auto MassManager::lastError() -> std::string const& {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
auto MassManager::saveDirectory() -> std::string const& {
|
||||
return _saveDirectory;
|
||||
}
|
||||
|
||||
auto MassManager::steamId() -> std::string const& {
|
||||
return _steamId;
|
||||
}
|
||||
|
||||
auto MassManager::profileSaveName() -> std::string const& {
|
||||
return _profileSaveName;
|
||||
}
|
||||
|
||||
auto MassManager::checkGameState() -> GameState {
|
||||
WTS_PROCESS_INFOW* process_infos = nullptr;
|
||||
unsigned long process_count = 0;
|
||||
|
||||
if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &process_infos, &process_count)) {
|
||||
for(unsigned long i = 0; i < process_count; ++i) {
|
||||
if(std::wcscmp(process_infos[i].pProcessName, L"MASS_Builder-Win64-Shipping.exe") == 0) {
|
||||
_gameState = GameState::Running;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
_gameState = GameState::NotRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_gameState = GameState::Unknown;
|
||||
}
|
||||
|
||||
if(process_infos != nullptr) {
|
||||
WTSFreeMemory(process_infos);
|
||||
process_infos = nullptr;
|
||||
}
|
||||
|
||||
return _gameState;
|
||||
}
|
||||
|
||||
auto MassManager::gameState() -> GameState {
|
||||
return _gameState;
|
||||
}
|
||||
|
||||
auto MassManager::getActiveSlot() -> char{
|
||||
auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_saveDirectory, _profileSaveName));
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &active_slot_locator[0], &active_slot_locator[31]);
|
||||
|
||||
if(iter == mmap.end()) {
|
||||
if(std::strncmp(&mmap[0x3F6], "Credit", 6) == 0) {
|
||||
_activeSlot = 0;
|
||||
}
|
||||
else {
|
||||
_lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file.";
|
||||
_activeSlot = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_activeSlot = *(iter + 41);
|
||||
}
|
||||
|
||||
return _activeSlot;
|
||||
}
|
||||
|
||||
auto MassManager::activeSlot() -> char {
|
||||
return _activeSlot;
|
||||
}
|
||||
|
||||
auto MassManager::importMass(const std::string& source, int hangar) -> bool {
|
||||
if(hangar < 0 && hangar >= 32) {
|
||||
_lastError = "Hangar out of range in MassManager::importMass()";
|
||||
return false;
|
||||
}
|
||||
|
||||
Utility::Directory::copy(source, source + ".tmp");
|
||||
|
||||
{
|
||||
auto mmap = Utility::Directory::map(source + ".tmp");
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &steamid_locator[0], &steamid_locator[23]);
|
||||
|
||||
if(iter == mmap.end()) {
|
||||
_lastError = "The M.A.S.S. file at " + source + " seems to be corrupt.";
|
||||
Utility::Directory::rm(source + ".tmp");
|
||||
return false;
|
||||
}
|
||||
|
||||
iter += 37;
|
||||
|
||||
if(std::strncmp(iter, _steamId.c_str(), _steamId.length()) != 0) {
|
||||
for(int i = 0; i < 17; ++i) {
|
||||
*(iter + i) = _steamId[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string dest = Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename);
|
||||
|
||||
if(Utility::Directory::exists(dest)) {
|
||||
Utility::Directory::rm(dest);
|
||||
}
|
||||
|
||||
Utility::Directory::move(source + ".tmp", dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto MassManager::moveMass(int source, int destination) -> bool {
|
||||
if(source < 0 && source >= 32) {
|
||||
_lastError = "Source hangar out of range in MassManager::moveMass()";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(destination < 0 && destination >= 32) {
|
||||
_lastError = "Destination hangar out of range in MassManager::moveMass()";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string source_file = Utility::Directory::join(_saveDirectory, _hangars[source]._filename);
|
||||
std::string dest_file = Utility::Directory::join(_saveDirectory, _hangars[destination]._filename);
|
||||
HangarState dest_state = _hangars[destination]._state;
|
||||
|
||||
switch(dest_state) {
|
||||
case HangarState::Empty:
|
||||
break;
|
||||
case HangarState::Invalid:
|
||||
Utility::Directory::rm(dest_file);
|
||||
break;
|
||||
case HangarState::Filled:
|
||||
Utility::Directory::move(dest_file, dest_file + ".tmp");
|
||||
break;
|
||||
}
|
||||
|
||||
Utility::Directory::move(source_file, dest_file);
|
||||
|
||||
if(dest_state == HangarState::Filled) {
|
||||
Utility::Directory::move(dest_file + ".tmp", source_file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MassManager::deleteMass(int hangar) {
|
||||
if(hangar < 0 && hangar >= 32) {
|
||||
_lastError = "Hangar number out of range in MassManager::deleteMass()";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string file = Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename);
|
||||
|
||||
if(Utility::Directory::exists(file)) {
|
||||
if(!Utility::Directory::rm(file)) {
|
||||
_lastError = "The M.A.S.S. file couldn't be deleted.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto MassManager::backupSaves(const std::string& filename) -> bool {
|
||||
if(filename.empty() || (filename.length() < 5 && !Utility::String::endsWith(filename, ".zip"))) {
|
||||
_lastError = "Invalid filename " + filename + " in MassManager::backupSaves()";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Utility::Directory::exists(filename)) {
|
||||
if(!Utility::Directory::rm(filename)) {
|
||||
_lastError = "Couldn't overwrite " + filename + " in MassManager::backupSaves()";
|
||||
}
|
||||
}
|
||||
|
||||
wxFFileOutputStream out{filename};
|
||||
wxZipOutputStream zip{out};
|
||||
|
||||
{
|
||||
zip.PutNextEntry(_profileSaveName);
|
||||
wxFFileInputStream profile_stream{Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, _profileSaveName)), "rb"};
|
||||
zip.Write(profile_stream);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 32; ++i) {
|
||||
std::string unit_file = Utility::Directory::join(_saveDirectory, _hangars[i]._filename);
|
||||
if(Utility::Directory::exists(unit_file)) {
|
||||
zip.PutNextEntry(_hangars[i]._filename);
|
||||
wxFFileInputStream unit_stream{Utility::Directory::toNativeSeparators(unit_file)};
|
||||
zip.Write(unit_stream);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MassManager::refreshHangar(int hangar) {
|
||||
if(hangar < 0 && hangar >= 32) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string unit_file = Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename);
|
||||
|
||||
if(!Utility::Directory::exists(unit_file)) {
|
||||
_hangars[hangar]._state = HangarState::Empty;
|
||||
_hangars[hangar]._massName = Containers::NullOpt;
|
||||
}
|
||||
else {
|
||||
Containers::Optional<std::string> name = getMassName(unit_file);
|
||||
|
||||
_hangars[hangar]._state = name ? HangarState::Filled : HangarState::Invalid;
|
||||
_hangars[hangar]._massName = name;
|
||||
}
|
||||
}
|
||||
|
||||
auto MassManager::hangarState(int hangar) -> HangarState {
|
||||
if(hangar < 0 && hangar >= 32) {
|
||||
return HangarState::Empty;
|
||||
}
|
||||
|
||||
return _hangars[hangar]._state;
|
||||
}
|
||||
|
||||
auto MassManager::massName(int hangar) -> Containers::Optional<std::string> {
|
||||
if(hangar < 0 && hangar >= 32) {
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
return _hangars[hangar]._massName;
|
||||
}
|
||||
|
||||
auto MassManager::getMassName(const std::string& filename) -> Containers::Optional<std::string> {
|
||||
Containers::Optional<std::string> name = Containers::NullOpt;
|
||||
|
||||
auto mmap = Utility::Directory::mapRead(filename);
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
|
||||
|
||||
if(iter != mmap.end()) {
|
||||
name = std::string{iter + 70};
|
||||
}
|
||||
else {
|
||||
_lastError = "Couldn't find the M.A.S.S. name in " + filename;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
auto MassManager::findSaveDirectory() -> bool {
|
||||
wchar_t h[MAX_PATH];
|
||||
if(!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, h))) {
|
||||
_lastError = "SHGetFolderPathW() failed in MassManager::findSaveDirectory()";
|
||||
return false;
|
||||
}
|
||||
|
||||
_saveDirectory = Utility::Directory::join(Utility::Directory::fromNativeSeparators(Utility::Unicode::narrow(h)), "MASS_Builder/Saved/SaveGames");
|
||||
|
||||
if(!Utility::Directory::exists(_saveDirectory)) {
|
||||
_lastError = _saveDirectory + " wasn't found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto MassManager::findSteamId() -> bool {
|
||||
std::vector<std::string> listing = Utility::Directory::list(_saveDirectory);
|
||||
|
||||
wxRegEx regex;
|
||||
if(!regex.Compile("Profile([0-9]{17}).sav", wxRE_ADVANCED)) {
|
||||
_lastError = "Couldn't compile the regex in MassManager::findSteamId()";
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const std::string& s : listing) {
|
||||
if(regex.Matches(s)) {
|
||||
_steamId = regex.GetMatch(s, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_lastError = "Couldn't find the profile save.";
|
||||
return false;
|
||||
}
|
91
MassManager/MassManager.h
Normal file
91
MassManager/MassManager.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
#ifndef MASSMANAGER_H
|
||||
#define MASSMANAGER_H
|
||||
|
||||
// wxMASSManager
|
||||
// Copyright (C) 2020 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 <string>
|
||||
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Containers/Optional.h>
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
enum class GameState : uint8_t {
|
||||
Unknown, NotRunning, Running
|
||||
};
|
||||
|
||||
enum class HangarState : uint8_t {
|
||||
Empty, Invalid, Filled
|
||||
};
|
||||
|
||||
class MassManager {
|
||||
public:
|
||||
MassManager();
|
||||
|
||||
auto ready() -> bool;
|
||||
auto lastError() -> std::string const&;
|
||||
|
||||
auto saveDirectory() -> std::string const&;
|
||||
auto steamId() -> std::string const&;
|
||||
auto profileSaveName() -> std::string const&;
|
||||
|
||||
auto checkGameState() -> GameState;
|
||||
auto gameState() -> GameState;
|
||||
|
||||
auto getActiveSlot() -> char;
|
||||
auto activeSlot() -> char;
|
||||
|
||||
auto importMass(const std::string& source, int hangar) -> bool;
|
||||
|
||||
auto moveMass(int source, int destination) -> bool;
|
||||
|
||||
auto deleteMass(int hangar) -> bool;
|
||||
|
||||
auto backupSaves(const std::string& filename) -> bool;
|
||||
|
||||
void refreshHangar(int hangar);
|
||||
auto hangarState(int hangar) -> HangarState;
|
||||
auto massName(int hangar) -> Containers::Optional<std::string>;
|
||||
|
||||
auto getMassName(const std::string& filename) -> Containers::Optional<std::string>;
|
||||
|
||||
private:
|
||||
auto findSaveDirectory() -> bool;
|
||||
auto findSteamId() -> bool;
|
||||
|
||||
bool _ready;
|
||||
|
||||
std::string _lastError = "";
|
||||
|
||||
std::string _saveDirectory = "";
|
||||
std::string _steamId = "";
|
||||
std::string _profileSaveName = "";
|
||||
|
||||
GameState _gameState = GameState::Unknown;
|
||||
|
||||
char _activeSlot = -1;
|
||||
|
||||
struct Hangar {
|
||||
HangarState _state = HangarState::Empty;
|
||||
Containers::Optional<std::string> _massName = Containers::NullOpt;
|
||||
std::string _filename = "";
|
||||
};
|
||||
|
||||
Containers::StaticArray<32, Hangar> _hangars{Containers::ValueInit};
|
||||
};
|
||||
|
||||
#endif //MASSMANAGER_H
|
6
main.cpp
6
main.cpp
|
@ -25,8 +25,12 @@ class MyApp: public wxApp {
|
|||
SetAppDisplayName("wxMASSManager");
|
||||
|
||||
EvtMainFrame* main_frame = new EvtMainFrame(nullptr);
|
||||
main_frame->Show();
|
||||
|
||||
if(!main_frame->ready()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
main_frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue