Finish re{writing} the MASS manager.

This commit is contained in:
Guillaume Jacquemin 2020-09-30 16:27:26 +02:00
parent d7fa2abafb
commit a06ea2caa4
10 changed files with 1231 additions and 872 deletions

View file

@ -51,6 +51,8 @@ add_executable(wxMASSManager WIN32
GUI/NameChangeDialog.cpp GUI/NameChangeDialog.cpp
GUI/EvtNameChangeDialog.h GUI/EvtNameChangeDialog.h
GUI/EvtNameChangeDialog.cpp GUI/EvtNameChangeDialog.cpp
Mass/Mass.h
Mass/Mass.cpp
MassBuilderManager/MassBuilderManager.h MassBuilderManager/MassBuilderManager.h
MassBuilderManager/MassBuilderManager.cpp MassBuilderManager/MassBuilderManager.cpp
MassManager/MassManager.h MassManager/MassManager.h

View file

@ -14,6 +14,13 @@
// 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/version.h>
#if !(CORRADE_VERSION_YEAR * 100 + CORRADE_VERSION_MONTH >= 202006)
#error This application requires Corrade 2020.06 or later to build.
#endif
#include <wx/busyinfo.h>
#include <wx/filedlg.h> #include <wx/filedlg.h>
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include <wx/numdlg.h> #include <wx/numdlg.h>
@ -21,27 +28,50 @@
#include <wx/scrolwin.h> #include <wx/scrolwin.h>
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Directory.h> #include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/FormatStl.h>
#include "EvtNameChangeDialog.h" #include "EvtNameChangeDialog.h"
#include "EvtMainFrame.h" #include "EvtMainFrame.h"
using namespace Corrade; EvtMainFrame::EvtMainFrame(wxWindow* parent):
MainFrame(parent),
EvtMainFrame::EvtMainFrame(wxWindow* parent): MainFrame(parent) { _profileManager{_mbManager.saveDirectory()}
{
SetIcon(wxIcon("MAINICON")); SetIcon(wxIcon("MAINICON"));
warningMessage(wxString::FromUTF8("Before you start using this app, a few things you should know:\n\n" //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" // "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"
"DISCLAIMER: The developer of this application (Guillaume Jacquemin) isn't associated with Vermillion Digital, and both parties cannot be held responsible for data loss or corruption this app might cause. PLEASE USE AT YOUR OWN RISK!\n\n" // "DISCLAIMER: The developer of this application (Guillaume Jacquemin) isn't associated with Vermillion Digital, and both parties cannot be held responsible for data loss or corruption this app might cause. 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.")); // "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."));
if(!_manager.ready()) { if(!_mbManager.ready()) {
errorMessage("There was an issue initialising the manager:\n\n" + _manager.lastError()); errorMessage("There was an error initialising the manager:\n\n" + _mbManager.lastError());
return; return;
} }
if(!_profileManager.ready()) {
errorMessage("There was an error initialising the manager:\n\n" + _profileManager.lastError());
return;
}
for(const Profile& p : _profileManager.profiles()) {
if(p.valid()) {
_profileChoice->Append(wxString::Format("%s%s", p.companyName(), p.type() == ProfileType::Demo ? " (Demo)" : ""));
}
}
_profileManager.setProfile(0);
_profileChoice->SetSelection(0);
_massManager.emplace(_profileManager.profileDirectory(),
_profileManager.currentProfile()->steamId(),
_profileManager.currentProfile()->type() == ProfileType::Demo);
updateProfileStats();
initialiseListView(); initialiseListView();
isGameRunning(); isGameRunning();
@ -52,75 +82,95 @@ EvtMainFrame::EvtMainFrame(wxWindow* parent): MainFrame(parent) {
_installedListView->Connect(wxEVT_LIST_COL_BEGIN_DRAG, wxListEventHandler(EvtMainFrame::listColumnDragEvent), nullptr, this); _installedListView->Connect(wxEVT_LIST_COL_BEGIN_DRAG, wxListEventHandler(EvtMainFrame::listColumnDragEvent), nullptr, this);
_watcher.Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(EvtMainFrame::fileUpdateEvent), nullptr, this); _watcher.Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(EvtMainFrame::fileUpdateEvent), nullptr, this);
_watcher.AddTree(wxFileName(Utility::Directory::toNativeSeparators(_manager.saveDirectory()), wxPATH_WIN), _watcher.AddTree(wxFileName{Utility::Directory::toNativeSeparators(_massManager->saveDirectory()), wxPATH_WIN},
wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME, wxString::Format("*%s.sav", _manager.steamId())); wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME,
wxString::Format("%s*%s.sav", _profileManager.currentProfile()->type() == ProfileType::Demo ? "Demo" : "", _profileManager.currentProfile()->steamId()));
if(_manager.hasDemoUnits()) { auto staged_masses = _massManager->stagedMasses();
int result = wxMessageBox("M.A.S.S.es from the demo version of the game were found.\n\n" for(const auto& s : staged_masses) {
"Do you want to move them to the staging area ?\n" _stagingList->Append(wxString::Format("%s (%s)", s.second, s.first));
"WARNING: M.A.S.S.es from the demo version will keep parts you haven't unlocked in the main game, so you might get an advantage if you haven't progressed to that point.",
"Question", wxCENTRE|wxYES_NO|wxICON_QUESTION, this);
if(result == wxYES) {
_manager.addDemoUnitsToStaging();
}
} }
std::vector<std::string> v = _manager.initialiseStagingArea(); _watcher.AddTree(wxFileName(Utility::Directory::toNativeSeparators(_massManager->stagingAreaDirectory()), wxPATH_WIN),
for(const std::string& s : v) {
_stagingList->Append(s);
}
_watcher.AddTree(wxFileName(Utility::Directory::toNativeSeparators(_manager.stagingAreaDirectory()), wxPATH_WIN),
wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME, "*.sav"); wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME, "*.sav");
_gameCheckTimer.Start(3000); _gameCheckTimer.Start(2000);
if(!_manager.findScreenshotDirectory()) {
warningMessage("Screenshot manager not ready:\n\n" + _manager.lastError());
_screenshotsPanel->Disable();
return;
}
_manager.loadScreenshots();
_watcher.AddTree(wxFileName(Utility::Directory::toNativeSeparators(_manager.screenshotDirectory()), wxPATH_WIN),
wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE, "*.png"); // Not monitoring wxFSW_EVENT_{MODIFY,RENAME}, because they're a massive pain to handle. Ugh.
_screenshotsList->SetImageList(&_screenshotThumbs, wxIMAGE_LIST_NORMAL); _screenshotsList->SetImageList(&_screenshotThumbs, wxIMAGE_LIST_NORMAL);
updateScreenshotList();
} }
EvtMainFrame::~EvtMainFrame() { EvtMainFrame::~EvtMainFrame() {
_watcher.RemoveAll();
_watcher.Disconnect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(EvtMainFrame::fileUpdateEvent), nullptr, this);
_installedListView->Disconnect(wxEVT_LIST_ITEM_SELECTED, wxListEventHandler(EvtMainFrame::installedSelectionEvent), nullptr, this); _installedListView->Disconnect(wxEVT_LIST_ITEM_SELECTED, wxListEventHandler(EvtMainFrame::installedSelectionEvent), nullptr, this);
_installedListView->Disconnect(wxEVT_LIST_ITEM_DESELECTED, wxListEventHandler(EvtMainFrame::installedSelectionEvent), nullptr, this); _installedListView->Disconnect(wxEVT_LIST_ITEM_DESELECTED, wxListEventHandler(EvtMainFrame::installedSelectionEvent), nullptr, this);
_installedListView->Disconnect(wxEVT_LIST_BEGIN_DRAG, wxListEventHandler(EvtMainFrame::listColumnDragEvent), nullptr, this); _installedListView->Disconnect(wxEVT_LIST_BEGIN_DRAG, wxListEventHandler(EvtMainFrame::listColumnDragEvent), nullptr, this);
_installedListView->Disconnect(wxEVT_LIST_COL_BEGIN_DRAG, wxListEventHandler(EvtMainFrame::listColumnDragEvent), nullptr, this); _installedListView->Disconnect(wxEVT_LIST_COL_BEGIN_DRAG, wxListEventHandler(EvtMainFrame::listColumnDragEvent), nullptr, this);
_watcher.Disconnect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(EvtMainFrame::fileUpdateEvent), nullptr, this);
} }
bool EvtMainFrame::ready() { bool EvtMainFrame::ready() {
return _manager.ready(); return _mbManager.ready() && _profileManager.ready();
}
void EvtMainFrame::profileSelectionEvent(wxCommandEvent&) {
_watcher.Remove(wxFileName{Utility::Directory::toNativeSeparators(Utility::Directory::path(_massManager->saveDirectory())) + "\\", wxPATH_WIN});
// Yeah, I really need that `+ "\\"`, because wxWidgets is *that* stupid.
int selection = _profileChoice->GetSelection();
if(_profileManager.setProfile(selection)) {
_massManager.emplace(_profileManager.profileDirectory(),
_profileManager.currentProfile()->steamId(),
_profileManager.currentProfile()->type() == ProfileType::Demo);
_watcher.AddTree(wxFileName{Utility::Directory::toNativeSeparators(_massManager->saveDirectory()), wxPATH_WIN},
wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME,
wxString::Format("%s*%s.sav", _profileManager.currentProfile()->type() == ProfileType::Demo ? "Demo" : "", _profileManager.currentProfile()->steamId()));
updateProfileStats();
refreshListView();
}
}
void EvtMainFrame::backupSelectedProfileEvent(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", _massManager->saveDirectory(),
wxString::Format("backup_%s%s_%s_%s.zip",
_profileManager.currentProfile()->type() == ProfileType::Demo ? "demo_" : "",
_profileManager.currentProfile()->companyName(),
_profileManager.currentProfile()->steamId(),
current_timestamp),
"Zip archive (*.zip)|*.zip",
wxFD_SAVE|wxFD_OVERWRITE_PROMPT};
if(save_dialog.ShowModal() == wxID_CANCEL) {
return;
}
if(!_profileManager.currentProfile()->backup(save_dialog.GetPath().ToStdString())) {
errorMessage(error_prefix + _massManager->lastError());
}
} }
void EvtMainFrame::importMassEvent(wxCommandEvent&) { void EvtMainFrame::importMassEvent(wxCommandEvent&) {
const static std::string error_prefix = "Importing failed:\n\n"; const static std::string error_prefix = "Importing failed:\n\n";
long selected_hangar = _installedListView->GetFirstSelected(); long selected_hangar = _installedListView->GetFirstSelected();
HangarState hangar_state = _manager.hangarState(selected_hangar); MassState mass_state = _massManager->massState(selected_hangar);
int staged_selection = _stagingList->GetSelection(); int staged_selection = _stagingList->GetSelection();
int confirmation; int confirmation;
if(hangar_state == HangarState::Filled) { if(mass_state == MassState::Valid) {
confirmation = wxMessageBox(wxString::Format("Hangar %.2d is already occupied by the M.A.S.S. named \"%s\". Are you sure you want to import the M.A.S.S. named \"%s\" to this hangar ?", confirmation = wxMessageBox(wxString::Format("Hangar %.2d is already occupied by the M.A.S.S. named \"%s\". Are you sure you want to import the M.A.S.S. named \"%s\" to this hangar ?",
selected_hangar + 1, *(_manager.massName(selected_hangar)), _manager.stagedMassName(staged_selection)), selected_hangar + 1, _massManager->massName(selected_hangar), _massManager->stagedMassName(staged_selection)),
"Question", wxYES_NO|wxCENTRE|wxICON_QUESTION, this); "Question", wxYES_NO|wxCENTRE|wxICON_QUESTION, this);
} }
else { else {
confirmation = wxMessageBox(wxString::Format("Are you sure you want to import the M.A.S.S. named \"%s\" to hangar %.2d ?", _manager.stagedMassName(staged_selection), selected_hangar + 1), confirmation = wxMessageBox(wxString::Format("Are you sure you want to import the M.A.S.S. named \"%s\" to hangar %.2d ?", _massManager->stagedMassName(staged_selection), selected_hangar + 1),
"Question", wxYES_NO|wxCENTRE|wxICON_QUESTION, this); "Question", wxYES_NO|wxCENTRE|wxICON_QUESTION, this);
} }
@ -128,13 +178,13 @@ void EvtMainFrame::importMassEvent(wxCommandEvent&) {
return; return;
} }
switch(_manager.gameState()) { switch(_mbManager.gameState()) {
case GameState::Unknown: case GameState::Unknown:
errorMessage(error_prefix + "For security reasons, importing is disabled if the game's status is unknown."); errorMessage(error_prefix + "For security reasons, importing is disabled if the game's status is unknown.");
break; break;
case GameState::NotRunning: case GameState::NotRunning:
if(!_manager.importMass(staged_selection, selected_hangar)) { if(!_massManager->importMass(staged_selection, selected_hangar)) {
errorMessage(error_prefix + _manager.lastError()); errorMessage(error_prefix + _massManager->lastError());
} }
break; break;
case GameState::Running: case GameState::Running:
@ -148,8 +198,8 @@ void EvtMainFrame::exportMassEvent(wxCommandEvent&) {
long slot = _installedListView->GetFirstSelected(); long slot = _installedListView->GetFirstSelected();
if(!_manager.exportMass(slot)) { if(!_massManager->exportMass(slot)) {
errorMessage(error_prefix + _manager.lastError()); errorMessage(error_prefix + _massManager->lastError());
} }
} }
@ -162,20 +212,20 @@ void EvtMainFrame::moveMassEvent(wxCommandEvent&) {
"- If the destination hangar is the same as the source, nothing will happen.\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 already contains a M.A.S.S., the two will be swapped.\n"
"- If the destination contains invalid data, it will be cleared first.", "- If the destination contains invalid data, it will be cleared first.",
*(_manager.massName(source_slot))), _massManager->massName(source_slot)),
"Slot", "Choose a slot", source_slot + 1, 1, 32, this); "Slot", "Choose a slot", source_slot + 1, 1, 32, this);
if(choice == -1 || choice == source_slot) { if(choice == -1 || choice == source_slot) {
return; return;
} }
switch(_manager.gameState()) { switch(_mbManager.gameState()) {
case GameState::Unknown: case GameState::Unknown:
errorMessage(error_prefix + "For security reasons, moving a M.A.S.S. is disabled if the game's status is unknown."); errorMessage(error_prefix + "For security reasons, moving a M.A.S.S. is disabled if the game's status is unknown.");
break; break;
case GameState::NotRunning: case GameState::NotRunning:
if(!_manager.moveMass(source_slot, choice - 1)) { if(!_massManager->moveMass(source_slot, choice - 1)) {
errorMessage(error_prefix + _manager.lastError()); errorMessage(error_prefix + _massManager->lastError());
} }
break; break;
case GameState::Running: case GameState::Running:
@ -192,13 +242,13 @@ void EvtMainFrame::deleteMassEvent(wxCommandEvent&) {
return; return;
} }
switch(_manager.gameState()) { switch(_mbManager.gameState()) {
case GameState::Unknown: case GameState::Unknown:
errorMessage(error_prefix + "For security reasons, deleting a M.A.S.S. is disabled if the game's status is unknown."); errorMessage(error_prefix + "For security reasons, deleting a M.A.S.S. is disabled if the game's status is unknown.");
break; break;
case GameState::NotRunning: case GameState::NotRunning:
if(!_manager.deleteMass(_installedListView->GetFirstSelected())) { if(!_massManager->deleteMass(_installedListView->GetFirstSelected())) {
errorMessage(error_prefix + _manager.lastError()); errorMessage(error_prefix + _massManager->lastError());
} }
break; break;
case GameState::Running: case GameState::Running:
@ -211,17 +261,17 @@ void EvtMainFrame::renameMassEvent(wxCommandEvent&) {
const static std::string error_prefix = "Rename failed:\n\n"; const static std::string error_prefix = "Rename failed:\n\n";
EvtNameChangeDialog dialog{this}; EvtNameChangeDialog dialog{this};
dialog.setName(*(_manager.massName(_installedListView->GetFirstSelected()))); dialog.setName(_massManager->massName(_installedListView->GetFirstSelected()));
int result = dialog.ShowModal(); int result = dialog.ShowModal();
if(result == wxID_OK) { if(result == wxID_OK) {
switch(_manager.gameState()) { switch(_mbManager.gameState()) {
case GameState::Unknown: case GameState::Unknown:
errorMessage(error_prefix + "For security reasons, renaming a M.A.S.S. is disabled if the game's status is unknown."); errorMessage(error_prefix + "For security reasons, renaming a M.A.S.S. is disabled if the game's status is unknown.");
break; break;
case GameState::NotRunning: case GameState::NotRunning:
if(!_manager.renameMass(_installedListView->GetFirstSelected(), dialog.getName())) { if(!_massManager->renameMass(_installedListView->GetFirstSelected(), dialog.getName())) {
errorMessage(error_prefix + _manager.lastError()); errorMessage(error_prefix + _massManager->lastError());
} }
break; break;
case GameState::Running: case GameState::Running:
@ -231,26 +281,8 @@ void EvtMainFrame::renameMassEvent(wxCommandEvent&) {
} }
} }
void EvtMainFrame::backupSavesEvent(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", _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;
}
if(!_manager.backupSaves(save_dialog.GetPath().ToStdString())) {
errorMessage(error_prefix + _manager.lastError());
}
}
void EvtMainFrame::openSaveDirEvent(wxCommandEvent&) { void EvtMainFrame::openSaveDirEvent(wxCommandEvent&) {
wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_manager.saveDirectory())); wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_massManager->saveDirectory()));
} }
void EvtMainFrame::stagingSelectionEvent(wxCommandEvent&) { void EvtMainFrame::stagingSelectionEvent(wxCommandEvent&) {
@ -266,12 +298,12 @@ void EvtMainFrame::deleteStagedEvent(wxCommandEvent&) {
int selection = _stagingList->GetSelection(); int selection = _stagingList->GetSelection();
if(selection != wxNOT_FOUND) { if(selection != wxNOT_FOUND) {
_manager.deleteStagedMass(selection); _massManager->deleteStagedMass(selection);
} }
} }
void EvtMainFrame::openStagingDirEvent(wxCommandEvent&) { void EvtMainFrame::openStagingDirEvent(wxCommandEvent&) {
wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_manager.stagingAreaDirectory())); wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_massManager->stagingAreaDirectory()));
} }
void EvtMainFrame::installedSelectionEvent(wxListEvent&) { void EvtMainFrame::installedSelectionEvent(wxListEvent&) {
@ -287,22 +319,22 @@ void EvtMainFrame::screenshotListSelectionEvent(wxListEvent&) {
} }
void EvtMainFrame::screenshotFilenameSortingEvent(wxCommandEvent&) { void EvtMainFrame::screenshotFilenameSortingEvent(wxCommandEvent&) {
_manager.sortScreenshots(SortType::Filename); _screenshotManager->sortScreenshots(SortType::Filename);
updateScreenshotList(); updateScreenshotList();
} }
void EvtMainFrame::screenshotCreationDateSortingEvent(wxCommandEvent&) { void EvtMainFrame::screenshotCreationDateSortingEvent(wxCommandEvent&) {
_manager.sortScreenshots(SortType::CreationDate); _screenshotManager->sortScreenshots(SortType::CreationDate);
updateScreenshotList(); updateScreenshotList();
} }
void EvtMainFrame::screenshotAscendingSortingEvent(wxCommandEvent&) { void EvtMainFrame::screenshotAscendingSortingEvent(wxCommandEvent&) {
_manager.sortScreenshots(SortOrder::Ascending); _screenshotManager->sortScreenshots(SortOrder::Ascending);
updateScreenshotList(); updateScreenshotList();
} }
void EvtMainFrame::screenshotDescendingSortingEvent(wxCommandEvent&) { void EvtMainFrame::screenshotDescendingSortingEvent(wxCommandEvent&) {
_manager.sortScreenshots(SortOrder::Descending); _screenshotManager->sortScreenshots(SortOrder::Descending);
updateScreenshotList(); updateScreenshotList();
} }
@ -323,12 +355,22 @@ void EvtMainFrame::deleteScreenshotEvent(wxCommandEvent&) {
long selection = _screenshotsList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); long selection = _screenshotsList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if(selection != -1) { if(selection != -1) {
_manager.deleteScreenshot(selection); _screenshotManager->deleteScreenshot(selection);
} }
} }
void EvtMainFrame::openScreenshotDirEvent(wxCommandEvent&) { void EvtMainFrame::openScreenshotDirEvent(wxCommandEvent&) {
wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_manager.screenshotDirectory())); wxExecute("explorer.exe " + Utility::Directory::toNativeSeparators(_screenshotManager->screenshotDirectory()));
}
void EvtMainFrame::tabChangeEvent(wxNotebookEvent& event) {
if(event.GetSelection() == 2 && !_screenshotManager) {
wxBusyInfo busy{"Loading screenshots...", this};
_screenshotManager.emplace(_mbManager.saveDirectory());
_watcher.AddTree(wxFileName(Utility::Directory::toNativeSeparators(_screenshotManager->screenshotDirectory()), wxPATH_WIN),
wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE, "*.png"); // Not monitoring MODIFY or RENAME, because they're a massive pain to handle. Ugh.
updateScreenshotList();
}
} }
void EvtMainFrame::fileUpdateEvent(wxFileSystemWatcherEvent& event) { void EvtMainFrame::fileUpdateEvent(wxFileSystemWatcherEvent& event) {
@ -342,13 +384,15 @@ void EvtMainFrame::fileUpdateEvent(wxFileSystemWatcherEvent& event) {
wxMilliSleep(50); wxMilliSleep(50);
if(event.GetPath().GetPath(wxPATH_GET_VOLUME, wxPATH_WIN) == Utility::Directory::toNativeSeparators(_manager.saveDirectory())) { wxString event_path = event.GetPath().GetPath(wxPATH_GET_VOLUME, wxPATH_WIN);
if(event_path == Utility::Directory::toNativeSeparators(_massManager->saveDirectory())) {
unitFileEventHandler(event_type, event_file, event); unitFileEventHandler(event_type, event_file, event);
} }
else if(event.GetPath().GetPath(wxPATH_GET_VOLUME, wxPATH_WIN) == Utility::Directory::toNativeSeparators(_manager.stagingAreaDirectory())) { else if(event_path == Utility::Directory::toNativeSeparators(_massManager->stagingAreaDirectory())) {
stagingFileEventHandler(event_type, event_file, event); stagingFileEventHandler(event_type, event_file, event);
} }
else if(event.GetPath().GetPath(wxPATH_GET_VOLUME, wxPATH_WIN) == Utility::Directory::toNativeSeparators(_manager.screenshotDirectory())) { else if(event_path == Utility::Directory::toNativeSeparators(_screenshotManager->screenshotDirectory())) {
screenshotFileEventHandler(event_type, event_file); screenshotFileEventHandler(event_type, event_file);
} }
@ -367,7 +411,7 @@ void EvtMainFrame::unitFileEventHandler(int event_type, const wxString& event_fi
switch (event_type) { switch (event_type) {
case wxFSW_EVENT_CREATE: case wxFSW_EVENT_CREATE:
case wxFSW_EVENT_DELETE: case wxFSW_EVENT_DELETE:
regex.Compile(wxString::Format("Unit([0-3][0-9])%s\\.sav", _manager.steamId()), wxRE_ADVANCED); regex.Compile(wxString::Format("%sUnit([0-3][0-9])%s\\.sav", _profileManager.currentProfile()->type() == ProfileType::Demo ? "Demo" : "", _profileManager.currentProfile()->steamId()), wxRE_ADVANCED);
if(regex.Matches(event_file)) { if(regex.Matches(event_file)) {
long slot; long slot;
@ -380,11 +424,11 @@ void EvtMainFrame::unitFileEventHandler(int event_type, const wxString& event_fi
if(_lastWatcherEventType == wxFSW_EVENT_RENAME) { if(_lastWatcherEventType == wxFSW_EVENT_RENAME) {
break; break;
} }
if(event_file == _manager.profileSaveName()) { if(event_file == _profileManager.currentProfile()->filename()) {
getActiveSlot(); getActiveSlot();
} }
else { else {
regex.Compile(wxString::Format("Unit([0-3][0-9])%s\\.sav", _manager.steamId()), wxRE_ADVANCED); regex.Compile(wxString::Format("%sUnit([0-3][0-9])%s\\.sav", _profileManager.currentProfile()->type() == ProfileType::Demo ? "Demo" : "", _profileManager.currentProfile()->steamId()), wxRE_ADVANCED);
if(regex.Matches(event_file)) { if(regex.Matches(event_file)) {
long slot; long slot;
@ -398,12 +442,12 @@ void EvtMainFrame::unitFileEventHandler(int event_type, const wxString& event_fi
wxString new_name = event.GetNewPath().GetFullName(); wxString new_name = event.GetNewPath().GetFullName();
long slot; 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.Compile(wxString::Format("%sUnit([0-3][0-9])%s\\.sav\\.tmp", _profileManager.currentProfile()->type() == ProfileType::Demo ? "Demo" : "", _profileManager.currentProfile()->steamId()), wxRE_ADVANCED), regex.Matches(new_name)) {
if(regex.GetMatch(new_name, 1).ToLong(&slot) && slot >= 0 && slot < 32) { if(regex.GetMatch(new_name, 1).ToLong(&slot) && slot >= 0 && slot < 32) {
refreshHangar(slot); refreshHangar(slot);
} }
} }
else if(regex.Compile(wxString::Format("Unit([0-3][0-9])%s\\.sav", _manager.steamId()), wxRE_ADVANCED), regex.Matches(new_name)) { else if(regex.Compile(wxString::Format("%sUnit([0-3][0-9])%s\\.sav", _profileManager.currentProfile()->type() == ProfileType::Demo ? "Demo" : "", _profileManager.currentProfile()->steamId()), wxRE_ADVANCED), regex.Matches(new_name)) {
if(regex.GetMatch(new_name, 1).ToLong(&slot) && slot >= 0 && slot < 32) { if(regex.GetMatch(new_name, 1).ToLong(&slot) && slot >= 0 && slot < 32) {
refreshHangar(slot); refreshHangar(slot);
if(regex.Matches(event_file)) { if(regex.Matches(event_file)) {
@ -422,31 +466,31 @@ void EvtMainFrame::stagingFileEventHandler(int event_type, const wxString& event
switch(event_type) { switch(event_type) {
case wxFSW_EVENT_CREATE: case wxFSW_EVENT_CREATE:
index = _manager.updateStagedMass(event_file.ToUTF8().data()); index = _massManager->updateStagedMass(event_file.ToUTF8().data());
if(index != -1) { if(index != -1) {
_stagingList->Insert(wxString::Format("%s (%s)", _manager.stagedMassName(index), event_file), index); _stagingList->Insert(wxString::Format("%s (%s)", _massManager->stagedMassName(index), event_file), index);
} }
break; break;
case wxFSW_EVENT_DELETE: case wxFSW_EVENT_DELETE:
index = _manager.removeStagedMass(event_file.ToUTF8().data()); index = _massManager->removeStagedMass(event_file.ToUTF8().data());
if(index != -1) { if(index != -1) {
_stagingList->Delete(index); _stagingList->Delete(index);
} }
break; break;
case wxFSW_EVENT_MODIFY: case wxFSW_EVENT_MODIFY:
index = _manager.updateStagedMass(event_file.ToUTF8().data()); index = _massManager->updateStagedMass(event_file.ToUTF8().data());
if(index != -1) { if(index != -1) {
_stagingList->SetString(index, wxString::Format("%s (%s)", _manager.stagedMassName(index), event_file)); _stagingList->SetString(index, wxString::Format("%s (%s)", _massManager->stagedMassName(index), event_file));
} }
break; break;
case wxFSW_EVENT_RENAME: case wxFSW_EVENT_RENAME:
index = _manager.removeStagedMass(event_file.ToUTF8().data()); index = _massManager->removeStagedMass(event_file.ToUTF8().data());
if(index != -1) { if(index != -1) {
_stagingList->Delete(index); _stagingList->Delete(index);
} }
index = _manager.updateStagedMass(event.GetNewPath().GetFullName().ToUTF8().data()); index = _massManager->updateStagedMass(event.GetNewPath().GetFullName().ToUTF8().data());
if(index != -1) { if(index != -1) {
_stagingList->Insert(wxString::Format("%s (%s)", _manager.stagedMassName(index), event.GetNewPath().GetFullName()), index); _stagingList->Insert(wxString::Format("%s (%s)", _massManager->stagedMassName(index), event.GetNewPath().GetFullName()), index);
} }
break; break;
} }
@ -457,19 +501,24 @@ void EvtMainFrame::screenshotFileEventHandler(int event_type, const wxString& ev
switch(event_type) { switch(event_type) {
case wxFSW_EVENT_CREATE: case wxFSW_EVENT_CREATE:
_manager.updateScreenshot(event_file.ToUTF8().data()); _screenshotManager->updateScreenshot(event_file.ToUTF8().data());
updateScreenshotList(); updateScreenshotList();
break; break;
case wxFSW_EVENT_DELETE: case wxFSW_EVENT_DELETE:
index = _screenshotsList->FindItem(-1, event_file, true); index = _screenshotsList->FindItem(-1, event_file, true);
if(index != -1) { if(index != -1) {
_manager.removeScreenshot(index); _screenshotManager->removeScreenshot(index);
_screenshotsList->DeleteItem(index); _screenshotsList->DeleteItem(index);
} }
break; break;
} }
} }
void EvtMainFrame::updateProfileStats() {
_companyName->SetLabel(_profileManager.currentProfile()->companyName());
_credits->SetLabel(wxString::Format("%i", _profileManager.currentProfile()->getCredits()));
}
void EvtMainFrame::initialiseListView() { void EvtMainFrame::initialiseListView() {
for(long i = 0; i < 32; i++) { for(long i = 0; i < 32; i++) {
_installedListView->InsertItem(i, wxString::Format("%.2i", i + 1)); _installedListView->InsertItem(i, wxString::Format("%.2i", i + 1));
@ -483,7 +532,10 @@ void EvtMainFrame::initialiseListView() {
} }
void EvtMainFrame::isGameRunning() { void EvtMainFrame::isGameRunning() {
GameState state = _manager.checkGameState(); _gameStatus->SetLabel("checking...");
_gameStatus->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_CAPTIONTEXT));
GameState state = _mbManager.checkGameState();
switch(state) { switch(state) {
case GameState::Unknown: case GameState::Unknown:
@ -512,7 +564,7 @@ void EvtMainFrame::refreshListView() {
} }
void EvtMainFrame::getActiveSlot() { void EvtMainFrame::getActiveSlot() {
char slot = _manager.activeSlot(); auto slot = _profileManager.currentProfile()->activeFrameSlot();
if(slot != -1) { if(slot != -1) {
wxFont tmp_font = _installedListView->GetItemFont(slot); wxFont tmp_font = _installedListView->GetItemFont(slot);
@ -520,7 +572,7 @@ void EvtMainFrame::getActiveSlot() {
_installedListView->SetItemFont(slot, tmp_font); _installedListView->SetItemFont(slot, tmp_font);
} }
slot = _manager.getActiveSlot(); slot = _profileManager.currentProfile()->getActiveFrameSlot();
if(slot != -1) { if(slot != -1) {
_installedListView->SetItemFont(slot, _installedListView->GetItemFont(slot).Bold()); _installedListView->SetItemFont(slot, _installedListView->GetItemFont(slot).Bold());
@ -530,14 +582,14 @@ void EvtMainFrame::getActiveSlot() {
void EvtMainFrame::updateCommandsState() { void EvtMainFrame::updateCommandsState() {
long selection = _installedListView->GetFirstSelected(); long selection = _installedListView->GetFirstSelected();
int staged_selection = _stagingList->GetSelection(); int staged_selection = _stagingList->GetSelection();
GameState game_state = _manager.gameState(); GameState game_state = _mbManager.gameState();
HangarState hangar_state = _manager.hangarState(selection); MassState mass_state = _massManager->massState(selection);
_importButton->Enable(selection != -1 && staged_selection != -1 && game_state != GameState::Running); _importButton->Enable(selection != -1 && staged_selection != -1 && game_state != GameState::Running);
_exportButton->Enable(selection != -1); _exportButton->Enable(selection != -1);
_moveButton->Enable(selection != -1 && game_state != GameState::Running && hangar_state != HangarState::Empty && hangar_state != HangarState::Invalid); _moveButton->Enable(selection != -1 && game_state == GameState::NotRunning && mass_state == MassState::Valid);
_deleteButton->Enable(selection != -1 && game_state != GameState::Running && hangar_state != HangarState::Empty); _deleteButton->Enable(selection != -1 && game_state == GameState::NotRunning && mass_state != MassState::Empty);
_renameButton->Enable(selection != -1 && game_state != GameState::Running && hangar_state != HangarState::Empty); _renameButton->Enable(selection != -1 && game_state == GameState::NotRunning && mass_state == MassState::Valid);
_deleteStagedButton->Enable(staged_selection != -1); _deleteStagedButton->Enable(staged_selection != -1);
long screenshot_selection = _screenshotsList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); long screenshot_selection = _screenshotsList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
@ -550,17 +602,17 @@ void EvtMainFrame::refreshHangar(int slot) {
return; return;
} }
_manager.refreshHangar(slot); _massManager->refreshHangar(slot);
switch(_manager.hangarState(slot)) { switch(_massManager->massState(slot)) {
case HangarState::Empty: case MassState::Empty:
_installedListView->SetItem(slot, 1, "<Empty>"); _installedListView->SetItem(slot, 1, "<Empty>");
break; break;
case HangarState::Invalid: case MassState::Invalid:
_installedListView->SetItem(slot, 1, "<Invalid>"); _installedListView->SetItem(slot, 1, "<Invalid>");
break; break;
case HangarState::Filled: case MassState::Valid:
_installedListView->SetItem(slot, 1, *(_manager.massName(slot))); _installedListView->SetItem(slot, 1, _massManager->massName(slot));
break; break;
} }
} }
@ -570,7 +622,7 @@ void EvtMainFrame::updateScreenshotList() {
_screenshotThumbs.RemoveAll(); _screenshotThumbs.RemoveAll();
int index = 0; int index = 0;
for(const Screenshot& s : _manager.screenshots()) { for(const Screenshot& s : _screenshotManager->screenshots()) {
_screenshotsList->InsertItem(index, _screenshotsList->InsertItem(index,
wxString::Format("%s\n%s", wxString::FromUTF8(s._filename.c_str()), s._creationDate.Format("%d/%m/%Y %H:%M:%S")), wxString::Format("%s\n%s", wxString::FromUTF8(s._filename.c_str()), s._creationDate.Format("%d/%m/%Y %H:%M:%S")),
_screenshotThumbs.Add(s._thumbnail)); _screenshotThumbs.Add(s._thumbnail));
@ -585,7 +637,8 @@ void EvtMainFrame::viewScreenshot() {
return; return;
} }
wxBitmap image(Utility::Directory::toNativeSeparators(Utility::Directory::join(_manager.screenshotDirectory(), _manager.screenshots().at(selection)._filename)), wxBITMAP_TYPE_PNG); wxBitmap image(Utility::Directory::toNativeSeparators(Utility::Directory::join(_screenshotManager->screenshotDirectory(),
_screenshotManager->screenshots().at(selection)._filename)), wxBITMAP_TYPE_PNG);
wxDialog view_dialog; wxDialog view_dialog;
view_dialog.Create(this, wxID_ANY, "Screenshot viewer", wxDefaultPosition, wxSize{1024, 576}, wxCAPTION|wxCLOSE_BOX|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER|wxSYSTEM_MENU); view_dialog.Create(this, wxID_ANY, "Screenshot viewer", wxDefaultPosition, wxSize{1024, 576}, wxCAPTION|wxCLOSE_BOX|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER|wxSYSTEM_MENU);

View file

@ -19,13 +19,21 @@
#include <string> #include <string>
#include <Corrade/Containers/Pointer.h>
#include <wx/fswatcher.h> #include <wx/fswatcher.h>
#include <wx/imaglist.h> #include <wx/imaglist.h>
#include "../MassManager/MassManager.h" #include "../MassManager/MassManager.h"
#include "../MassBuilderManager/MassBuilderManager.h"
#include "../Profile/Profile.h"
#include "../ProfileManager/ProfileManager.h"
#include "../ScreenshotManager/ScreenshotManager.h"
#include "MainFrame.h" #include "MainFrame.h"
using namespace Corrade;
class EvtMainFrame: public MainFrame { class EvtMainFrame: public MainFrame {
public: public:
EvtMainFrame(wxWindow* parent); EvtMainFrame(wxWindow* parent);
@ -34,13 +42,16 @@ class EvtMainFrame: public MainFrame {
auto ready() -> bool; auto ready() -> bool;
protected: protected:
// Profile-related events
void profileSelectionEvent(wxCommandEvent&);
void backupSelectedProfileEvent(wxCommandEvent&);
// M.A.S.S.-related events // M.A.S.S.-related events
void importMassEvent(wxCommandEvent&); void importMassEvent(wxCommandEvent&);
void exportMassEvent(wxCommandEvent&); void exportMassEvent(wxCommandEvent&);
void moveMassEvent(wxCommandEvent&); void moveMassEvent(wxCommandEvent&);
void deleteMassEvent(wxCommandEvent&); void deleteMassEvent(wxCommandEvent&);
void renameMassEvent(wxCommandEvent&); void renameMassEvent(wxCommandEvent&);
void backupSavesEvent(wxCommandEvent&);
void openSaveDirEvent(wxCommandEvent&); void openSaveDirEvent(wxCommandEvent&);
void stagingSelectionEvent(wxCommandEvent&); void stagingSelectionEvent(wxCommandEvent&);
void deleteStagedEvent(wxCommandEvent&); void deleteStagedEvent(wxCommandEvent&);
@ -48,7 +59,7 @@ class EvtMainFrame: public MainFrame {
void installedSelectionEvent(wxListEvent&); void installedSelectionEvent(wxListEvent&);
void listColumnDragEvent(wxListEvent&); void listColumnDragEvent(wxListEvent&);
// Screenshot events // Screenshot-related events
void screenshotListSelectionEvent(wxListEvent&); void screenshotListSelectionEvent(wxListEvent&);
void screenshotFilenameSortingEvent(wxCommandEvent&); void screenshotFilenameSortingEvent(wxCommandEvent&);
void screenshotCreationDateSortingEvent(wxCommandEvent&); void screenshotCreationDateSortingEvent(wxCommandEvent&);
@ -60,6 +71,7 @@ class EvtMainFrame: public MainFrame {
void openScreenshotDirEvent(wxCommandEvent&); void openScreenshotDirEvent(wxCommandEvent&);
// General events // General events
void tabChangeEvent(wxNotebookEvent& event);
void fileUpdateEvent(wxFileSystemWatcherEvent& event); void fileUpdateEvent(wxFileSystemWatcherEvent& event);
void gameCheckTimerEvent(wxTimerEvent&); void gameCheckTimerEvent(wxTimerEvent&);
@ -68,6 +80,8 @@ class EvtMainFrame: public MainFrame {
void stagingFileEventHandler(int event_type, const wxString& event_file, const wxFileSystemWatcherEvent& event); void stagingFileEventHandler(int event_type, const wxString& event_file, const wxFileSystemWatcherEvent& event);
void screenshotFileEventHandler(int event_type, const wxString& event_file); void screenshotFileEventHandler(int event_type, const wxString& event_file);
void updateProfileStats();
void initialiseListView(); void initialiseListView();
void isGameRunning(); void isGameRunning();
void refreshListView(); void refreshListView();
@ -82,7 +96,10 @@ class EvtMainFrame: public MainFrame {
void warningMessage(const wxString& message); void warningMessage(const wxString& message);
void errorMessage(const wxString& message); void errorMessage(const wxString& message);
MassManager _manager; MassBuilderManager _mbManager;
ProfileManager _profileManager;
Containers::Pointer<MassManager> _massManager;
Containers::Pointer<ScreenshotManager> _screenshotManager;
wxFileSystemWatcher _watcher; wxFileSystemWatcher _watcher;
int _lastWatcherEventType = 0; int _lastWatcherEventType = 0;

View file

@ -20,7 +20,67 @@ MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& title, co
wxBoxSizer* bSizerMainPanel; wxBoxSizer* bSizerMainPanel;
bSizerMainPanel = new wxBoxSizer( wxVERTICAL ); bSizerMainPanel = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bSizerProfile;
bSizerProfile = new wxBoxSizer( wxHORIZONTAL );
_profileLabel = new wxStaticText( _mainPanel, wxID_ANY, wxT("Profile to manage:"), wxDefaultPosition, wxDefaultSize, 0 );
_profileLabel->Wrap( -1 );
bSizerProfile->Add( _profileLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
wxArrayString _profileChoiceChoices;
_profileChoice = new wxChoice( _mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, _profileChoiceChoices, 0 );
_profileChoice->SetSelection( 0 );
_profileChoice->SetMinSize( wxSize( 150,-1 ) );
bSizerProfile->Add( _profileChoice, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
_backupSelectedButton = new wxButton( _mainPanel, wxID_ANY, wxT("Backup selected profile"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerProfile->Add( _backupSelectedButton, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
bSizerMainPanel->Add( bSizerProfile, 0, wxEXPAND, 5 );
_managerNotebook = new wxNotebook( _mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); _managerNotebook = new wxNotebook( _mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
_profilePanel = new wxPanel( _managerNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizerProfilePanel;
bSizerProfilePanel = new wxBoxSizer( wxHORIZONTAL );
wxStaticBoxSizer* sbSizerGeneralInfo;
sbSizerGeneralInfo = new wxStaticBoxSizer( new wxStaticBox( _profilePanel, wxID_ANY, wxT("General information") ), wxVERTICAL );
wxFlexGridSizer* fgSizerGeneralStats;
fgSizerGeneralStats = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizerGeneralStats->AddGrowableCol( 1 );
fgSizerGeneralStats->SetFlexibleDirection( wxBOTH );
fgSizerGeneralStats->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
_companyNameLabel = new wxStaticText( sbSizerGeneralInfo->GetStaticBox(), wxID_ANY, wxT("Company name:"), wxDefaultPosition, wxDefaultSize, 0 );
_companyNameLabel->Wrap( -1 );
fgSizerGeneralStats->Add( _companyNameLabel, 0, wxALL, 5 );
_companyName = new wxStaticText( sbSizerGeneralInfo->GetStaticBox(), wxID_ANY, wxT("<blank>"), wxDefaultPosition, wxDefaultSize, 0 );
_companyName->Wrap( -1 );
fgSizerGeneralStats->Add( _companyName, 0, wxALL|wxEXPAND, 5 );
_creditsLabel = new wxStaticText( sbSizerGeneralInfo->GetStaticBox(), wxID_ANY, wxT("Credits:"), wxDefaultPosition, wxDefaultSize, 0 );
_creditsLabel->Wrap( -1 );
fgSizerGeneralStats->Add( _creditsLabel, 0, wxALL, 5 );
_credits = new wxStaticText( sbSizerGeneralInfo->GetStaticBox(), wxID_ANY, wxT("0"), wxDefaultPosition, wxDefaultSize, 0 );
_credits->Wrap( -1 );
fgSizerGeneralStats->Add( _credits, 0, wxALL|wxEXPAND, 5 );
sbSizerGeneralInfo->Add( fgSizerGeneralStats, 1, wxEXPAND, 5 );
bSizerProfilePanel->Add( sbSizerGeneralInfo, 1, wxEXPAND|wxALL, 5 );
_profilePanel->SetSizer( bSizerProfilePanel );
_profilePanel->Layout();
bSizerProfilePanel->Fit( _profilePanel );
_managerNotebook->AddPage( _profilePanel, wxT("Profile details and stats"), false );
_massPanel = new wxPanel( _managerNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); _massPanel = new wxPanel( _managerNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizerMassPanel; wxBoxSizer* bSizerMassPanel;
bSizerMassPanel = new wxBoxSizer( wxHORIZONTAL ); bSizerMassPanel = new wxBoxSizer( wxHORIZONTAL );
@ -51,9 +111,6 @@ MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& title, co
wxBoxSizer* bSizerSecondRow; wxBoxSizer* bSizerSecondRow;
bSizerSecondRow = new wxBoxSizer( wxHORIZONTAL ); bSizerSecondRow = new wxBoxSizer( wxHORIZONTAL );
_zipButton = new wxButton( sbSizerInstalled->GetStaticBox(), wxID_ANY, wxT("Backup save files"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerSecondRow->Add( _zipButton, 2, wxALL|wxEXPAND, 5 );
_openSaveDirButton = new wxButton( sbSizerInstalled->GetStaticBox(), wxID_ANY, wxT("Open save directory"), wxDefaultPosition, wxDefaultSize, 0 ); _openSaveDirButton = new wxButton( sbSizerInstalled->GetStaticBox(), wxID_ANY, wxT("Open save directory"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerSecondRow->Add( _openSaveDirButton, 2, wxEXPAND|wxALL, 5 ); bSizerSecondRow->Add( _openSaveDirButton, 2, wxEXPAND|wxALL, 5 );
@ -208,10 +265,12 @@ MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& title, co
this->Centre( wxBOTH ); this->Centre( wxBOTH );
// Connect Events // Connect Events
_profileChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( MainFrame::profileSelectionEvent ), NULL, this );
_backupSelectedButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::backupSelectedProfileEvent ), NULL, this );
_managerNotebook->Connect( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, wxNotebookEventHandler( MainFrame::tabChangeEvent ), NULL, this );
_moveButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::moveMassEvent ), NULL, this ); _moveButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::moveMassEvent ), NULL, this );
_deleteButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::deleteMassEvent ), NULL, this ); _deleteButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::deleteMassEvent ), NULL, this );
_renameButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::renameMassEvent ), NULL, this ); _renameButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::renameMassEvent ), NULL, this );
_zipButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::backupSavesEvent ), NULL, this );
_openSaveDirButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openSaveDirEvent ), NULL, this ); _openSaveDirButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openSaveDirEvent ), NULL, this );
_importButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::importMassEvent ), NULL, this ); _importButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::importMassEvent ), NULL, this );
_exportButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::exportMassEvent ), NULL, this ); _exportButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::exportMassEvent ), NULL, this );
@ -234,10 +293,12 @@ MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& title, co
MainFrame::~MainFrame() MainFrame::~MainFrame()
{ {
// Disconnect Events // Disconnect Events
_profileChoice->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( MainFrame::profileSelectionEvent ), NULL, this );
_backupSelectedButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::backupSelectedProfileEvent ), NULL, this );
_managerNotebook->Disconnect( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, wxNotebookEventHandler( MainFrame::tabChangeEvent ), NULL, this );
_moveButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::moveMassEvent ), NULL, this ); _moveButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::moveMassEvent ), NULL, this );
_deleteButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::deleteMassEvent ), NULL, this ); _deleteButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::deleteMassEvent ), NULL, this );
_renameButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::renameMassEvent ), NULL, this ); _renameButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::renameMassEvent ), NULL, this );
_zipButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::backupSavesEvent ), NULL, this );
_openSaveDirButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openSaveDirEvent ), NULL, this ); _openSaveDirButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openSaveDirEvent ), NULL, this );
_importButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::importMassEvent ), NULL, this ); _importButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::importMassEvent ), NULL, this );
_exportButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::exportMassEvent ), NULL, this ); _exportButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::exportMassEvent ), NULL, this );

