//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "basepanel.h" #include "savegamedialog.h" #include "winlite.h" // FILETIME #include "vgui/ILocalize.h" #include "vgui/ISurface.h" #include "vgui/ISystem.h" #include "vgui/IVGui.h" #include "vgui_controls/AnimationController.h" #include "vgui_controls/ImagePanel.h" #include "filesystem.h" #include "keyvalues.h" #include "modinfo.h" #include "engineinterface.h" #include "gameui_interface.h" #include "vstdlib/random.h" #include "basesavegamedialog.h" #include "savegamebrowserdialog.h" using namespace vgui; // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #include "vgui_controls/Frame.h" #include "utlvector.h" extern const char *COM_GetModDirectory(); using namespace vgui; CSaveGameDialogXbox::CSaveGameDialogXbox( vgui::Panel *parent ) : BaseClass( parent ), m_bGameSaving ( false ), m_bNewSaveAvailable( false ) { m_bFilterAutosaves = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CSaveGameDialogXbox::PerformSelectedAction( void ) { BaseClass::PerformSelectedAction(); // If there are no panels, don't allow this if ( GetNumPanels() == 0 ) return; SetControlDisabled( true ); // Decide if this is an overwrite or a new save game bool bNewSave = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; if ( bNewSave ) { OnCommand( "SaveGame" ); } else { BasePanel()->ShowMessageDialog( MD_SAVE_OVERWRITE, this ); } } //----------------------------------------------------------------------------- // Purpose: // Input : bNewSaveSelected - //----------------------------------------------------------------------------- void CSaveGameDialogXbox::UpdateFooterOptions( void ) { CFooterPanel *pFooter = GetFooterPanel(); // Show available buttons pFooter->ClearButtons(); bool bSavePanelsActive = ( GetNumPanels() != 0 ); if ( bSavePanelsActive ) { bool bNewSaveSelected = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; if ( bNewSaveSelected ) { pFooter->AddNewButtonLabel( "#GameUI_SaveGame_NewSave", "#GameUI_Icons_A_BUTTON" ); } else { pFooter->AddNewButtonLabel( "#GameUI_SaveGame_Overwrite", "#GameUI_Icons_A_BUTTON" ); } } // Always available pFooter->AddNewButtonLabel( "#GameUI_Close", "#GameUI_Icons_B_BUTTON" ); pFooter->AddNewButtonLabel( "#GameUI_Console_StorageChange", "#GameUI_Icons_Y_BUTTON" ); } //----------------------------------------------------------------------------- // Purpose: perfrom the save on a separate thread //----------------------------------------------------------------------------- class CAsyncCtxSaveGame : public CBaseModPanel::CAsyncJobContext { public: explicit CAsyncCtxSaveGame( CSaveGameDialogXbox *pDlg ); ~CAsyncCtxSaveGame() {} public: virtual void ExecuteAsync(); virtual void Completed(); public: char m_szFilename[MAX_PATH]; CSaveGameDialogXbox *m_pSaveGameDlg; }; CAsyncCtxSaveGame::CAsyncCtxSaveGame( CSaveGameDialogXbox *pDlg ) : CBaseModPanel::CAsyncJobContext( 3.0f ), // Storage device info for at least 3 seconds m_pSaveGameDlg( pDlg ) { NULL; } void CAsyncCtxSaveGame::ExecuteAsync() { // Sit and wait for the async save to finish for ( ; ; ) { if ( !engine->IsSaveInProgress() ) // Save operation is no longer in progress break; else ThreadSleep( 50 ); } } void CAsyncCtxSaveGame::Completed() { m_pSaveGameDlg->SaveCompleted( this ); } //----------------------------------------------------------------------------- // Purpose: kicks off the async save (called on the main thread) //----------------------------------------------------------------------------- void CSaveGameDialogXbox::InitiateSaving() { // Determine whether this is a new save or overwrite bool bNewSave = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; // Allocate the async context for saving CAsyncCtxSaveGame *pAsyncCtx = new CAsyncCtxSaveGame( this ); // If this is an overwrite then there was an overwrite warning displayed if ( !bNewSave ) BasePanel()->CloseMessageDialog( DIALOG_STACK_IDX_WARNING ); // Now display the saving warning BasePanel()->ShowMessageDialog( MD_SAVING_WARNING, this ); // Kick off saving char *szFilename = pAsyncCtx->m_szFilename; const int maxFilenameLen = sizeof( pAsyncCtx->m_szFilename ); char szCmd[MAX_PATH]; // See if this is the "new save game" slot if ( bNewSave ) { // dgoodenough - Stub out generation of a unique file name for now // PS3_BUILDFIX // FIXME - this will need a workover // @wge Same for OSX #if defined ( _PS3 ) || defined( _OSX ) || defined (LINUX) unsigned currentTime = 0; #else // Create a new save game (name is created from the current time, which should be pretty unique) FILETIME currentFileTime; GetSystemTimeAsFileTime( ¤tFileTime ); unsigned currentTime = currentFileTime.dwLowDateTime; #endif Q_snprintf( szFilename, maxFilenameLen, "%s_%u", COM_GetModDirectory(), currentTime ); Q_snprintf( szCmd, sizeof( szCmd ), "xsave %s", szFilename ); engine->ExecuteClientCmd( szCmd ); Q_strncat( szFilename, ".360.sav", maxFilenameLen ); } else { const SaveGameDescription_t *pDesc = GetActivePanelSaveDescription(); Q_strncpy( szFilename, pDesc->szShortName, maxFilenameLen ); Q_snprintf( szCmd, sizeof( szCmd ), "xsave %s", szFilename ); engine->ExecuteClientCmd( szCmd ); } // Enqueue waiting BasePanel()->ExecuteAsync( pAsyncCtx ); } //----------------------------------------------------------------------------- // Purpose: handles the end of async save (called on the main thread) //----------------------------------------------------------------------------- void CSaveGameDialogXbox::SaveCompleted( CAsyncCtxSaveGame *pCtx ) { char const *szFilename = pCtx->m_szFilename; // We should now be saved so get the new desciption back from the file char szDirectory[MAX_PATH]; Q_snprintf( szDirectory, sizeof( szDirectory ), "%s:/%s", COM_GetModDirectory(), szFilename ); ParseSaveData( szDirectory, szFilename, &m_NewSaveDesc ); // Close the progress dialog BasePanel()->CloseMessageDialog( DIALOG_STACK_IDX_WARNING ); bool bNewSave = ( GetActivePanelIndex() == 0 ) && m_bNewSaveAvailable; if ( bNewSave ) { AnimateInsertNewPanel( &m_NewSaveDesc ); } else { AnimateOverwriteActivePanel( &m_NewSaveDesc ); } m_bGameSaving = false; } //----------------------------------------------------------------------------- // Purpose: handles button commands //----------------------------------------------------------------------------- void CSaveGameDialogXbox::OnCommand( const char *command ) { if ( !Q_stricmp( command, "SaveGame" ) ) { if ( m_bGameSaving ) return; m_bGameSaving = true; SetControlDisabled( true ); // Initiate the saving operation InitiateSaving(); } else if ( !Q_stricmp( command, "SaveSuccess" ) ) { vgui::surface()->PlaySound( "UI/buttonclick.wav" ); GameUI().SetSavedThisMenuSession( true ); } else if ( !Q_stricmp( command, "CloseAndSelectResume" ) ) { BasePanel()->ArmFirstMenuItem(); OnCommand( "Close" ); } else if ( !Q_stricmp( command, "OverwriteGameCancelled" ) ) { SetControlDisabled( false ); } else if ( !Q_stricmp( command, "RefreshSaveGames" ) ) { RefreshSaveGames(); } else if ( !Q_stricmp( command, "ReleaseModalWindow" ) ) { vgui::surface()->RestrictPaintToSinglePanel( NULL ); } else if ( !m_bGameSaving ) { BaseClass::OnCommand(command); } } //----------------------------------------------------------------------------- // Purpose: On completion of scanning, prepend a utility slot on the stack //----------------------------------------------------------------------------- void CSaveGameDialogXbox::OnDoneScanningSaveGames( void ) { ConVarRef save_history_count("save_history_count" ); m_bNewSaveAvailable = false; // dgoodenough - limit this to _X360 for now. // PS3_BUILDFIX // FIXME - do we need something here on PS3? #ifdef _X360 #pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Slamming controller for xbox storage id to 0" ) if ( XBX_GetStorageDeviceId( 0 ) == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId( 0 ) == XBX_STORAGE_DECLINED ) return; // We only allow 10 save games minus the number of autosaves, autosavedangerous, and autosave0?'s at once if ( GetNumPanels() >= 10 - ( 2 + (unsigned)save_history_count.GetInt() ) ) return; if ( GetStorageSpaceUsed() + XBX_SAVEGAME_BYTES > XBX_PERSISTENT_BYTES_NEEDED ) return; m_bNewSaveAvailable = true; SaveGameDescription_t bogusDesc = { "#GameUI_SaveGame_NewSavedGame", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", "#GameUI_SaveGame_NewSave", 0, 0 }; CGameSavePanel *newSavePanel = SETUP_PANEL( new CGameSavePanel( this, &bogusDesc, true ) ); AddPanel( newSavePanel ); #endif // _GAMECONSOLE }