diff options
Diffstat (limited to 'gameui/SaveGameBrowserDialog.cpp')
| -rw-r--r-- | gameui/SaveGameBrowserDialog.cpp | 1423 |
1 files changed, 1423 insertions, 0 deletions
diff --git a/gameui/SaveGameBrowserDialog.cpp b/gameui/SaveGameBrowserDialog.cpp new file mode 100644 index 0000000..ac894ff --- /dev/null +++ b/gameui/SaveGameBrowserDialog.cpp @@ -0,0 +1,1423 @@ +//========= Copyright 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 "SaveGameBrowserDialog.h" + +extern const char *COM_GetModDirectory( void ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGameSavePanel::CGameSavePanel( CSaveGameBrowserDialog *parent, SaveGameDescription_t *pSaveDesc, bool bCommandPanel ) +: BaseClass( parent, "SaveGamePanel" ) +{ + // Store our save description internally for reference later by our parent + m_SaveInfo = (*pSaveDesc); + m_bNewSavePanel = bCommandPanel; + + // Setup our main graphical elements + m_pLevelPicBorder = SETUP_PANEL( new ImagePanel( this, "LevelPicBorder" ) ); + m_pLevelPic = SETUP_PANEL( new ImagePanel( this, "LevelPic" ) ); + + // Setup our various labels + m_pChapterTitle = new Label( this, "ChapterLabel", m_SaveInfo.szComment ); + m_pTime = new Label( this, "TimeLabel", m_SaveInfo.szFileTime ); + m_pElapsedTime = new Label( this, "ElapsedLabel", m_SaveInfo.szElapsedTime ); + m_pType = new Label( this, "TypeLabel", m_SaveInfo.szType ); + + // Make sure we have a chapter description + char *pchChapterName = Q_stristr( m_SaveInfo.szComment, "chapter" ); + if ( pchChapterName ) + { + char szChapterImage[ 256 ]; + Q_snprintf( szChapterImage, sizeof(szChapterImage), "chapters/%s", Q_strlower( pchChapterName ) ); + char *ext = Q_strrchr( szChapterImage, '_' ); + if ( ext ) + { + *ext = '\0'; + } + m_pLevelPic->SetImage( szChapterImage ); + } + else + { + m_pLevelPic->SetImage( "ui_logo" ); + } + + // Setup our basic settings + KeyValues *pKeys = NULL; + if ( GameUI().IsConsoleUI() ) + { + pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "SaveGamePanel.res" ); + } + LoadControlSettings( "Resource/SaveGamePanel.res", NULL, pKeys ); + + int px, py; + m_pLevelPicBorder->GetPos( px, py ); + SetSize( m_pLevelPicBorder->GetWide(), py + m_pLevelPicBorder->GetTall() + ( m_pType->GetTall() + 16 ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGameSavePanel::~CGameSavePanel( void ) +{ + if ( m_pLevelPicBorder ) + delete m_pLevelPicBorder; + + if ( m_pLevelPic ) + delete m_pLevelPic; + + if ( m_pChapterTitle ) + delete m_pChapterTitle; + + if ( m_pTime ) + delete m_pTime; + + if ( m_pElapsedTime ) + delete m_pElapsedTime; + + if ( m_pType ) + delete m_pType; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameSavePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + m_TextColor = pScheme->GetColor( "NewGame.TextColor", Color(255, 255, 255, 255) ); + m_FillColor = pScheme->GetColor( "NewGame.FillColor", Color(255, 255, 255, 255) ); + m_DisabledColor = pScheme->GetColor( "NewGame.DisabledColor", Color(255, 255, 255, 4) ); + m_SelectedColor = pScheme->GetColor( "NewGame.SelectionColor", Color(255, 255, 255, 255) ); + + // Turn various labels off if we're the "stubbed" panel + if ( m_bNewSavePanel ) + { + m_pTime->SetVisible( false ); + m_pElapsedTime->SetVisible( false ); + m_pType->SetVisible( false ); + } + + // Setup our initial state + m_pChapterTitle->SetFgColor( m_TextColor ); + m_pTime->SetFgColor( m_TextColor ); + m_pElapsedTime->SetFgColor( m_TextColor ); + + m_pLevelPic->SetFillColor( Color( 0, 0, 0, 255 ) ); + m_pLevelPicBorder->SetFillColor( Color( 0, 0, 0, 255 ) ); + + if ( m_bNewSavePanel ) + { + float flScaleAmount = m_pLevelPic->GetScaleAmount(); + if ( flScaleAmount <= 0.0f ) + flScaleAmount = 1.0f; + + // TBD: Draw the game logo here! + int picWide = 64.0f * flScaleAmount; + int picTall = 64.0f * flScaleAmount; + int borderWide = m_pLevelPicBorder->GetWide(); + int borderTall = m_pLevelPicBorder->GetTall(); + int borderX, borderY; + m_pLevelPicBorder->GetPos( borderX, borderY ); + m_pLevelPic->SetPos( borderX + ( ( borderWide - picWide ) / 2 ), borderY + ( ( borderTall - picTall ) / 2 ) ); + m_pLevelPic->SetFillColor( Color( 0, 0, 0, 0 ) ); + } + + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: Overwrite the level description +// Input : *pDesc - Description to use +//----------------------------------------------------------------------------- +void CGameSavePanel::SetDescription( SaveGameDescription_t *pDesc ) +{ + // Store our save description internally for reference later by our parent + m_SaveInfo = (*pDesc); + + // Setup our main graphical elements + m_pChapterTitle->SetText( m_SaveInfo.szComment ); + m_pTime->SetText( m_SaveInfo.szFileTime ); + m_pElapsedTime->SetText( m_SaveInfo.szElapsedTime ); + m_pType->SetText( m_SaveInfo.szType ); + + // Make sure we have a chapter description + char *pchChapterName = Q_stristr( m_SaveInfo.szComment, "chapter" ); + if ( pchChapterName ) + { + char szChapterImage[ 256 ]; + Q_snprintf( szChapterImage, sizeof(szChapterImage), "chapters/%s", Q_strlower( pchChapterName ) ); + char *ext = Q_strrchr( szChapterImage, '_' ); + if ( ext ) + { + *ext = '\0'; + } + m_pLevelPic->SetImage( szChapterImage ); + } +} + +// +// +// +// +// + +//----------------------------------------------------------------------------- +// Purpose: new game chapter selection +//----------------------------------------------------------------------------- +CSaveGameBrowserDialog::CSaveGameBrowserDialog( vgui::Panel *parent ) +: BaseClass( parent, "SaveGameDialog" ), + m_bFilterAutosaves( false ), + m_iSelectedSave( -1 ), + m_bScrolling( false ), + m_ScrollCt( 0 ), + m_ScrollSpeed( 0.0f ), + m_ButtonPressed( SCROLL_NONE ), + m_ScrollDirection( SCROLL_NONE ), + m_nDeletedPanel( INVALID_INDEX ), + m_nAddedPanel( INVALID_INDEX ), + m_nUsedStorageSpace( 0 ), + m_bControlDisabled( false ) +{ + // Setup basic attributes + SetDeleteSelfOnClose( true ); + SetSizeable( false ); + + // Create the backer that highlights the currently selected save + m_pCenterBg = SETUP_PANEL( new Panel( this, "CenterBG" ) ); + m_pCenterBg->SetPaintBackgroundType( 2 ); + m_pCenterBg->SetVisible( true ); + + // Create our button footer + m_pFooter = new CFooterPanel( parent, "SaveGameFooter" ); + + // Load our res files from the keyvalue we're holding + KeyValues *pKeys = NULL; + if ( GameUI().IsConsoleUI() ) + { + pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "SaveGameDialog.res" ); + } + + LoadControlSettings( "Resource/SaveGameDialog.res", NULL, pKeys ); +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +CSaveGameBrowserDialog::~CSaveGameBrowserDialog( void ) +{ + // Release all elements + m_SavePanels.PurgeAndDeleteElements(); + + // Kill the footer + if ( m_pFooter ) + { + delete m_pFooter; + m_pFooter = NULL; + } + + if ( m_pCenterBg ) + { + delete m_pCenterBg; + m_pCenterBg = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Show the "No save games to display" indication label and hide all browsing UI +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ShowNoSaveGameUI( void ) +{ + // Show the "no save games" text + vgui::Label *pNoSavesLabel = dynamic_cast<vgui::Label *>(FindChildByName( "NoSavesLabel" )); + if ( pNoSavesLabel ) + { + if ( m_bSaveGameIsCorrupt ) + { + pNoSavesLabel->SetText("#GameUI_SaveGame_CorruptFile"); + } + else + { + pNoSavesLabel->SetText("#GameUI_NoSaveGamesToDisplay"); + } + pNoSavesLabel->SetVisible( true ); + } + + if ( m_pCenterBg ) + m_pCenterBg->SetVisible( false ); + + vgui::Panel *pLeftArrow = FindChildByName( "LeftArrow" ); + if ( pLeftArrow ) + pLeftArrow->SetVisible( false ); + + vgui::Panel *pRightArrow = FindChildByName( "RightArrow" ); + if ( pRightArrow ) + pRightArrow->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Hide all "No save games" UI +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::HideNoSaveGameUI( void ) +{ + // Show the "no save games" text + vgui::Panel *pNoSavesLabel = FindChildByName( "NoSavesLabel" ); + if ( pNoSavesLabel ) + pNoSavesLabel->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::LayoutPanels( void ) +{ + // Setup our panels depending on the mode we're in + if ( HasActivePanels() ) + { + // Hide any indicators about no save games + HideNoSaveGameUI(); + + // Layout panel positions relative to the dialog center. + int panelWidth = m_SavePanels[0]->GetWide() + 16; + int dialogWidth = GetWide(); + m_PanelXPos[2] = ( dialogWidth - panelWidth ) / 2 + 8; + m_PanelXPos[1] = m_PanelXPos[2] - panelWidth; + m_PanelXPos[0] = m_PanelXPos[1]; + m_PanelXPos[3] = m_PanelXPos[2] + panelWidth; + m_PanelXPos[4] = m_PanelXPos[3]; + + m_PanelAlpha[0] = 0; + m_PanelAlpha[1] = 64; + m_PanelAlpha[2] = 255; + m_PanelAlpha[3] = 64; + m_PanelAlpha[4] = 0; + + int panelHeight; + m_SavePanels[0]->GetSize( panelWidth, panelHeight ); + m_pCenterBg->SetVisible( true ); + m_pCenterBg->SetWide( panelWidth + 16 ); + m_pCenterBg->SetPos( m_PanelXPos[2] - 8, m_PanelYPos[2] - (panelHeight - m_nCenterBgTallDefault) + 8 ); + m_pCenterBg->SetBgColor( Color( 190, 115, 0, 255 ) ); + } + else + { + // Hide anything to do with browsing the saves + ShowNoSaveGameUI(); + + } + + // Do internal cleanup to make sure we present a correct state to the user + UpdateMenuComponents( SCROLL_NONE ); + UpdateFooterOptions(); +} + +//----------------------------------------------------------------------------- +// Purpose: Do a fancy slide-out when we're first displayed +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::AnimateDialogStart( void ) +{ + const float flAnimInTime = 0.5f; + const float flOffset = 0.1f; + + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + if ( m_PanelIndex[i] == INVALID_INDEX ) + continue; + + // Start us at the "opening" position + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[i] ]; + if ( panel ) + { + panel->SetPos( m_PanelXPos[0], m_PanelYPos[0] ); + panel->SetAlpha( m_PanelAlpha[0] ); + panel->SetVisible( true ); + panel->SetEnabled( true ); + panel->SetZPos( NUM_SLOTS - i ); + } + + // Now make them slide out where they're going + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[i], 0, flAnimInTime + (flOffset*i), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[i], 0, flAnimInTime + (flOffset*i), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[i], 0, flAnimInTime + (flOffset*i), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + // Move and fade the back label + m_pCenterBg->SetAlpha( 0 ); + int nX, nY; + m_pCenterBg->GetPos( nX, nY ); + m_pCenterBg->SetPos( nX-m_pCenterBg->GetWide(), nY ); + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "xpos", nX, 0, flAnimInTime + (flOffset*2), vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 255, 0, (flAnimInTime+ (flOffset*2))*2.0f, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + CGameSavePanel *selectedPanel = GetActivePanel(); + if ( selectedPanel && selectedPanel->IsAutoSaveType() ) + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault + 20 ); + } + else + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Do our initial layout +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::Activate( void ) +{ + // Start scanning for saved games + ScanSavedGames( m_bFilterAutosaves ); + + // Finish our layout depending on what the result of the scan was + LayoutPanels(); + + // Animate the opening animation + AnimateDialogStart(); + + BaseClass::Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Apply special properties of the menu +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + int ypos = inResourceData->GetInt( "chapterypos", 20 ); + for ( int i = 0; i < NUM_SLOTS; ++i ) + { + m_PanelYPos[i] = ypos; + } + + m_nCenterBgTallDefault = inResourceData->GetInt( "centerbgtall", 0 ); + m_pCenterBg->SetTall( m_nCenterBgTallDefault ); + + m_ScrollSpeedSlow = inResourceData->GetFloat( "scrollslow", 0.0f ); + m_ScrollSpeedFast = inResourceData->GetFloat( "scrollfast", 0.0f ); + SetFastScroll( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Apply scheme settings +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + UpdateMenuComponents( SCROLL_NONE ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the correct properties for visible components +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::UpdateMenuComponents( EScrollDirection dir ) +{ + // This is called prior to any scrolling, so we need to look ahead to the post-scroll state + int centerIdx = SLOT_CENTER; + + // Scroll given our direction of travel + if ( dir == SCROLL_LEFT ) + { + ++centerIdx; + } + else if ( dir == SCROLL_RIGHT ) + { + --centerIdx; + } + + int leftIdx = centerIdx - 1; + int rightIdx = centerIdx + 1; + + // Update the state of the side arrows depending on whether or not we can scroll that direction + vgui::Panel *leftArrow = this->FindChildByName( "LeftArrow" ); + vgui::Panel *rightArrow = this->FindChildByName( "RightArrow" ); + if ( leftArrow ) + { + leftArrow->SetVisible( true ); + if ( m_PanelIndex[leftIdx] != INVALID_INDEX ) + { + leftArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); + } + else + { + leftArrow->SetFgColor( Color( 128, 128, 128, 64 ) ); + } + } + if ( rightArrow ) + { + rightArrow->SetVisible( true ); + if ( m_PanelIndex[rightIdx] != INVALID_INDEX ) + { + rightArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); + } + else + { + rightArrow->SetFgColor( Color( 128, 128, 128, 64 ) ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets a chapter as selected +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::SetSelectedSaveIndex( int index ) +{ + m_iSelectedSave = index; + + // If we have no panels, there's nothing to update + if ( HasActivePanels() == false ) + return; + + // Setup panels to the left of the selected panel + int currIdx = index; + for ( int i = SLOT_CENTER; i >= 0 && currIdx >= 0; --i ) + { + m_PanelIndex[i] = currIdx; + --currIdx; + InitPanelIndexForDisplay( i ); + } + + // Setup panels to the right of the selected panel + currIdx = index + 1; + for ( int i = SLOT_CENTER + 1; i < NUM_SLOTS && currIdx < m_SavePanels.Count(); ++i ) + { + m_PanelIndex[i] = currIdx; + ++currIdx; + InitPanelIndexForDisplay( i ); + } + + UpdateMenuComponents( SCROLL_NONE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove the currently selected animation from the list with proper animations +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::RemoveActivePanel( void ) +{ + // Kill the current panel + m_nDeletedPanel = m_PanelIndex[SLOT_CENTER]; + + // Start our current panel fading + CGameSavePanel *pPanel = m_SavePanels[ m_nDeletedPanel ]; + GetAnimationController()->RunAnimationCommand( pPanel, "alpha", 0, 0, m_ScrollSpeedFast, vgui::AnimationController::INTERPOLATOR_ACCEL ); + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 0, 0, m_ScrollSpeedFast, vgui::AnimationController::INTERPOLATOR_ACCEL ); + PostMessage( this, new KeyValues( "FinishDelete" ), m_ScrollSpeed ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::CloseAfterSave( void ) +{ + OnCommand( "CloseAndSelectResume" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::FinishInsert( void ) +{ + CGameSavePanel *panel = m_SavePanels[ m_nAddedPanel ]; + + const float flScrollSpeed = 0.75f; + + // Run the actual movement + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[SLOT_RIGHT], 0, flScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[SLOT_RIGHT], 0, flScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", 255, 0, flScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + PostMessage( this, new KeyValues( "CloseAfterSave" ), flScrollSpeed*2.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert a new panel at the desired location +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::AnimateInsertNewPanel( const SaveGameDescription_t *pDesc ) +{ + // This is the panel that's going to move + CGameSavePanel *pNewPanel = SETUP_PANEL( new CGameSavePanel( this, (SaveGameDescription_t *) pDesc ) ); + pNewPanel->SetVisible( false ); + + // Tack this onto the list + m_nAddedPanel = m_SavePanels.InsertAfter( 0, pNewPanel ); + + // Set it up but turn it off immediately + pNewPanel->SetPos( m_PanelXPos[SLOT_CENTER], m_PanelYPos[SLOT_CENTER] ); + pNewPanel->SetVisible( true ); + pNewPanel->SetEnabled( true ); + pNewPanel->SetZPos( 0 ); + pNewPanel->SetAlpha( 0.0f ); + + // Increment our indices to reflect the change + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + if ( m_PanelIndex[i] == INVALID_INDEX ) + continue; + + if ( m_PanelIndex[i] > 0 ) + { + m_PanelIndex[i]++; + } + } + + // Fade the right panel away + if ( IsValidPanel( m_PanelIndex[ SLOT_RIGHT ] ) ) + { + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[ SLOT_RIGHT ] ]; + + // Run the actual movement + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[SLOT_OFFRIGHT], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[SLOT_OFFRIGHT], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[SLOT_OFFRIGHT], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + PostMessage( this, new KeyValues( "FinishInsert" ), m_ScrollSpeed ); + } + else + { + PostMessage( this, new KeyValues( "FinishInsert" ), 0.1f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Pop in the new description +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::FinishOverwriteFadeDown( void ) +{ + const float flFadeInTime = 0.25f; + + // Fade the right panel away + CGameSavePanel *pActivePanel = GetActivePanel(); + if ( pActivePanel ) + { + pActivePanel->SetDescription( &m_NewSaveGameDesc ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( pActivePanel, "alpha", m_PanelAlpha[SLOT_CENTER], 0, flFadeInTime, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 255, 0, flFadeInTime, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + PostMessage( this, new KeyValues( "CloseAfterSave" ), flFadeInTime + 0.1f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Animate an overwrite event by fading out the old panel and bringing it back with a new description +// Input : *pNewDesc - The new description to display +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::AnimateOverwriteActivePanel( const SaveGameDescription_t *pNewDesc ) +{ + // Save a copy of this description + m_NewSaveGameDesc = (*pNewDesc); + + // Fade the right panel away + CGameSavePanel *pActivePanel = GetActivePanel(); + if ( pActivePanel ) + { + // Panel alpha + GetAnimationController()->RunAnimationCommand( pActivePanel, "alpha", 0, 0, 0.5f, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 0, 0, 0.5f, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + PostMessage( this, new KeyValues( "FinishOverwriteFadeDown" ), 0.75f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called before a panel scroll starts. +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PreScroll( EScrollDirection dir ) +{ + int hideIdx = INVALID_INDEX; + if ( m_nDeletedPanel != INVALID_INDEX ) + { + hideIdx = m_nDeletedPanel; + } + else if ( dir == SCROLL_LEFT ) + { + hideIdx = m_PanelIndex[SLOT_LEFT]; + } + else if ( dir == SCROLL_RIGHT ) + { + hideIdx = m_PanelIndex[SLOT_RIGHT]; + } + + if ( hideIdx != INVALID_INDEX ) + { + // Push back the panel that's about to be hidden + // so the next panel scrolls over the top of it. + m_SavePanels[hideIdx]->SetZPos( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called after a panel scroll finishes. +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PostScroll( EScrollDirection dir ) +{ + // FIXME: Nothing to do here... +} + +//----------------------------------------------------------------------------- +// Purpose: Initiates a panel scroll and starts the animation. +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ScrollSelectionPanels( EScrollDirection dir ) +{ + // Only initiate a scroll if panels aren't currently scrolling + if ( !m_bScrolling ) + { + // Handle any pre-scroll setup + PreScroll( dir ); + + if ( dir == SCROLL_LEFT) + { + m_ScrollCt += SCROLL_LEFT; + } + else if ( dir == SCROLL_RIGHT && m_PanelIndex[SLOT_CENTER] != 0 ) + { + m_ScrollCt += SCROLL_RIGHT; + } + + m_bScrolling = true; + AnimateSelectionPanels(); + + // Update the arrow colors, help text, and buttons. Doing it here looks better than having + // the components change after the entire scroll animation has finished. + UpdateMenuComponents( m_ScrollDirection ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Do all slide animation work here +// Input : nPanelIndex - Panel we're currently operating on +// nNextPanelIndex - Panel we're going to be moving over +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PerformSlideAction( int nPanelIndex, int nNextPanelIndex ) +{ + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[ nPanelIndex ] ]; + + // Run the actual movement + GetAnimationController()->RunAnimationCommand( panel, "xpos", m_PanelXPos[nNextPanelIndex], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + GetAnimationController()->RunAnimationCommand( panel, "ypos", m_PanelYPos[nNextPanelIndex], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Panel alpha + GetAnimationController()->RunAnimationCommand( panel, "alpha", m_PanelAlpha[nNextPanelIndex], 0, m_ScrollSpeed, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Initiates the scripted scroll and fade effects of all five slotted panels +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::AnimateSelectionPanels( void ) +{ + int idxOffset = 0; + int startIdx = SLOT_LEFT; + int endIdx = SLOT_RIGHT; + + // Don't scroll outside the bounds of the panel list + if ( m_ScrollCt >= SCROLL_LEFT && m_PanelIndex[SLOT_CENTER] < m_SavePanels.Count() - 1 ) + { + if ( m_nDeletedPanel != INVALID_INDEX ) + { + startIdx = SLOT_RIGHT; + } + + idxOffset = -1; + endIdx = SLOT_OFFRIGHT; + m_ScrollDirection = SCROLL_LEFT; + } + else if ( m_ScrollCt <= SCROLL_RIGHT && m_PanelIndex[SLOT_CENTER] > 0 ) + { + idxOffset = 1; + startIdx = SLOT_OFFLEFT; + m_ScrollDirection = SCROLL_RIGHT; + } + + if ( 0 == idxOffset ) + { + // Kill the scroll, it's outside the bounds + m_ScrollCt = 0; + m_bScrolling = false; + m_ScrollDirection = SCROLL_NONE; + vgui::surface()->PlaySound( "player/suit_denydevice.wav" ); + return; + } + + // Should never happen + if ( startIdx > endIdx ) + return; + + for ( int i = startIdx; i <= endIdx; ++i ) + { + // Don't animate the special panel, just skip it + if ( m_PanelIndex[i] == m_nDeletedPanel ) + continue; + + int nNextIdx = i+idxOffset; + if ( m_PanelIndex[i] != INVALID_INDEX ) + { + PerformSlideAction( i, nNextIdx ); + } + } + + vgui::surface()->PlaySound( "UI/buttonclick.wav" ); + + // Animate the center background panel + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 0, 0, m_ScrollSpeed * 0.25f, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + + // Scrolling up through chapters, offset is negative + m_iSelectedSave -= idxOffset; + + UpdateFooterOptions(); + + PostMessage( this, new KeyValues( "FinishScroll" ), m_ScrollSpeed ); +} + +//----------------------------------------------------------------------------- +// Purpose: After a scroll, each panel slot holds the index of a panel that has +// scrolled to an adjacent slot. This function updates each slot so +// it holds the index of the panel that is actually in that slot's position. +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ShiftPanelIndices( int offset ) +{ + // Shift all the elements over one slot, then calculate what the last slot's index should be. + int lastSlot = NUM_SLOTS - 1; + + // Handle the deletion case + if ( m_nDeletedPanel != INVALID_INDEX ) + { + // Scroll panels in from the right + Q_memmove( &m_PanelIndex[SLOT_CENTER], &m_PanelIndex[SLOT_RIGHT], 2* sizeof( m_PanelIndex[SLOT_CENTER] ) ); + + if ( m_PanelIndex[lastSlot] != INVALID_INDEX ) + { + int num = m_PanelIndex[ lastSlot ] + 1; + if ( IsValidPanel( num ) ) + { + m_PanelIndex[lastSlot] = num; + InitPanelIndexForDisplay( lastSlot ); + } + else + { + m_PanelIndex[lastSlot] = INVALID_INDEX; + } + } + } + else if ( offset > 0 ) + { + // Hide the panel that's dropping out of the slots + if ( IsValidPanel( m_PanelIndex[0] ) ) + { + m_SavePanels[ m_PanelIndex[0] ]->SetVisible( false ); + } + + // Scrolled panels to the right, so shift the indices one slot to the left + Q_memmove( &m_PanelIndex[0], &m_PanelIndex[1], lastSlot * sizeof( m_PanelIndex[0] ) ); + if ( m_PanelIndex[lastSlot] != INVALID_INDEX ) + { + int num = m_PanelIndex[ lastSlot ] + 1; + if ( IsValidPanel( num ) ) + { + m_PanelIndex[lastSlot] = num; + InitPanelIndexForDisplay( lastSlot ); + } + else + { + m_PanelIndex[lastSlot] = INVALID_INDEX; + } + } + } + else + { + // Hide the panel that's dropping out of the slots + if ( IsValidPanel( m_PanelIndex[lastSlot] ) ) + { + m_SavePanels[ m_PanelIndex[lastSlot] ]->SetVisible( false ); + } + + // Scrolled panels to the left, so shift the indices one slot to the right + Q_memmove( &m_PanelIndex[1], &m_PanelIndex[0], lastSlot * sizeof( m_PanelIndex[0] ) ); + if ( m_PanelIndex[0] != INVALID_INDEX ) + { + int num = m_PanelIndex[0] - 1; + if ( IsValidPanel( num ) ) + { + m_PanelIndex[0] = num; + InitPanelIndexForDisplay( 0 ); + } + else + { + m_PanelIndex[0] = INVALID_INDEX; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Validates an index into the selection panels vector +//----------------------------------------------------------------------------- +bool CSaveGameBrowserDialog::IsValidPanel( const int idx ) +{ + if ( idx < 0 || idx >= m_SavePanels.Count() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets up a panel's properties before it is displayed +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::InitPanelIndexForDisplay( const int idx ) +{ + CGameSavePanel *panel = m_SavePanels[ m_PanelIndex[idx] ]; + if ( panel ) + { + panel->SetPos( m_PanelXPos[idx], m_PanelYPos[idx] ); + panel->SetAlpha( m_PanelAlpha[idx] ); + panel->SetVisible( true ); + panel->SetEnabled( true ); + if ( m_PanelAlpha[idx] ) + { + panel->SetZPos( NUM_SLOTS ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets which scroll speed should be used +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::SetFastScroll( bool fast ) +{ + m_ScrollSpeed = fast ? m_ScrollSpeedFast : m_ScrollSpeedSlow; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks if a button is being held down, and speeds up the scroll +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ContinueScrolling( void ) +{ + if ( !GameUI().IsConsoleUI() ) + { + if ( m_PanelIndex[SLOT_CENTER-1] % 3 ) + { + ScrollSelectionPanels( m_ScrollDirection ); + } + return; + } + + if ( m_ButtonPressed == m_ScrollDirection ) + { + SetFastScroll( true ); + ScrollSelectionPanels( m_ScrollDirection ); + } + else if ( m_ButtonPressed != SCROLL_NONE ) + { + // The other direction has been pressed - start a slow scroll + SetFastScroll( false ); + ScrollSelectionPanels( (EScrollDirection)m_ButtonPressed ); + } + else + { + SetFastScroll( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Fade animation has finished, now slide or be done +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::FinishDelete( void ) +{ + // Catch the case where all saves are now gone! + if ( m_SavePanels.Count() == 1 ) + { + m_nDeletedPanel = INVALID_INDEX; + m_SavePanels.PurgeAndDeleteElements(); + + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + m_PanelIndex[i] = INVALID_INDEX; + } + + LayoutPanels(); + return; + } + + EScrollDirection nDirection = ( IsValidPanel( m_nDeletedPanel + 1 ) ) ? SCROLL_LEFT : SCROLL_RIGHT; + ScrollSelectionPanels( nDirection ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a scroll distance of one slot has been completed +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::FinishScroll( void ) +{ + // Fade the center bg panel back in + GetAnimationController()->RunAnimationCommand( m_pCenterBg, "alpha", 255, 0, m_ScrollSpeed * 0.25f, vgui::AnimationController::INTERPOLATOR_LINEAR ); + + ShiftPanelIndices( m_ScrollDirection ); + m_bScrolling = false; + m_ScrollCt = 0; + + // End of scroll step + PostScroll( m_ScrollDirection ); + + if ( m_nDeletedPanel != INVALID_INDEX ) + { + // Find where we're going next + int newSave = m_nDeletedPanel; + if ( m_SavePanels.IsValidIndex( m_nDeletedPanel + 1 ) == false ) + { + newSave = m_nDeletedPanel - 1; + } + + // Remove it from our list + CGameSavePanel *pPanel = m_SavePanels[ m_nDeletedPanel ]; + m_SavePanels.Remove( m_nDeletedPanel ); + delete pPanel; + + // Decrement all the indices to reflect the change + for ( int i = 0; i < NUM_SLOTS; i++ ) + { + if ( m_PanelIndex[i] > m_nDeletedPanel ) + m_PanelIndex[i]--; + } + + // Clear the spot and be done with it + SetSelectedSaveIndex( newSave ); + m_nDeletedPanel = INVALID_INDEX; + UpdateMenuComponents( SCROLL_NONE ); + } + + // Size the "autosave" blade if need-be + CGameSavePanel *selectedPanel = GetActivePanel(); + if ( selectedPanel && selectedPanel->IsAutoSaveType() ) + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault + 20 ); + } + else + { + m_pCenterBg->SetTall( m_nCenterBgTallDefault ); + } + + // Continue scrolling if necessary + ContinueScrolling(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnClose( void ) +{ + SetControlDisabled( true ); + + m_KeyRepeat.Reset(); + BasePanel()->RunCloseAnimation( "CloseNewGameDialog_OpenMainMenu" ); + + BaseClass::OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: Our save games have changed, so layout our panel again +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::RefreshSaveGames( void ) +{ + // Close any pending messages + BasePanel()->CloseMessageDialog( DIALOG_STACK_IDX_WARNING ); + + // Don't leave us in a locked state + SetControlDisabled( false ); + + // Re-scan the saved games + ScanSavedGames( m_bFilterAutosaves ); + + // Re-layout the panels + LayoutPanels(); + + // Run our animation again + AnimateDialogStart(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PerformSelectedAction( void ) +{ + // By default, do nothing + m_KeyRepeat.Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PerformDeletion( void ) +{ + // By default, do nothing + m_KeyRepeat.Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: Release our key repeater +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnKeyCodeReleased( vgui::KeyCode code ) +{ + m_KeyRepeat.KeyUp( code ); + + BaseClass::OnKeyCodeReleased( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: Update our keypress repeater +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnThink( void ) +{ + vgui::KeyCode code = m_KeyRepeat.KeyRepeated(); + if ( code ) + { + OnKeyCodePressed( code ); + } + + BaseClass::OnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + // If the console has UI up, then ignore it + if ( BasePanel()->IsWaitingForConsoleUI() ) + return; + + // Inhibit key activity during transitions + if ( GetAlpha() != 255 || m_bControlDisabled ) + return; + + m_KeyRepeat.KeyDown( code ); + + switch( code ) + { + case KEY_XBUTTON_A: + case STEAMCONTROLLER_A: + PerformSelectedAction(); + break; + + case KEY_XBUTTON_B: + case STEAMCONTROLLER_B: + OnClose(); + break; + + case KEY_XBUTTON_X: + case STEAMCONTROLLER_X: + PerformDeletion(); + break; + + case KEY_XBUTTON_Y: + case STEAMCONTROLLER_Y: + BasePanel()->OnChangeStorageDevice(); + break; + + // Move the selection up and down + case KEY_XSTICK1_LEFT: + case KEY_XBUTTON_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + ScrollSelectionPanels( SCROLL_RIGHT ); + break; + + case KEY_XSTICK1_RIGHT: + case KEY_XBUTTON_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + ScrollSelectionPanels( SCROLL_LEFT ); + break; + + default: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::PaintBackground( void ) +{ + int wide, tall; + GetSize( wide, tall ); + + Color col = GetBgColor(); + DrawBox( 0, 0, wide, tall, col, 1.0f ); + + int y = 32; + + // draw an inset + Color darkColor; + darkColor.SetColor( 0.70f * (float)col.r(), 0.70f * (float)col.g(), 0.70f * (float)col.b(), col.a() ); + vgui::surface()->DrawSetColor( darkColor ); + vgui::surface()->DrawFilledRect( 8, y, wide - 8, tall - 8 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Parses the save game info out of the .sav file header +//----------------------------------------------------------------------------- +bool CSaveGameBrowserDialog::ParseSaveData( char const *pszFileName, char const *pszShortName, SaveGameDescription_t *save ) +{ + char szMapName[SAVEGAME_MAPNAME_LEN]; + char szComment[SAVEGAME_COMMENT_LEN]; + char szElapsedTime[SAVEGAME_ELAPSED_LEN]; + + if ( !pszFileName || !pszShortName ) + return false; + + Q_strncpy( save->szShortName, pszShortName, sizeof(save->szShortName) ); + Q_strncpy( save->szFileName, pszFileName, sizeof(save->szFileName) ); + + FileHandle_t fh = g_pFullFileSystem->Open( pszFileName, "rb", "MOD" ); + if (fh == FILESYSTEM_INVALID_HANDLE) + return false; + + save->iSize = g_pFullFileSystem->Size( fh ); + + int readok = SaveReadNameAndComment( fh, szMapName, sizeof(szMapName), szComment, sizeof(szComment) ); + g_pFullFileSystem->Close(fh); + + if ( !readok ) + { + return false; + } + + Q_strncpy( save->szMapName, szMapName, sizeof(save->szMapName) ); + + // Elapsed time is the last 6 characters in comment. (mmm:ss) + int i; + i = strlen( szComment ); + Q_strncpy( szElapsedTime, "??", sizeof( szElapsedTime ) ); + if (i >= 6) + { + Q_strncpy( szElapsedTime, (char *)&szComment[i - 6], 7 ); + szElapsedTime[6] = '\0'; + + // parse out + int minutes = atoi( szElapsedTime ); + int seconds = atoi( szElapsedTime + 4); + int hours = minutes / 60; + minutes %= 60; + + wchar_t wzHours[6]; + wchar_t wzMins[4]; + wchar_t wzSecs[4]; + + _snwprintf( wzHours, ARRAYSIZE(wzHours), L"%d", hours ); + _snwprintf( wzMins, ARRAYSIZE(wzMins), L"%d", minutes ); + _snwprintf( wzSecs, ARRAYSIZE(wzSecs), L"%d", seconds ); + + wchar_t buf[20]; + + // reformat + if ( hours ) + { + g_pVGuiLocalize->ConstructString( buf, sizeof( buf ), g_pVGuiLocalize->Find( "#GameUI_LoadDialog_Hr_Min" ), 2, wzHours, wzMins ); + } + else if ( minutes ) + { + g_pVGuiLocalize->ConstructString( buf, sizeof( buf ), g_pVGuiLocalize->Find( "#GameUI_LoadDialog_Min_Sec" ), 2, wzMins, wzSecs ); + } + else + { + g_pVGuiLocalize->ConstructString( buf, sizeof( buf ), g_pVGuiLocalize->Find( "#GameUI_LoadDialog_Sec" ), 1, wzSecs ); + } + + g_pVGuiLocalize->ConvertUnicodeToANSI( buf, szElapsedTime, sizeof(szElapsedTime) ); + + // Chop elapsed out of comment. + char *pChop = Q_stristr( szComment, " " ); + if ( pChop != NULL ) + { + (*pChop) = '\0'; + } + } + + // calculate the file name to print + const char *pszType = ""; + if (strstr(pszFileName, "quick")) + { + pszType = "#GameUI_QuickSave"; + } + else if (strstr(pszFileName, "autosave")) + { + pszType = "#GameUI_AutoSave"; + } + + Q_strncpy( save->szType, pszType, sizeof(save->szType) ); + Q_strncpy( save->szComment, szComment, sizeof(save->szComment) ); + Q_strncpy( save->szElapsedTime, szElapsedTime, sizeof(save->szElapsedTime) ); + + // Now get file time stamp. + long fileTime = g_pFullFileSystem->GetFileTime(pszFileName); + char szFileTime[32]; + g_pFullFileSystem->FileTimeToString(szFileTime, sizeof(szFileTime), fileTime); + char *newline = strstr(szFileTime, "\n"); + if (newline) + { + *newline = 0; + } + Q_strncpy( save->szFileTime, szFileTime, sizeof(save->szFileTime) ); + save->iTimestamp = fileTime; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Update our footer options depending on what we've selected +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::UpdateFooterOptions( void ) +{ + // Do nothing +} + +//----------------------------------------------------------------------------- +// Purpose: Sort our games by time +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::SortSaveGames( SaveGameDescription_t *pSaves, unsigned int nNumSaves ) +{ + qsort( pSaves, nNumSaves, sizeof(SaveGameDescription_t), &CBaseSaveGameDialog::SaveGameSortFunc ); +} + +//----------------------------------------------------------------------------- +// Purpose: builds save game list from directory +//----------------------------------------------------------------------------- +void CSaveGameBrowserDialog::ScanSavedGames( bool bIgnoreAutosave ) +{ + // Start with a clean slate + m_nUsedStorageSpace = 0; + m_bSaveGameIsCorrupt = false; + + // Clear all known panels I'm holding now + m_SavePanels.PurgeAndDeleteElements(); + + // Reset all indices + for ( int i = 0; i < NUM_SLOTS; ++i ) + { + m_PanelIndex[i] = INVALID_INDEX; + } + + // Clear our list + CUtlVector<SaveGameDescription_t> saveGames; + + // Get the search path + char szDirectory[_MAX_PATH]; + + if ( IsX360() ) + Q_snprintf( szDirectory, sizeof( szDirectory ), "%s:/*", COM_GetModDirectory() ); + else + Q_snprintf( szDirectory, sizeof( szDirectory ), "save/*" ); + + Q_DefaultExtension( szDirectory, IsX360() ? ".360.sav" : ".sav", sizeof( szDirectory ) ); + Q_FixSlashes( szDirectory ); + + // iterate the saved files + FileFindHandle_t handle; + const char *pFileName = g_pFullFileSystem->FindFirst( szDirectory, &handle ); + while (pFileName) + { + if ( !Q_strnicmp(pFileName, "HLSave", strlen( "HLSave" ) ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + char szFileName[_MAX_PATH]; + + if ( IsX360() ) + Q_snprintf(szFileName, sizeof( szFileName ), "%s:/%s", COM_GetModDirectory(), pFileName ); + else + Q_snprintf(szFileName, sizeof( szFileName ), "save/%s", pFileName); + + Q_FixSlashes( szFileName ); + + // Only load save games from the current mod's save dir + if( !g_pFullFileSystem->FileExists( szFileName, "MOD" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + SaveGameDescription_t save; + if ( ParseSaveData( szFileName, pFileName, &save ) ) + { + // Add on this file's size to the count + m_nUsedStorageSpace += save.iSize; + + // Always ignore autosave dangerous (they're not considered safe until committed) + if ( Q_stristr( save.szShortName, "dangerous" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + + // If we're ignoring autosaves, skip it here + if ( bIgnoreAutosave ) + { + if ( !Q_stricmp( save.szType, "#GameUI_Autosave" ) ) + { + pFileName = g_pFullFileSystem->FindNext( handle ); + continue; + } + } + + saveGames.AddToTail( save ); + } + + pFileName = g_pFullFileSystem->FindNext( handle ); + } + + g_pFullFileSystem->FindClose( handle ); + + // Sort the save list + SortSaveGames( saveGames.Base(), saveGames.Count() ); + + // Now add them in order + for ( int i = 0; i < saveGames.Count(); i++ ) + { + CGameSavePanel *savePanel = SETUP_PANEL( new CGameSavePanel( this, &saveGames[i] ) ); + + savePanel->SetVisible( false ); + m_SavePanels.AddToTail( savePanel ); + } + + // Notify derived classes that save games are done being scanned + OnDoneScanningSaveGames(); + + // Always start with the first panel (as we're sorted in a specific order) + SetSelectedSaveIndex( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the currently selected panel +//----------------------------------------------------------------------------- +CGameSavePanel *CSaveGameBrowserDialog::GetActivePanel( void ) +{ + if ( IsValidPanel( m_iSelectedSave ) == false ) + return NULL; + + return m_SavePanels[ m_iSelectedSave ]; +} |