View file

@ -48,7 +48,7 @@
<property name="size">-1,-1</property> <property name="size">-1,-1</property>
<property name="style">wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU</property> <property name="style">wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU</property>
<property name="subclass">; ; forward_declare</property> <property name="subclass">; ; forward_declare</property>
<property name="title">M.A.S.S. Manager 1.2.2</property> <property name="title">M.A.S.S. Builder Save Tool 2.0.0-alpha</property>
<property name="tooltip"></property> <property name="tooltip"></property>
<property name="window_extra_style"></property> <property name="window_extra_style"></property>
<property name="window_name"></property> <property name="window_name"></property>
@ -119,6 +119,216 @@
<property name="name">bSizerMainPanel</property> <property name="name">bSizerMainPanel</property>
<property name="orient">wxVERTICAL</property> <property name="orient">wxVERTICAL</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizerProfile</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="0">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Profile to manage:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_profileLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="0">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices"></property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">150,-1</property>
<property name="moveable">1</property>
<property name="name">_profileChoice</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="selection">0</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChoice">profileSelectionEvent</event>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="0">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Backup selected profile</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_backupSelectedButton</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">backupSelectedProfileEvent</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="1">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND</property> <property name="flag">wxEXPAND</property>
@ -176,6 +386,346 @@
<property name="window_extra_style"></property> <property name="window_extra_style"></property>
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
<event name="OnNotebookPageChanged">tabChangeEvent</event>
<object class="notebookpage" expanded="1">
<property name="bitmap"></property>
<property name="label">Profile details and stats</property>
<property name="select">0</property>
<object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_profilePanel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizerProfilePanel</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">1</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">General information</property>
<property name="minimum_size"></property>
<property name="name">sbSizerGeneralInfo</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxFlexGridSizer" expanded="1">
<property name="cols">2</property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols">1</property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">fgSizerGeneralStats</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="rows">0</property>
<property name="vgap">0</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Company name:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_companyNameLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">&lt;blank&gt;</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_companyName</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Credits:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_creditsLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">0</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_credits</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="notebookpage" expanded="1"> <object class="notebookpage" expanded="1">
<property name="bitmap"></property> <property name="bitmap"></property>
<property name="label">M.A.S.S.es</property> <property name="label">M.A.S.S.es</property>
@ -310,11 +860,11 @@
<property name="window_style"></property> <property name="window_style"></property>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND</property> <property name="flag">wxEXPAND</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxStaticBoxSizer" expanded="1"> <object class="wxStaticBoxSizer" expanded="0">
<property name="id">wxID_ANY</property> <property name="id">wxID_ANY</property>
<property name="label">Hangar actions</property> <property name="label">Hangar actions</property>
<property name="minimum_size"></property> <property name="minimum_size"></property>
@ -552,79 +1102,6 @@
<property name="name">bSizerSecondRow</property> <property name="name">bSizerSecondRow</property>
<property name="orient">wxHORIZONTAL</property> <property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">2</property>
<object class="wxButton" expanded="0">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Backup save files</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">_zipButton</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">backupSavesEvent</event>
</object>
</object>
<object class="sizeritem" expanded="0"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property> <property name="flag">wxEXPAND|wxALL</property>
@ -702,11 +1179,11 @@
</object> </object>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL</property> <property name="flag">wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1"> <object class="wxBoxSizer" expanded="0">
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="name">bSizerImportExport</property> <property name="name">bSizerImportExport</property>
<property name="orient">wxVERTICAL</property> <property name="orient">wxVERTICAL</property>
@ -859,11 +1336,11 @@
</object> </object>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property> <property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxStaticBoxSizer" expanded="1"> <object class="wxStaticBoxSizer" expanded="0">
<property name="id">wxID_ANY</property> <property name="id">wxID_ANY</property>
<property name="label">Staging area</property> <property name="label">Staging area</property>
<property name="minimum_size"></property> <property name="minimum_size"></property>
@ -1090,7 +1567,7 @@
<property name="bitmap"></property> <property name="bitmap"></property>
<property name="label">Photo mode shots</property> <property name="label">Photo mode shots</property>
<property name="select">0</property> <property name="select">0</property>
<object class="wxPanel" expanded="1"> <object class="wxPanel" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1141,16 +1618,16 @@
<property name="window_extra_style"></property> <property name="window_extra_style"></property>
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property> <property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1"> <object class="wxBoxSizer" expanded="0">
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="name">bSizerScreenshotsPanel</property> <property name="name">bSizerScreenshotsPanel</property>
<property name="orient">wxHORIZONTAL</property> <property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxListCtrl" expanded="1"> <object class="wxListCtrl" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1211,20 +1688,20 @@
<event name="OnListItemSelected">screenshotListSelectionEvent</event> <event name="OnListItemSelected">screenshotListSelectionEvent</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND</property> <property name="flag">wxEXPAND</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1"> <object class="wxBoxSizer" expanded="0">
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="name">bSizerScreenshotCommands</property> <property name="name">bSizerScreenshotCommands</property>
<property name="orient">wxVERTICAL</property> <property name="orient">wxVERTICAL</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property> <property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxStaticBoxSizer" expanded="1"> <object class="wxStaticBoxSizer" expanded="0">
<property name="id">wxID_ANY</property> <property name="id">wxID_ANY</property>
<property name="label">Sorting</property> <property name="label">Sorting</property>
<property name="minimum_size"></property> <property name="minimum_size"></property>
@ -1232,20 +1709,20 @@
<property name="orient">wxVERTICAL</property> <property name="orient">wxVERTICAL</property>
<property name="parent">1</property> <property name="parent">1</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND</property> <property name="flag">wxEXPAND</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1"> <object class="wxBoxSizer" expanded="0">
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="name">bSizerSortType</property> <property name="name">bSizerSortType</property>
<property name="orient">wxHORIZONTAL</property> <property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL</property> <property name="flag">wxALL</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxRadioButton" expanded="1"> <object class="wxRadioButton" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1306,11 +1783,11 @@
<event name="OnRadioButton">screenshotFilenameSortingEvent</event> <event name="OnRadioButton">screenshotFilenameSortingEvent</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL</property> <property name="flag">wxALL</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxRadioButton" expanded="1"> <object class="wxRadioButton" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1373,20 +1850,20 @@
</object> </object>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND</property> <property name="flag">wxEXPAND</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1"> <object class="wxBoxSizer" expanded="0">
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="name">bSizerSortOrder</property> <property name="name">bSizerSortOrder</property>
<property name="orient">wxHORIZONTAL</property> <property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL</property> <property name="flag">wxALL</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxRadioButton" expanded="1"> <object class="wxRadioButton" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1447,11 +1924,11 @@
<event name="OnRadioButton">screenshotAscendingSortingEvent</event> <event name="OnRadioButton">screenshotAscendingSortingEvent</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL</property> <property name="flag">wxALL</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxRadioButton" expanded="1"> <object class="wxRadioButton" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1516,11 +1993,11 @@
</object> </object>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxButton" expanded="1"> <object class="wxButton" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1589,11 +2066,11 @@
<event name="OnButtonClick">viewScreenshotEvent</event> <event name="OnButtonClick">viewScreenshotEvent</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxButton" expanded="1"> <object class="wxButton" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1662,11 +2139,11 @@
<event name="OnButtonClick">deleteScreenshotEvent</event> <event name="OnButtonClick">deleteScreenshotEvent</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxButton" expanded="1"> <object class="wxButton" expanded="0">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -1807,7 +2284,7 @@
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALIGN_CENTER_HORIZONTAL</property> <property name="flag">wxALIGN_CENTER_HORIZONTAL</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1"> <object class="wxBoxSizer" expanded="0">
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="name">bSizerGameStatus</property> <property name="name">bSizerGameStatus</property>
<property name="orient">wxHORIZONTAL</property> <property name="orient">wxHORIZONTAL</property>

