From f532803d560911629608f4f3c209a54267bf9a23 Mon Sep 17 00:00:00 2001 From: William JCM Date: Tue, 11 May 2021 14:54:33 +0200 Subject: [PATCH] Add a way to change the story progression state. --- GUI/EvtMainFrame.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++ GUI/EvtMainFrame.h | 5 +++ GUI/MainFrame.cpp | 6 ++++ GUI/MainFrame.fbp | 73 +++++++++++++++++++++++++++++++++++++ GUI/MainFrame.h | 2 ++ Profile/Profile.cpp | 15 ++++++++ Profile/Profile.h | 1 + 7 files changed, 188 insertions(+) diff --git a/GUI/EvtMainFrame.cpp b/GUI/EvtMainFrame.cpp index ee60bf0..732d76c 100644 --- a/GUI/EvtMainFrame.cpp +++ b/GUI/EvtMainFrame.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -107,6 +108,8 @@ EvtMainFrame::EvtMainFrame(wxWindow* parent): wxFSW_EVENT_CREATE|wxFSW_EVENT_DELETE|wxFSW_EVENT_MODIFY|wxFSW_EVENT_RENAME, "*.sav"); _gameCheckTimer.Start(2000); + + initStoryProgressMenu(); } EvtMainFrame::~EvtMainFrame() { @@ -207,6 +210,37 @@ void EvtMainFrame::companyRenameEvent(wxMouseEvent&) { } } +void EvtMainFrame::storyProgressSelectionEvent(wxCommandEvent& event) { + const static std::string error_prefix = "StoryProgress change failed:\n\n"; + + std::int32_t story_progress = event.GetId() ^ (-10000); + + if(_unsafeMode == false) { + switch(_mbManager.gameState()) { + case GameState::Unknown: + errorMessage(error_prefix + "For security reasons, changing the story progression is disabled if the game's status is unknown."); + break; + case GameState::NotRunning: + if(!_profileManager.currentProfile()->setStoryProgress(story_progress)) { + errorMessage(error_prefix + _profileManager.currentProfile()->lastError()); + } + break; + case GameState::Running: + errorMessage(error_prefix + "Changing the story progression is disabled while the game is running."); + break; + } + } + else if(!_profileManager.currentProfile()->setStoryProgress(story_progress)) { + errorMessage(error_prefix + _profileManager.currentProfile()->lastError()); + } + + updateProfileStats(); +} + +void EvtMainFrame::openStoryProgressMenuEvent(wxCommandEvent&) { + PopupMenu(_storyProgressSelectionMenu.get()); +} + void EvtMainFrame::importMassEvent(wxCommandEvent&) { const static std::string error_prefix = "Importing failed:\n\n"; @@ -560,6 +594,57 @@ void EvtMainFrame::updateProfileStats() { } } +void EvtMainFrame::initStoryProgressMenu() { + _storyProgressSelectionMenu.emplace(); + + if(!_storyProgressSelectionMenu) { + errorMessage("Error initialising the story progress selection menu."); + this->Destroy(); + return; + } + + wxMenu* submenu = nullptr; + + for(const auto& pair : story_progress_map) { + if(std::strncmp(pair.second + 10, "start", 5) == 0) { + submenu = new wxMenu(); + + if(!submenu) { + errorMessage("Error initialising the story progress selection menu."); + this->Destroy(); + return; + } + + _storyProgressSelectionMenu->Append(wxID_ANY, wxString{pair.second, 9}, submenu); + + wxMenuItem* item = submenu->Append(pair.first ^ (-10000), "Chapter start"); + + if(!item) { + errorMessage("Error initialising the story progress selection menu."); + this->Destroy(); + return; + } + } + else { + if(!submenu) { + errorMessage("Error initialising the story progress selection menu."); + this->Destroy(); + return; + } + + wxMenuItem* item = submenu->Append(pair.first ^ (-10000), wxString{pair.second + 12}); + + if(!item) { + errorMessage("Error initialising the story progress selection menu."); + this->Destroy(); + return; + } + } + } + + _storyProgressSelectionMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(EvtMainFrame::storyProgressSelectionEvent), nullptr, this); +} + void EvtMainFrame::initialiseListView() { for(long i = 0; i < 32; i++) { _installedListView->InsertItem(i, wxString::Format("%.2i", i + 1)); @@ -627,6 +712,7 @@ void EvtMainFrame::updateCommandsState() { MassState mass_state = _massManager->massState(selection); _companyRenameButton->Enable(_unsafeMode == true || game_state == GameState::NotRunning); + _storyProgressChangeButton->Enable(_unsafeMode == true || game_state == GameState::NotRunning); _importButton->Enable(selection != -1 && staged_selection != -1 && (_unsafeMode == true || game_state == GameState::NotRunning)); _exportButton->Enable(selection != -1); diff --git a/GUI/EvtMainFrame.h b/GUI/EvtMainFrame.h index fb0106d..0cd96ef 100644 --- a/GUI/EvtMainFrame.h +++ b/GUI/EvtMainFrame.h @@ -45,6 +45,8 @@ class EvtMainFrame: public MainFrame { void profileSelectionEvent(wxCommandEvent&); void backupSelectedProfileEvent(wxCommandEvent&); void companyRenameEvent(wxMouseEvent&); + void storyProgressSelectionEvent(wxCommandEvent& event); + void openStoryProgressMenuEvent(wxCommandEvent&); // M.A.S.S.-related events void importMassEvent(wxCommandEvent&); @@ -72,6 +74,7 @@ class EvtMainFrame: public MainFrame { void stagingFileEventHandler(int event_type, const wxString& event_file, const wxFileSystemWatcherEvent& event); void updateProfileStats(); + void initStoryProgressMenu(); void initialiseListView(); void isGameRunning(); @@ -90,6 +93,8 @@ class EvtMainFrame: public MainFrame { ProfileManager _profileManager; Containers::Pointer _massManager; + Containers::Pointer _storyProgressSelectionMenu; + wxFileSystemWatcher _watcher; int _lastWatcherEventType = 0; }; diff --git a/GUI/MainFrame.cpp b/GUI/MainFrame.cpp index 2d105c6..e940e65 100644 --- a/GUI/MainFrame.cpp +++ b/GUI/MainFrame.cpp @@ -114,6 +114,10 @@ MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& title, co _companyRenameButton = new wxButton( sbSizerGeneralInfo->GetStaticBox(), wxID_ANY, wxT("Rename company"), wxDefaultPosition, wxDefaultSize, 0 ); bSizerProfileCommands->Add( _companyRenameButton, 0, wxALL, 5 ); + _storyProgressChangeButton = new wxButton( sbSizerGeneralInfo->GetStaticBox(), wxID_ANY, wxT("Change story progress"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerProfileCommands->Add( _storyProgressChangeButton, 0, wxALL, 5 ); + + sbSizerGeneralInfo->Add( bSizerProfileCommands, 0, wxEXPAND, 5 ); @@ -254,6 +258,7 @@ MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& title, co _openScreenshotDirButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openScreenshotDirEvent ), NULL, this ); _unsafeCheckbox->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainFrame::unsafeCheckboxEvent ), NULL, this ); _companyRenameButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::companyRenameEvent ), NULL, this ); + _storyProgressChangeButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openStoryProgressMenuEvent ), NULL, this ); _moveButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::moveMassEvent ), NULL, this ); _deleteButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::deleteMassEvent ), NULL, this ); _renameButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::renameMassEvent ), NULL, this ); @@ -274,6 +279,7 @@ MainFrame::~MainFrame() _openScreenshotDirButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openScreenshotDirEvent ), NULL, this ); _unsafeCheckbox->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainFrame::unsafeCheckboxEvent ), NULL, this ); _companyRenameButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::companyRenameEvent ), NULL, this ); + _storyProgressChangeButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::openStoryProgressMenuEvent ), NULL, this ); _moveButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::moveMassEvent ), NULL, this ); _deleteButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::deleteMassEvent ), NULL, this ); _renameButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainFrame::renameMassEvent ), NULL, this ); diff --git a/GUI/MainFrame.fbp b/GUI/MainFrame.fbp index f0594ea..d95977e 100644 --- a/GUI/MainFrame.fbp +++ b/GUI/MainFrame.fbp @@ -1194,6 +1194,79 @@ companyRenameEvent + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Change story progress + + 0 + + 0 + + + 0 + + 1 + _storyProgressChangeButton + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + openStoryProgressMenuEvent + + diff --git a/GUI/MainFrame.h b/GUI/MainFrame.h index 7a9724e..04dbfe3 100644 --- a/GUI/MainFrame.h +++ b/GUI/MainFrame.h @@ -59,6 +59,7 @@ class MainFrame : public wxFrame wxStaticText* _lastMissionIdLabel; wxStaticText* _lastMissionId; wxButton* _companyRenameButton; + wxButton* _storyProgressChangeButton; wxPanel* _massPanel; wxListView* _installedListView; wxButton* _moveButton; @@ -83,6 +84,7 @@ class MainFrame : public wxFrame virtual void openScreenshotDirEvent( wxCommandEvent& event ) { event.Skip(); } virtual void unsafeCheckboxEvent( wxCommandEvent& event ) { event.Skip(); } virtual void companyRenameEvent( wxCommandEvent& event ) { event.Skip(); } + virtual void openStoryProgressMenuEvent( wxCommandEvent& event ) { event.Skip(); } virtual void moveMassEvent( wxCommandEvent& event ) { event.Skip(); } virtual void deleteMassEvent( wxCommandEvent& event ) { event.Skip(); } virtual void renameMassEvent( wxCommandEvent& event ) { event.Skip(); } diff --git a/Profile/Profile.cpp b/Profile/Profile.cpp index 2b9413e..31fc5cf 100644 --- a/Profile/Profile.cpp +++ b/Profile/Profile.cpp @@ -211,6 +211,21 @@ auto Profile::getStoryProgress() -> std::int32_t { return _storyProgress; } +auto Profile::setStoryProgress(std::int32_t progress) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &story_progress_locator[0], &story_progress_locator[29]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x27) = progress; + return true; + } + else{ + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + auto Profile::lastMissionId() const -> std::int32_t { return _lastMissionId; } diff --git a/Profile/Profile.h b/Profile/Profile.h index a5547c8..fa98a5d 100644 --- a/Profile/Profile.h +++ b/Profile/Profile.h @@ -36,6 +36,7 @@ class Profile { auto storyProgress() const -> std::int32_t; auto getStoryProgress() -> std::int32_t; + auto setStoryProgress(std::int32_t progress) -> bool; auto lastMissionId() const -> std::int32_t; auto getLastMissionId() -> std::int32_t;