diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index f14cfac5fb..640c1c4521 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -343,6 +343,7 @@ set(pcsx2GuiHeaders gui/Panels/MemoryCardPanels.h gui/pxEventThread.h gui/RecentIsoList.h + gui/Saveslots.h ) # Warning: the declaration of the .h are mandatory in case of resources files. It will ensure the creation diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp index 7a9d28240c..78c73d0c14 100644 --- a/pcsx2/System/SysCoreThread.cpp +++ b/pcsx2/System/SysCoreThread.cpp @@ -236,6 +236,7 @@ void SysCoreThread::GameStartingInThread() sApp.PostAppMethod(&Pcsx2App::resetDebugger); ApplyLoadedPatches(PPT_ONCE_ON_LOAD); + UI_UpdateSysControls(); } bool SysCoreThread::StateCheckInThread() diff --git a/pcsx2/gui/AppSaveStates.h b/pcsx2/gui/AppSaveStates.h index 8fee5eb198..2925f39e2f 100644 --- a/pcsx2/gui/AppSaveStates.h +++ b/pcsx2/gui/AppSaveStates.h @@ -17,6 +17,7 @@ #include "App.h" #include "SaveState.h" +#include "Saveslots.h" // -------------------------------------------------------------------------------------- // SysExecEvent_SaveSinglePlugin @@ -60,15 +61,3 @@ extern void StateCopy_SaveToFile( const wxString& file ); extern void StateCopy_LoadFromFile( const wxString& file ); extern void StateCopy_SaveToSlot( uint num ); extern void StateCopy_LoadFromSlot( uint slot, bool isFromBackup = false ); - -extern void States_registerLoadBackupMenuItem( wxMenuItem* loadBackupMenuItem ); - -extern bool States_isSlotUsed(int num); -extern void States_DefrostCurrentSlotBackup(); -extern void States_DefrostCurrentSlot(); -extern void States_FreezeCurrentSlot(); -extern void States_CycleSlotForward(); -extern void States_CycleSlotBackward(); - -extern void States_SetCurrentSlot( int slot ); -extern int States_GetCurrentSlot(); diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index 66fb797bff..892373bfdf 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -28,6 +28,7 @@ #include "AppAccelerators.h" #include "svnrev.h" +#include "Saveslots.h" // ------------------------------------------------------------------------ wxMenu* MainEmuFrame::MakeStatesSubMenu( int baseid, int loadBackupId ) const @@ -36,18 +37,18 @@ wxMenu* MainEmuFrame::MakeStatesSubMenu( int baseid, int loadBackupId ) const for (int i = 0; i < 10; i++) { - mnuSubstates->Append( baseid+i+1, wxsFormat(_("Slot %d"), i) ); + // Will be changed once an iso is loaded. + mnuSubstates->Append(baseid + i + 1, wxsFormat(_("Slot %d"), i)); } - if( loadBackupId>=0 ) + + if (loadBackupId >= 0) { mnuSubstates->AppendSeparator(); - wxMenuItem* m = mnuSubstates->Append( loadBackupId, _("Backup") ); + wxMenuItem* m = mnuSubstates->Append(loadBackupId, _("Backup")); m->Enable( false ); - States_registerLoadBackupMenuItem( m ); } - //mnuSubstates->Append( baseid - 1, _("Other...") ); return mnuSubstates; } @@ -787,4 +788,3 @@ void PerPluginMenuInfo::OnLoaded() ); MyMenu.Enable( GetPluginMenuId_Settings(PluginId), true ); } - diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h index 07abbb17ac..3f8c47e490 100644 --- a/pcsx2/gui/MainFrame.h +++ b/pcsx2/gui/MainFrame.h @@ -137,6 +137,7 @@ public: void UpdateIsoSrcSelection(); void RemoveCdvdMenu(); void EnableMenuItem( int id, bool enable ); + void SetMenuItemLabel(int id, wxString str); void EnableCdvdPluginSubmenu(bool isEnable = true); bool Destroy(); diff --git a/pcsx2/gui/Saveslots.cpp b/pcsx2/gui/Saveslots.cpp index 3c86f369c6..57a11dc2f2 100644 --- a/pcsx2/gui/Saveslots.cpp +++ b/pcsx2/gui/Saveslots.cpp @@ -17,11 +17,13 @@ #include "App.h" #include "AppSaveStates.h" #include "ConsoleLogger.h" +#include "MainFrame.h" #include "Common.h" #include "GS.h" #include "Elfheader.h" +#include "Saveslots.h" // -------------------------------------------------------------------------------------- // Saveslot Section @@ -29,15 +31,7 @@ static int StatesC = 0; static const int StateSlotsCount = 10; -static wxMenuItem* g_loadBackupMenuItem =NULL; - -bool States_isSlotUsed(int num) -{ - if (ElfCRC == 0) - return false; - else - return wxFileExists( SaveStateBase::GetFilename( num ) ); -} +Saveslot saveslot_cache[10] = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}}; // FIXME : Use of the IsSavingOrLoading flag is mostly a hack until we implement a // complete thread to manage queuing savestate tasks, and zipping states to disk. --air @@ -49,90 +43,101 @@ public: wxString GetEventName() const { return L"ClearSavingLoadingFlag"; } virtual ~SysExecEvent_ClearSavingLoadingFlag() = default; - SysExecEvent_ClearSavingLoadingFlag() - { - } - - SysExecEvent_ClearSavingLoadingFlag* Clone() const { return new SysExecEvent_ClearSavingLoadingFlag(); } + SysExecEvent_ClearSavingLoadingFlag() { } + SysExecEvent_ClearSavingLoadingFlag *Clone() const { return new SysExecEvent_ClearSavingLoadingFlag(); } protected: void InvokeEvent() { IsSavingOrLoading = false; + UI_UpdateSysControls(); } }; -void Sstates_updateLoadBackupMenuItem( bool isBeforeSave = false); +void Sstates_updateLoadBackupMenuItem(bool isBeforeSave); void States_FreezeCurrentSlot() { // FIXME : Use of the IsSavingOrLoading flag is mostly a hack until we implement a // complete thread to manage queuing savestate tasks, and zipping states to disk. --air - if( !SysHasValidState() ) + if (!SysHasValidState()) { - Console.WriteLn( "Save state: Aborting (VM is not active)." ); + Console.WriteLn("Save state: Aborting (VM is not active)."); return; } - if( wxGetApp().HasPendingSaves() || IsSavingOrLoading.exchange(true) ) + if (wxGetApp().HasPendingSaves() || IsSavingOrLoading.exchange(true)) { - Console.WriteLn( "Load or save action is already pending." ); + Console.WriteLn("Load or save action is already pending."); return; } - Sstates_updateLoadBackupMenuItem( true ); + Sstates_updateLoadBackupMenuItem(true); - GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() ); - StateCopy_SaveToSlot( StatesC ); - - GetSysExecutorThread().PostIdleEvent( SysExecEvent_ClearSavingLoadingFlag() ); + GSchangeSaveState(StatesC, SaveStateBase::GetFilename(StatesC).ToUTF8()); + StateCopy_SaveToSlot(StatesC); + + // Hack: Update the saveslot saying it's filled *right now* because it's still writing the file and we don't have a timestamp. + saveslot_cache[StatesC].empty = false; + saveslot_cache[StatesC].updated = wxDateTime::Now(); + saveslot_cache[StatesC].crc = ElfCRC; + + GetSysExecutorThread().PostIdleEvent(SysExecEvent_ClearSavingLoadingFlag()); } -void _States_DefrostCurrentSlot( bool isFromBackup ) +void _States_DefrostCurrentSlot(bool isFromBackup) { - if( !SysHasValidState() ) + if (!SysHasValidState()) { - Console.WriteLn( "Load state: Aborting (VM is not active)." ); + Console.WriteLn("Load state: Aborting (VM is not active)."); return; } - if( IsSavingOrLoading.exchange(true) ) + if (IsSavingOrLoading.exchange(true)) { - Console.WriteLn( "Load or save action is already pending." ); + Console.WriteLn("Load or save action is already pending."); return; } - GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() ); - StateCopy_LoadFromSlot( StatesC, isFromBackup ); + GSchangeSaveState(StatesC, SaveStateBase::GetFilename(StatesC).ToUTF8()); + StateCopy_LoadFromSlot(StatesC, isFromBackup); - GetSysExecutorThread().PostIdleEvent( SysExecEvent_ClearSavingLoadingFlag() ); + GetSysExecutorThread().PostIdleEvent(SysExecEvent_ClearSavingLoadingFlag()); - Sstates_updateLoadBackupMenuItem(); + Sstates_updateLoadBackupMenuItem(false); } void States_DefrostCurrentSlot() { - _States_DefrostCurrentSlot( false ); + _States_DefrostCurrentSlot(false); } void States_DefrostCurrentSlotBackup() { - _States_DefrostCurrentSlot( true ); + _States_DefrostCurrentSlot(true); } - -void States_registerLoadBackupMenuItem( wxMenuItem* loadBackupMenuItem ) +// I'd keep an eye on this function, as it may still be problematic. +void Sstates_updateLoadBackupMenuItem(bool isBeforeSave) { - g_loadBackupMenuItem = loadBackupMenuItem; + wxString file = SaveStateBase::GetFilename(StatesC); + + if (!(isBeforeSave && g_Conf->EmuOptions.BackupSavestate)) + { + file = file + L".backup"; + } + + sMainFrame.EnableMenuItem(MenuId_State_LoadBackup, wxFileExists(file)); + sMainFrame.SetMenuItemLabel(MenuId_State_LoadBackup, wxsFormat(L"%s %d", _("Backup"), StatesC)); } static void OnSlotChanged() { - OSDlog( Color_StrongGreen, true, " > Selected savestate slot %d", StatesC ); + OSDlog(Color_StrongGreen, true, " > Selected savestate slot %d", StatesC); - if( GSchangeSaveState != NULL ) + if (GSchangeSaveState != NULL) GSchangeSaveState(StatesC, SaveStateBase::GetFilename(StatesC).utf8_str()); - Sstates_updateLoadBackupMenuItem(); + Sstates_updateLoadBackupMenuItem(false); } int States_GetCurrentSlot() @@ -140,33 +145,20 @@ int States_GetCurrentSlot() return StatesC; } -void Sstates_updateLoadBackupMenuItem( bool isBeforeSave ) +void States_SetCurrentSlot(int slot) { - if( !g_loadBackupMenuItem ) return; - - int slot = States_GetCurrentSlot(); - wxString file = SaveStateBase::GetFilename( slot ); - g_loadBackupMenuItem->Enable( wxFileExists( isBeforeSave && g_Conf->EmuOptions.BackupSavestate ? file : file + L".backup" ) ); - wxString label; - label.Printf(L"%s %d", _("Backup"), slot ); - g_loadBackupMenuItem->SetItemLabel( label ); -} - -void States_SetCurrentSlot( int slot ) -{ - StatesC = std::min( std::max( slot, 0 ), StateSlotsCount ); + StatesC = std::min(std::max(slot, 0), StateSlotsCount); OnSlotChanged(); } void States_CycleSlotForward() { - StatesC = (StatesC+1) % StateSlotsCount; + StatesC = (StatesC + 1) % StateSlotsCount; OnSlotChanged(); } void States_CycleSlotBackward() { - StatesC = (StatesC+StateSlotsCount-1) % StateSlotsCount; + StatesC = (StatesC + StateSlotsCount - 1) % StateSlotsCount; OnSlotChanged(); } - diff --git a/pcsx2/gui/Saveslots.h b/pcsx2/gui/Saveslots.h new file mode 100644 index 0000000000..3886d76c48 --- /dev/null +++ b/pcsx2/gui/Saveslots.h @@ -0,0 +1,100 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2018 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 PCSX2. + * If not, see . + */ + + +#pragma once + +#include "PS2Edefs.h" +#include "System.h" +#include "Elfheader.h" + +class Saveslot +{ +public: + u32 slot_num; + bool empty; + wxDateTime updated; + u32 crc; + + Saveslot() + { + slot_num = 0; + empty = true; + updated = wxInvalidDateTime; + crc = ElfCRC; + } + + Saveslot(int i) + { + slot_num = i; + empty = true; + updated = wxInvalidDateTime; + crc = ElfCRC; + } + + bool isUsed() + { + return wxFileExists(SaveStateBase::GetFilename(slot_num)); + } + + wxDateTime GetTimestamp() + { + if (!isUsed()) return wxInvalidDateTime; + + return wxDateTime(wxFileModificationTime(SaveStateBase::GetFilename(slot_num))); + } + + void UpdateCache() + { + empty = !isUsed(); + updated = GetTimestamp(); + crc = ElfCRC; + } + + wxString SlotName() + { + if (empty) return wxsFormat(_("Slot %d - Empty"), slot_num); + + if (updated != wxInvalidDateTime) return wxsFormat(_("Slot %d - %s %s"), slot_num, updated.FormatDate(), updated.FormatTime()); + + return wxsFormat(_("Slot %d - Unknown Time"), slot_num); + } + + void ConsoleDump() + { + Console.WriteLn("Slot %i information:", slot_num); + Console.WriteLn("Internal CRC = %i; Current CRC = %i.", crc, ElfCRC); + if (empty) + Console.WriteLn("Slot cache says it is empty."); + else + Console.WriteLn("Slot cache says it is used."); + + if (updated != wxInvalidDateTime) + Console.WriteLn(wxsFormat(_("Write time is %s %s."), updated.FormatDate(), updated.FormatTime())); + + if (isUsed()) + Console.WriteLn(wxsFormat(_("The disk has a file on it dated %s %s."), GetTimestamp().FormatDate(), GetTimestamp().FormatTime())); + } +}; + +extern Saveslot saveslot_cache[10]; +extern void States_DefrostCurrentSlotBackup(); +extern void States_DefrostCurrentSlot(); +extern void States_FreezeCurrentSlot(); +extern void States_CycleSlotForward(); +extern void States_CycleSlotBackward(); +extern void States_SetCurrentSlot(int slot); +extern int States_GetCurrentSlot(); +extern void Sstates_updateLoadBackupMenuItem(bool isBeforeSave); \ No newline at end of file diff --git a/pcsx2/gui/SysState.cpp b/pcsx2/gui/SysState.cpp index 11dceb9e23..caeec947ab 100644 --- a/pcsx2/gui/SysState.cpp +++ b/pcsx2/gui/SysState.cpp @@ -476,6 +476,7 @@ protected: void CleanupEvent() { + UI_UpdateSysControls(); } }; @@ -674,6 +675,7 @@ void StateCopy_SaveToSlot( uint num ) Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", WX_STR(file) ); StateCopy_SaveToFile( file ); + UI_UpdateSysControls(); } void StateCopy_LoadFromSlot( uint slot, bool isFromBackup ) @@ -690,4 +692,5 @@ void StateCopy_LoadFromSlot( uint slot, bool isFromBackup ) Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", WX_STR(file) ); StateCopy_LoadFromFile( file ); + UI_UpdateSysControls(); } diff --git a/pcsx2/gui/UpdateUI.cpp b/pcsx2/gui/UpdateUI.cpp index 5397e2a96b..01b439d6d4 100644 --- a/pcsx2/gui/UpdateUI.cpp +++ b/pcsx2/gui/UpdateUI.cpp @@ -19,84 +19,119 @@ // General Notes: // * It's very important that we re-discover menu items by ID every time we change them, -// because the modern era of configurable GUIs means that we can't be assured the IDs -// exist anymore. +// because the modern era of configurable GUIs means that we can't be assured the IDs +// exist anymore. // This is necessary because this stupid wxWidgets thing has implicit debug errors // in the FindItem call that asserts if the menu options are missing. This is bad // mojo for configurable/dynamic menus. >_< -void MainEmuFrame::EnableMenuItem( int id, bool enable ) +void MainEmuFrame::EnableMenuItem(int id, bool enable) { - if( wxMenuItem* item = m_menubar.FindItem(id) ) - item->Enable( enable ); + if (wxMenuItem *item = m_menubar.FindItem(id)) + item->Enable(enable); } -static void _SaveLoadStuff( bool enabled ) +void MainEmuFrame::SetMenuItemLabel(int id, wxString str) { - sMainFrame.EnableMenuItem( MenuId_Sys_LoadStates, enabled ); - sMainFrame.EnableMenuItem( MenuId_Sys_SaveStates, enabled ); + if (wxMenuItem *item = m_menubar.FindItem(id)) + item->SetItemLabel(str); +} + +static void _SaveLoadStuff(bool enabled) +{ + sMainFrame.EnableMenuItem(MenuId_Sys_LoadStates, enabled); + sMainFrame.EnableMenuItem(MenuId_Sys_SaveStates, enabled); + + for (int i = 0; i < 10; i++) + { + int load_menu_item = MenuId_State_Load01 + i + 1; + int save_menu_item = MenuId_State_Save01 + i + 1; + + // If the cache is out of sync with the actual files, we need to update things. First update, the cache'll be blank, and this will populate everything. + if (saveslot_cache[i].empty == saveslot_cache[i].isUsed()) + { + // If there is actually a file there, or the cache was for a different game, we force an update. + // If the cache says there's a saveslot for the current game that there isn't a file for, writing it is done in a different thread, + // so it might not be written yet. Which is why I cache to begin with. + if (saveslot_cache[i].isUsed() || (saveslot_cache[i].crc != ElfCRC)) + { + saveslot_cache[i].UpdateCache(); + } + } + + sMainFrame.EnableMenuItem(load_menu_item, !saveslot_cache[i].empty); + sMainFrame.SetMenuItemLabel(load_menu_item, saveslot_cache[i].SlotName()); + sMainFrame.SetMenuItemLabel(save_menu_item, saveslot_cache[i].SlotName()); + } + Sstates_updateLoadBackupMenuItem(false); } // Updates the enable/disable status of all System related controls: menus, toolbars, // etc. Typically called by SysEvtHandler whenever the message pump becomes idle. void UI_UpdateSysControls() { - if( wxGetApp().Rpc_TryInvokeAsync( &UI_UpdateSysControls ) ) return; + if (wxGetApp().Rpc_TryInvokeAsync(&UI_UpdateSysControls)) + return; - sApp.PostAction( CoreThreadStatusEvent( CoreThread_Indeterminate ) ); + sApp.PostAction(CoreThreadStatusEvent(CoreThread_Indeterminate)); - //_SaveLoadStuff( true ); - _SaveLoadStuff( SysHasValidState() ); + _SaveLoadStuff(SysHasValidState()); } void UI_DisableSysShutdown() { - if( wxGetApp().Rpc_TryInvokeAsync( &UI_DisableSysShutdown ) ) return; + if (wxGetApp().Rpc_TryInvokeAsync(&UI_DisableSysShutdown)) + return; - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false ); + sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, false); sMainFrame.EnableMenuItem(MenuId_IsoBrowse, !g_Conf->AskOnBoot); wxGetApp().GetRecentIsoManager().EnableItems(!g_Conf->AskOnBoot); } void UI_EnableSysShutdown() { - if( wxGetApp().Rpc_TryInvokeAsync( &UI_EnableSysShutdown ) ) return; + if (wxGetApp().Rpc_TryInvokeAsync(&UI_EnableSysShutdown)) + return; - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true ); + sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, true); } void UI_DisableSysActions() { - if( wxGetApp().Rpc_TryInvokeAsync( &UI_DisableSysActions ) ) return; + if (wxGetApp().Rpc_TryInvokeAsync(&UI_DisableSysActions)) + return; - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false ); - - _SaveLoadStuff( false ); + sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, false); + + _SaveLoadStuff(false); } void UI_EnableSysActions() { - if( wxGetApp().Rpc_TryInvokeAsync( &UI_EnableSysActions ) ) return; + if (wxGetApp().Rpc_TryInvokeAsync(&UI_EnableSysActions)) + return; - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true ); + sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, true); sMainFrame.EnableMenuItem(MenuId_IsoBrowse, true); wxGetApp().GetRecentIsoManager().EnableItems(true); - - _SaveLoadStuff( true ); + + _SaveLoadStuff(true); } void UI_DisableStateActions() { - if( wxGetApp().Rpc_TryInvokeAsync( &UI_DisableStateActions ) ) return; - _SaveLoadStuff( false ); + if (wxGetApp().Rpc_TryInvokeAsync(&UI_DisableStateActions)) + return; + + _SaveLoadStuff(false); } void UI_EnableStateActions() { - if( wxGetApp().Rpc_TryInvokeAsync( &UI_EnableStateActions ) ) return; - _SaveLoadStuff( true ); + if (wxGetApp().Rpc_TryInvokeAsync(&UI_EnableStateActions)) + return; + + _SaveLoadStuff(true); } - - diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj index 6318f431eb..3f79092097 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj @@ -427,6 +427,7 @@ + diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters index 0243efc87a..f781ab956b 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters @@ -1197,6 +1197,9 @@ AppHost\Include + + AppHost\Include + AppHost\Cheats