View file

@ -9,23 +9,24 @@
#include <wx/artprov.h> #include <wx/artprov.h>
#include <wx/xrc/xmlres.h> #include <wx/xrc/xmlres.h>
#include <wx/listctrl.h> #include <wx/string.h>
#include <wx/stattext.h>
#include <wx/gdicmn.h> #include <wx/gdicmn.h>
#include <wx/font.h> #include <wx/font.h>
#include <wx/colour.h> #include <wx/colour.h>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/string.h> #include <wx/choice.h>
#include <wx/bitmap.h> #include <wx/bitmap.h>
#include <wx/image.h> #include <wx/image.h>
#include <wx/icon.h> #include <wx/icon.h>
#include <wx/button.h> #include <wx/button.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/statbox.h> #include <wx/statbox.h>
#include <wx/listbox.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/listctrl.h>
#include <wx/listbox.h>
#include <wx/radiobut.h> #include <wx/radiobut.h>
#include <wx/notebook.h> #include <wx/notebook.h>
#include <wx/stattext.h>
#include <wx/hyperlink.h> #include <wx/hyperlink.h>
#include <wx/timer.h> #include <wx/timer.h>
#include <wx/frame.h> #include <wx/frame.h>
@ -42,13 +43,20 @@ class MainFrame : public wxFrame
protected: protected:
wxPanel* _mainPanel; wxPanel* _mainPanel;
wxStaticText* _profileLabel;
wxChoice* _profileChoice;
wxButton* _backupSelectedButton;
wxNotebook* _managerNotebook; wxNotebook* _managerNotebook;
wxPanel* _profilePanel;
wxStaticText* _companyNameLabel;
wxStaticText* _companyName;
wxStaticText* _creditsLabel;
wxStaticText* _credits;
wxPanel* _massPanel; wxPanel* _massPanel;
wxListView* _installedListView; wxListView* _installedListView;
wxButton* _moveButton; wxButton* _moveButton;
wxButton* _deleteButton; wxButton* _deleteButton;
wxButton* _renameButton; wxButton* _renameButton;
wxButton* _zipButton;
wxButton* _openSaveDirButton; wxButton* _openSaveDirButton;
wxButton* _importButton; wxButton* _importButton;
wxButton* _exportButton; wxButton* _exportButton;
@ -72,10 +80,12 @@ class MainFrame : public wxFrame
wxTimer _gameCheckTimer; wxTimer _gameCheckTimer;
// Virtual event handlers, overide them in your derived class // Virtual event handlers, overide them in your derived class
virtual void profileSelectionEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void backupSelectedProfileEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void tabChangeEvent( wxNotebookEvent& event ) { event.Skip(); }
virtual void moveMassEvent( wxCommandEvent& event ) { event.Skip(); } virtual void moveMassEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void deleteMassEvent( wxCommandEvent& event ) { event.Skip(); } virtual void deleteMassEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void renameMassEvent( wxCommandEvent& event ) { event.Skip(); } virtual void renameMassEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void backupSavesEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void openSaveDirEvent( wxCommandEvent& event ) { event.Skip(); } virtual void openSaveDirEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void importMassEvent( wxCommandEvent& event ) { event.Skip(); } virtual void importMassEvent( wxCommandEvent& event ) { event.Skip(); }
virtual void exportMassEvent( wxCommandEvent& event ) { event.Skip(); } virtual void exportMassEvent( wxCommandEvent& event ) { event.Skip(); }
@ -96,7 +106,7 @@ class MainFrame : public wxFrame
public: public:
MainFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("M.A.S.S. Manager 1.2.2"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxCLIP_CHILDREN|wxTAB_TRAVERSAL ); MainFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("M.A.S.S. Builder Save Tool 2.0.0-alpha"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxCLIP_CHILDREN|wxTAB_TRAVERSAL );
~MainFrame(); ~MainFrame();

188
Mass/Mass.cpp Normal file
View file

@ -0,0 +1,188 @@
// 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 <Corrade/version.h>
#if !(CORRADE_VERSION_YEAR * 100 + CORRADE_VERSION_MONTH >= 202006)
#error This application requires Corrade 2020.06 or later to build.
#endif
#include <cstring>
#include <algorithm>
#include <Corrade/Containers/Array.h>
#include <Corrade/Utility/Directory.h>
#include "Mass.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' };
std::string Mass::_lastError = "";
Mass::Mass(const std::string& filename) {
_filename = filename;
if(!Utility::Directory::exists(_filename)) {
_lastError = "The file " + _filename + " couldn't be found.";
return;
}
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};
_state = MassState::Valid;
}
else {
_lastError = "The name couldn't be found in " + filename;
_state = MassState::Invalid;
}
}
auto Mass::lastError() -> std::string const& {
return _lastError;
}
auto Mass::getNameFromFile(const std::string& filename) -> std::string {
if(!Utility::Directory::exists(filename)) {
_lastError = "The file " + filename + " couldn't be found.";
return "";
}
std::string name = "";
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 = "The name couldn't be found in " + filename;
}
return name;
}
auto Mass::filename() -> std::string const&{
return _filename;
}
auto Mass::name() -> std::string const&{
return _name;
}
auto Mass::getName() -> std::string const& {
if(!Utility::Directory::exists(_filename)) {
_lastError = "The file " + _filename + " couldn't be found.";
_state = MassState::Empty;
return _name = "";
}
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()) {
_state = MassState::Valid;
return _name = std::string{iter + 70};
}
else {
_lastError = "The name couldn't be found in " + _filename;
_state = MassState::Invalid;
return _name = "";
}
}
auto Mass::state() -> MassState {
return _state;
}
auto Mass::updateSteamId(const std::string& steam_id) -> bool {
if(!Utility::Directory::exists(_filename)) {
_lastError = "The file " + _filename + " couldn't be found.";
_state = MassState::Empty;
return false;
}
Utility::Directory::copy(_filename, _filename + ".tmp");
{
auto mmap = Utility::Directory::map(_filename + ".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 " + _filename + " seems to be corrupt.";
Utility::Directory::rm(_filename + ".tmp");
return false;
}
iter += 37;
if(std::strncmp(iter, steam_id.c_str(), steam_id.length()) != 0) {
for(int i = 0; i < 17; ++i) {
*(iter + i) = steam_id[i];
}
}
}
if(Utility::Directory::exists(_filename)) {
Utility::Directory::rm(_filename);
}
Utility::Directory::move(_filename + ".tmp", _filename);
return true;
}
auto Mass::rename(const std::string& new_name) -> bool {
char length_difference = static_cast<char>(_name.length() - new_name.length());
std::string mass_data = Utility::Directory::readString(_filename);
auto iter = std::search(mass_data.begin(), mass_data.end(), &mass_name_locator[0], &mass_name_locator[56]);
if(iter != mass_data.end()) {
*(iter - 45) = *(iter - 45) - length_difference;
*(iter + 57) = *(iter + 57) - length_difference;
*(iter + 66) = *(iter + 66) - length_difference;
while(*(iter + 70) != '\0') {
mass_data.erase(iter + 70);
}
mass_data.insert(iter + 70, new_name.cbegin(), new_name.cend());
if(!Utility::Directory::writeString(_filename, mass_data)) {
_lastError = "The file" + _filename + " couldn't be written to.";
return false;
}
return true;
}
else {
_lastError = "Couldn't find the M.A.S.S. name in " + _filename;
return false;
}
}

59
Mass/Mass.h Normal file
View file

@ -0,0 +1,59 @@
#ifndef MASS_H
#define MASS_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>
enum class MassState : std::uint8_t {
Empty, Invalid, Valid
};
class Mass {
public:
Mass(const std::string& filename);
Mass(const Mass&) = delete;
Mass& operator=(const Mass&) = delete;
Mass(Mass&&) = default;
Mass& operator=(Mass&&) = default;
static auto lastError() -> std::string const&;
static auto getNameFromFile(const std::string& filename) -> std::string;
auto filename() -> std::string const&;
auto name() -> std::string const&;
auto getName() -> std::string const&;
auto state() -> MassState;
auto updateSteamId(const std::string& steam_id) -> bool;
auto rename(const std::string& new_name) -> bool;
private:
static std::string _lastError;
std::string _filename = "";
std::string _name = "";
MassState _state = MassState::Empty;
};
#endif // MASS_H

View file

@ -14,88 +14,60 @@
// 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 <cstring> #include <Corrade/version.h>
#if !(CORRADE_VERSION_YEAR * 100 + CORRADE_VERSION_MONTH >= 202006)
#error This application requires Corrade 2020.06 or later to build.
#endif
#include <algorithm> #include <algorithm>
#include <wx/filename.h>
#include <wx/regex.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <shlobj.h>
#include <wtsapi32.h>
#include <Corrade/Containers/Array.h>
#include <Corrade/Utility/Directory.h> #include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/FormatStl.h> #include <Corrade/Utility/FormatStl.h>
#include <Corrade/Utility/String.h> #include <Corrade/Utility/String.h>
#include <Corrade/Utility/Unicode.h>
#include "MassManager.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' }; static const std::string empty_string = "";
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' }; const std::string MassManager::_stagingAreaDirectory =
Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging");
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(const std::string& save_path, const std::string& steam_id, bool demo) {
_saveDirectory = save_path;
_steamId = steam_id;
_demo = demo;
constexpr unsigned char credits_locator[] = { 'C', 'r', 'e', 'd', 'i', 't', '\0', 0x0C, '\0', '\0', '\0', 'I', 'n', 't', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y', '\0' }; Containers::arrayReserve(_hangars, 32);
MassManager::MassManager() { std::string mass_filename = "";
_ready = findSaveDirectory() && findSteamId(); for(int i = 0; i < 32; i++) {
mass_filename =
if(!_ready) { Utility::Directory::join(_saveDirectory, Utility::formatString("{}Unit{:.2d}{}.sav", demo ? "Demo" : "", i, _steamId));
return; Containers::arrayAppend(_hangars, Mass{mass_filename});
} }
std::string executable_location = Utility::Directory::path(Utility::Directory::executableLocation()); if(!Utility::Directory::exists(_stagingAreaDirectory)) {
Utility::Directory::mkpath(_stagingAreaDirectory);
_stagingAreaDirectory = Utility::Directory::join(executable_location, "staging");
_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::hasDemoUnits() -> bool {
using Utility::Directory::Flag; using Utility::Directory::Flag;
std::vector<std::string> list = Utility::Directory::list(_saveDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot); std::vector<std::string> file_list =
Utility::Directory::list(_stagingAreaDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
for(const std::string& file : list) { auto iter = std::remove_if(file_list.begin(), file_list.end(), [](std::string& file){
if(Utility::String::beginsWith(file, "DemoUnit") && Utility::String::endsWith(file, ".sav")) { return !Utility::String::endsWith(file, ".sav");
return true; });
file_list.erase(iter, file_list.end());
for(const std::string& file : file_list) {
std::string name = Mass::getNameFromFile(Utility::Directory::join(_stagingAreaDirectory, file));
if(name != "") {
_stagedMasses[file] = name;
} }
} }
return false;
}
void MassManager::addDemoUnitsToStaging() {
using Utility::Directory::Flag;
std::vector<std::string> list = Utility::Directory::list(_saveDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
auto predicate = [](const std::string& name)->bool{
return !(Utility::String::endsWith(name, ".sav") && Utility::String::beginsWith(name, "DemoUnit"));
};
list.erase(std::remove_if(list.begin(), list.end(), predicate), list.end());
for(const std::string& file : list) {
Utility::Directory::move(Utility::Directory::join(_saveDirectory, file),
Utility::Directory::join(_stagingAreaDirectory, file));
}
} }
auto MassManager::saveDirectory() -> std::string const& { auto MassManager::saveDirectory() -> std::string const& {
@ -106,115 +78,38 @@ auto MassManager::stagingAreaDirectory() -> std::string const& {
return _stagingAreaDirectory; return _stagingAreaDirectory;
} }
auto MassManager::screenshotDirectory() -> std::string const& { auto MassManager::lastError() -> std::string const& {
return _screenshotDirectory; return _lastError;
} }
auto MassManager::steamId() -> std::string const& { auto MassManager::massName(int hangar) -> std::string const& {
return _steamId; if(hangar < 0 || hangar >= 32) {
return empty_string;
}
return _hangars[hangar].name();
} }
auto MassManager::profileSaveName() -> std::string const& { auto MassManager::massState(int hangar) -> MassState {
return _profileSaveName; if(hangar < 0 || hangar >= 32) {
return MassState::Empty;
}
return _hangars[hangar].state();
} }
auto MassManager::checkGameState() -> GameState { void MassManager::refreshHangar(int hangar) {
WTS_PROCESS_INFOW* process_infos = nullptr; if(hangar < 0 || hangar >= 32) {
unsigned long process_count = 0; return;
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) { std::string mass_filename =
WTSFreeMemory(process_infos); Utility::Directory::join(_saveDirectory, Utility::formatString("{}Unit{:.2d}{}.sav", _demo ? "Demo" : "", hangar, _steamId));
process_infos = nullptr; _hangars[hangar] = Mass{mass_filename};
}
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::search(mmap.begin(), mmap.end(), &credits_locator[0], &credits_locator[22]) != mmap.end()) {
_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::importMass(int staged_index, int hangar) -> bool { auto MassManager::importMass(int staged_index, int hangar) -> bool {
if(hangar < 0 && hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of range in MassManager::importMass()"; _lastError = "Hangar out of range in MassManager::importMass()";
return false; return false;
} }
@ -230,33 +125,18 @@ auto MassManager::importMass(int staged_index, int hangar) -> bool {
Utility::Directory::copy(source, source + ".tmp"); Utility::Directory::copy(source, source + ".tmp");
if(!Mass{source + ".tmp"}.updateSteamId(_steamId))
{ {
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."; _lastError = "The M.A.S.S. file at " + source + " seems to be corrupt.";
Utility::Directory::rm(source + ".tmp"); Utility::Directory::rm(source + ".tmp");
return false; return false;
} }
iter += 37; if(Utility::Directory::exists(_hangars[hangar].filename())) {
Utility::Directory::rm(_hangars[hangar].filename());
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); Utility::Directory::move(source + ".tmp", _hangars[hangar].filename());
if(Utility::Directory::exists(dest)) {
Utility::Directory::rm(dest);
}
Utility::Directory::move(source + ".tmp", dest);
return true; return true;
} }
@ -266,25 +146,21 @@ auto MassManager::importMass(int staged_index, int hangar) -> bool {
} }
auto MassManager::exportMass(int hangar) -> bool { auto MassManager::exportMass(int hangar) -> bool {
if(hangar < 0 && hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of range in MassManager::exportMass()"; _lastError = "Hangar out of range in MassManager::exportMass()";
return false; return false;
} }
if(_hangars[hangar]._state == HangarState::Empty || if(_hangars[hangar].state() == MassState::Empty ||
_hangars[hangar]._state == HangarState::Invalid) { _hangars[hangar].state() == MassState::Invalid) {
_lastError = Utility::formatString("There is no valid data to export in hangar {:.2d}", hangar); _lastError = Utility::formatString("There is no valid data to export in hangar {:.2d}", hangar);
}
auto name = _hangars[hangar]._massName;
if(!name) {
_lastError = "There was an unexpected error in MassManager::exportMass()";
return false; return false;
} }
std::string source = Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename); auto name = _hangars[hangar].name();
std::string dest = Utility::Directory::join(_stagingAreaDirectory, Utility::formatString("{}_{}.sav", _steamId, *(_hangars[hangar]._massName)));
std::string source = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename());
std::string dest = Utility::Directory::join(_stagingAreaDirectory, Utility::formatString("{}_{}.sav", _steamId, _hangars[hangar].name()));
if(!Utility::Directory::copy(source, dest)) { if(!Utility::Directory::copy(source, dest)) {
_lastError = Utility::formatString("Couldn't export data from hangar {:.2d} to {}", hangar, dest); _lastError = Utility::formatString("Couldn't export data from hangar {:.2d} to {}", hangar, dest);
@ -295,34 +171,34 @@ auto MassManager::exportMass(int hangar) -> bool {
} }
auto MassManager::moveMass(int source, int destination) -> bool { auto MassManager::moveMass(int source, int destination) -> bool {
if(source < 0 && source >= 32) { if(source < 0 || source >= 32) {
_lastError = "Source hangar out of range in MassManager::moveMass()"; _lastError = "Source hangar out of range.";
return false; return false;
} }
if(destination < 0 && destination >= 32) { if(destination < 0 || destination >= 32) {
_lastError = "Destination hangar out of range in MassManager::moveMass()"; _lastError = "Destination hangar out of range.";
return false; return false;
} }
std::string source_file = Utility::Directory::join(_saveDirectory, _hangars[source]._filename); std::string source_file = _hangars[source].filename();
std::string dest_file = Utility::Directory::join(_saveDirectory, _hangars[destination]._filename); std::string dest_file = _hangars[destination].filename();
HangarState dest_state = _hangars[destination]._state; MassState dest_state = _hangars[destination].state();
switch(dest_state) { switch(dest_state) {
case HangarState::Empty: case MassState::Empty:
break; break;
case HangarState::Invalid: case MassState::Invalid:
Utility::Directory::rm(dest_file); Utility::Directory::rm(dest_file);
break; break;
case HangarState::Filled: case MassState::Valid:
Utility::Directory::move(dest_file, dest_file + ".tmp"); Utility::Directory::move(dest_file, dest_file + ".tmp");
break; break;
} }
Utility::Directory::move(source_file, dest_file); Utility::Directory::move(source_file, dest_file);
if(dest_state == HangarState::Filled) { if(dest_state == MassState::Valid) {
Utility::Directory::move(dest_file + ".tmp", source_file); Utility::Directory::move(dest_file + ".tmp", source_file);
} }
@ -330,177 +206,53 @@ auto MassManager::moveMass(int source, int destination) -> bool {
} }
auto MassManager::deleteMass(int hangar) -> bool { auto MassManager::deleteMass(int hangar) -> bool {
if(hangar < 0 && hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar number out of range in MassManager::deleteMass()"; _lastError = "Hangar out of bounds";
return false; return false;
} }
std::string file = Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename); bool result = Utility::Directory::rm(_hangars[hangar].filename());
if(!result) {
if(Utility::Directory::exists(file)) { _lastError = "Deletion failed. Maybe the file was already deleted, or it's locked by another application.";
if(!Utility::Directory::rm(file)) {
_lastError = "The M.A.S.S. file couldn't be deleted.";
return false;
} }
} return result;
return true;
} }
auto MassManager::renameMass(int hangar, const std::string& new_name) -> bool{ auto MassManager::renameMass(int hangar, const std::string& new_name) -> bool {
if(hangar < 0 && hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar number out of range in MassManager::renameMass()"; _lastError = "Hangar number out of range.";
return false; return false;
} }
if(new_name.length() > 32) { if(new_name.length() > 32) {
_lastError = "The new name is longer than 32 characters in MassManager::renameMass()"; _lastError = "The new name is longer than 32 characters.";
return false; return false;
} }
char length_difference = static_cast<char>(massName(hangar)->length() - new_name.length()); if(!_hangars[hangar].rename(new_name)) {
_lastError = _hangars[hangar].lastError();
std::string mass_data = Utility::Directory::readString(Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename));
auto iter = std::search(mass_data.begin(), mass_data.end(), &mass_name_locator[0], &mass_name_locator[56]);
if(iter != mass_data.end()) {
*(iter - 45) = *(iter - 45) - length_difference;
*(iter + 57) = *(iter + 57) - length_difference;
*(iter + 66) = *(iter + 66) - length_difference;
while(*(iter + 70) != '\0') {
mass_data.erase(iter + 70);
}
mass_data.insert(iter + 70, new_name.cbegin(), new_name.cend());
if(!Utility::Directory::writeString(Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename), mass_data)) {
_lastError = "The file" + _hangars[hangar]._filename + " couldn't be written to.";
return false; return false;
} }
return true; return true;
}
else {
_lastError = "Couldn't find the M.A.S.S. name in " + _hangars[hangar]._filename;
return false;
}
} }
auto MassManager::backupSaves(const std::string& filename) -> bool { auto MassManager::stagedMasses() -> std::map<std::string, std::string> const& {
if(filename.empty() || (filename.length() < 5 && !Utility::String::endsWith(filename, ".zip"))) { return _stagedMasses;
_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) { auto MassManager::stagedMassName(int index) -> std::string {
if(hangar < 0 && hangar >= 32) { int i = 0;
return; for(const auto& mass_info : _stagedMasses) {
if(i != index) {
++i;
continue;
} }
std::string unit_file = Utility::Directory::join(_saveDirectory, _hangars[hangar]._filename); return mass_info.second;
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; return std::string{""};
}
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::initialiseStagingArea() -> std::vector<std::string> {
if(!Utility::Directory::exists(_stagingAreaDirectory)) {
Utility::Directory::mkpath(_stagingAreaDirectory);
}
using Utility::Directory::Flag;
std::vector<std::string> file_list = Utility::Directory::list(_stagingAreaDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
auto iter = std::remove_if(file_list.begin(), file_list.end(), [](std::string& file){
return !Utility::String::endsWith(file, ".sav");
});
file_list.erase(iter, file_list.end());
std::vector<std::string> mass_names;
mass_names.reserve(file_list.size());
for(const std::string& file : file_list) {
auto name = getMassName(Utility::Directory::join(_stagingAreaDirectory, file));
if(name) {
mass_names.push_back(Utility::formatString("{} ({})", *name, file));
_stagedMasses[file] = *name;
}
}
mass_names.shrink_to_fit();
return std::move(mass_names);
} }
auto MassManager::updateStagedMass(const std::string& filename) -> int { auto MassManager::updateStagedMass(const std::string& filename) -> int {
@ -510,13 +262,13 @@ auto MassManager::updateStagedMass(const std::string& filename) -> int {
return -1; return -1;
} }
auto name = getMassName(file); auto name = Mass::getNameFromFile(file);
if(!name) { if(name == "") {
return -1; return -1;
} }
_stagedMasses[filename] = *name; _stagedMasses[filename] = name;
int index = 0; int index = 0;
@ -554,190 +306,3 @@ void MassManager::deleteStagedMass(int index) {
} }
} }
} }
auto MassManager::stagedMassName(int index) -> std::string {
int i = 0;
for(const auto& mass_info : _stagedMasses) {
if(i != index) {
++i;
continue;
}
return mass_info.second;
}
return "";
}
auto MassManager::stagedMassName(const std::string& filename) -> std::string {
auto iter = _stagedMasses.find(filename);
if(iter == _stagedMasses.end()) {
return "";
}
return iter->second;
}
void MassManager::loadScreenshots() {
using Utility::Directory::Flag;
std::vector<std::string> file_list = Utility::Directory::list(_screenshotDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
auto iter = std::remove_if(file_list.begin(), file_list.end(), [](std::string& file){
return !Utility::String::endsWith(file, ".png");
});
file_list.erase(iter, file_list.end());
_screenshots.reserve(file_list.size());
for(const std::string& file : file_list) {
addScreenshot(file);
}
}
auto MassManager::screenshots() -> std::vector<Screenshot> const& {
return _screenshots;
}
void MassManager::sortScreenshots(SortType type) {
_sortType = type;
sortScreenshots();
}
void MassManager::sortScreenshots(SortOrder order) {
_sortOrder = order;
sortScreenshots();
}
void MassManager::sortScreenshots() {
auto predicate = [this](const Screenshot& item_1, const Screenshot& item_2)->bool{
switch(_sortType) {
case SortType::Filename:
return wxString::FromUTF8(item_1._filename.c_str()).CmpNoCase(wxString::FromUTF8(item_2._filename.c_str())) < 0;
case SortType::CreationDate:
return item_1._creationDate.IsEarlierThan(item_2._creationDate);
}
return true;
};
switch(_sortOrder) {
case SortOrder::Ascending:
std::stable_sort(_screenshots.begin(), _screenshots.end(), predicate);
break;
case SortOrder::Descending:
std::stable_sort(_screenshots.rbegin(), _screenshots.rend(), predicate);
break;
}
}
auto MassManager::updateScreenshot(const std::string& filename) -> int {
addScreenshot(filename);
sortScreenshots();
int index = 0;
for(const Screenshot& s : _screenshots) {
if(s._filename == filename) {
return index;
}
}
return -1;
}
void MassManager::removeScreenshot(int index) {
if(static_cast<size_t>(index + 1) > _screenshots.size()) {
return;
}
auto it = _screenshots.begin() + index;
_screenshots.erase(it);
}
void MassManager::deleteScreenshot(int index) {
if(static_cast<size_t>(index + 1) > _screenshots.size()) {
return;
}
Utility::Directory::rm(Utility::Directory::join(_screenshotDirectory, _screenshots[index]._filename));
}
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;
}
auto MassManager::findScreenshotDirectory() -> bool {
wchar_t h[MAX_PATH];
if(!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, h))) {
_lastError = "SHGetFolderPathW() failed in MassManager::findScreenshotDirectory()";
return false;
}
_screenshotDirectory = Utility::Directory::join(Utility::Directory::fromNativeSeparators(Utility::Unicode::narrow(h)), "MASS_Builder/Saved/Screenshots/WindowsNoEditor");
if(!Utility::Directory::exists(_screenshotDirectory)) {
_lastError = _screenshotDirectory + " wasn't found.";
return false;
}
return true;
}
void MassManager::addScreenshot(const std::string& filename) {
std::string screenshot_path = Utility::Directory::toNativeSeparators(Utility::Directory::join(_screenshotDirectory, filename));
wxFileName screenshot_meta(screenshot_path);
wxDateTime creation_date;
screenshot_meta.GetTimes(nullptr, nullptr, &creation_date);
wxImage thumb{screenshot_path, wxBITMAP_TYPE_PNG};
wxSize size = thumb.GetSize();
if(size.GetWidth() > size.GetHeight()) {
size.Set(160, (size.GetHeight() * 160) / size.GetWidth());
}
else if(size.GetHeight() > size.GetWidth()) {
size.Set((size.GetWidth() * 160) / size.GetHeight(), 160);
}
else {
size.Set(160, 160);
}
thumb.Rescale(size.GetWidth(), size.GetHeight(), wxIMAGE_QUALITY_HIGH)
.Resize(wxSize{160, 160}, wxPoint{(160 - size.GetWidth()) / 2, (160 - size.GetHeight()) / 2});
_screenshots.push_back(Screenshot{filename, creation_date, thumb});
}

View file

@ -19,62 +19,27 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vector>
#include <Corrade/Containers/StaticArray.h> #include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
#include <wx/datetime.h> #include "../Mass/Mass.h"
#include <wx/image.h>
using namespace Corrade; using namespace Corrade;
enum class GameState : uint8_t {
Unknown, NotRunning, Running
};
enum class HangarState : uint8_t {
Empty, Invalid, Filled
};
enum class SortType : uint8_t {
Filename, CreationDate
};
enum class SortOrder: uint8_t {
Ascending, Descending
};
struct Screenshot {
std::string _filename;
wxDateTime _creationDate;
wxImage _thumbnail;
};
class MassManager { class MassManager {
public: public:
MassManager(); MassManager(const std::string& save_path, const std::string& steam_id, bool demo);
auto ready() -> bool;
auto lastError() -> std::string const&;
auto hasDemoUnits() -> bool;
void addDemoUnitsToStaging();
auto saveDirectory() -> std::string const&; auto saveDirectory() -> std::string const&;
auto stagingAreaDirectory() -> std::string const&; auto stagingAreaDirectory() -> std::string const&;
auto screenshotDirectory() -> std::string const&;
auto steamId() -> std::string const&; auto lastError() -> std::string const&;
auto profileSaveName() -> std::string const&;
auto checkGameState() -> GameState; auto massName(int hangar) -> std::string const&;
auto gameState() -> GameState; auto massState(int hangar) -> MassState;
auto getActiveSlot() -> char; void refreshHangar(int hangar);
auto activeSlot() -> char;
auto importMass(const std::string& source, int hangar) -> bool;
auto importMass(int staged_index, int hangar) -> bool; auto importMass(int staged_index, int hangar) -> bool;
auto exportMass(int hangar) -> bool; auto exportMass(int hangar) -> bool;
@ -82,64 +47,26 @@ class MassManager {
auto deleteMass(int hangar) -> bool; auto deleteMass(int hangar) -> bool;
auto renameMass(int hangar, const std::string& new_name) -> bool; auto renameMass(int hangar, const std::string& new_name) -> bool;
auto backupSaves(const std::string& filename) -> bool; auto stagedMasses() -> std::map<std::string, std::string> const&;
void refreshHangar(int hangar); auto stagedMassName(int index) -> std::string;
auto hangarState(int hangar) -> HangarState;
auto massName(int hangar) -> Containers::Optional<std::string>;
auto getMassName(const std::string& filename) -> Containers::Optional<std::string>;
auto initialiseStagingArea() -> std::vector<std::string>;
auto updateStagedMass(const std::string& filename) -> int; auto updateStagedMass(const std::string& filename) -> int;
auto removeStagedMass(const std::string& filename) -> int; auto removeStagedMass(const std::string& filename) -> int;
void deleteStagedMass(int index); void deleteStagedMass(int index);
auto stagedMassName(int index) -> std::string;
auto stagedMassName(const std::string& filename) -> std::string;
auto findScreenshotDirectory() -> bool;
void loadScreenshots();
auto screenshots() -> std::vector<Screenshot> const&;
void sortScreenshots(SortType type);
void sortScreenshots(SortOrder order);
void sortScreenshots();
auto updateScreenshot(const std::string& filename) -> int;
void removeScreenshot(int index);
void deleteScreenshot(int index);
private: private:
auto findSaveDirectory() -> bool; std::string _saveDirectory;
auto findSteamId() -> bool; std::string _steamId;
bool _demo;
void addScreenshot(const std::string& filename);
bool _ready = false;
std::string _lastError = ""; std::string _lastError = "";
std::string _stagingAreaDirectory = ""; Containers::Array<Mass> _hangars;
std::string _saveDirectory = "";
std::string _screenshotDirectory = "";
std::string _steamId = "";
std::string _profileSaveName = "";
GameState _gameState = GameState::Unknown; static const std::string _stagingAreaDirectory;
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};
std::map<std::string, std::string> _stagedMasses; std::map<std::string, std::string> _stagedMasses;
std::vector<Screenshot> _screenshots;
SortType _sortType = SortType::Filename;
SortOrder _sortOrder = SortOrder::Ascending;
}; };
#endif //MASSMANAGER_H #endif //MASSMANAGER_H