diff --git a/stepmania/src/GameConstantsAndTypes.cpp b/stepmania/src/GameConstantsAndTypes.cpp index 9ebe490d62..aa6b71f25c 100644 --- a/stepmania/src/GameConstantsAndTypes.cpp +++ b/stepmania/src/GameConstantsAndTypes.cpp @@ -122,8 +122,10 @@ XToThemedString( HoldNoteScore, NUM_HOLD_NOTE_SCORES ); static const CString MemoryCardStateNames[NUM_MEMORY_CARD_STATES] = { "ready", + "checking", "late", "error", + "removed", "none", }; XToString( MemoryCardState ); diff --git a/stepmania/src/GameConstantsAndTypes.h b/stepmania/src/GameConstantsAndTypes.h index 1925fc080b..ea46e9adab 100644 --- a/stepmania/src/GameConstantsAndTypes.h +++ b/stepmania/src/GameConstantsAndTypes.h @@ -173,8 +173,10 @@ enum ProfileSlot enum MemoryCardState { MEMORY_CARD_STATE_READY, + MEMORY_CARD_STATE_CHECKING, MEMORY_CARD_STATE_TOO_LATE, MEMORY_CARD_STATE_WRITE_ERROR, + MEMORY_CARD_STATE_REMOVED, MEMORY_CARD_STATE_NO_CARD, NUM_MEMORY_CARD_STATES, MEMORY_CARD_STATE_INVALID, diff --git a/stepmania/src/GameState.cpp b/stepmania/src/GameState.cpp index 1f545b7175..d1d1bcae16 100644 --- a/stepmania/src/GameState.cpp +++ b/stepmania/src/GameState.cpp @@ -109,19 +109,14 @@ void GameState::Reset() { EndGame(); - /* Don't do the OS mount for cards during the attract screens. Only do OS mounts in - * the time between BeginGame and PlayersFinalized. I think we do that anyway (except - * for background write tests, which happen anyway unless the memory card thread is - * paused). Can we remove this? */ - MEMCARDMAN->LockCards(); - ASSERT( THEME ); m_timeGameStarted.SetZero(); m_pCurStyle = NULL; FOREACH_PlayerNumber( p ) m_bSideIsJoined[p] = false; - m_bPlayersFinalized = false; + MEMCARDMAN->SetPlayersFinalized( false ); + MEMCARDMAN->UnlockCards(); // m_iCoins = 0; // don't reset coin count! m_MasterPlayerNumber = PLAYER_INVALID; m_mapEnv.clear(); @@ -161,6 +156,11 @@ void GameState::Reset() ResetMusicStatistics(); ResetStageStatistics(); + + FOREACH_PlayerNumber( pn ) + if( PROFILEMAN->ProfileWasLoadedFromMemoryCard(pn) ) + PROFILEMAN->UnloadProfile( pn ); + SONGMAN->FreeAllLoadedFromProfiles(); SONGMAN->UpdateBest(); SONGMAN->UpdateShuffled(); @@ -230,10 +230,10 @@ void GameState::BeginGame() void GameState::PlayersFinalized() { - if( m_bPlayersFinalized ) + if( MEMCARDMAN->GetPlayersFinalized() ) return; - m_bPlayersFinalized = true; + MEMCARDMAN->SetPlayersFinalized( true ); MEMCARDMAN->LockCards(); diff --git a/stepmania/src/GameState.h b/stepmania/src/GameState.h index 9889d1ab72..e0efde4f0f 100644 --- a/stepmania/src/GameState.h +++ b/stepmania/src/GameState.h @@ -47,7 +47,6 @@ public: const Game* m_pCurGame; const Style* m_pCurStyle; bool m_bSideIsJoined[NUM_PLAYERS]; // left side, right side - bool m_bPlayersFinalized; PlayMode m_PlayMode; // many screens display different info depending on this value int m_iCoins; // not "credits" PlayerNumber m_MasterPlayerNumber; // used in Styles where one player controls both sides diff --git a/stepmania/src/MemoryCardManager.cpp b/stepmania/src/MemoryCardManager.cpp index a737149154..e591c1dbc1 100644 --- a/stepmania/src/MemoryCardManager.cpp +++ b/stepmania/src/MemoryCardManager.cpp @@ -11,7 +11,6 @@ #include "ScreenManager.h" #include "ProfileManager.h" #include "Foreach.h" -#include "GameState.h" #include "RageUtil_WorkerThread.h" MemoryCardManager* MEMCARDMAN = NULL; // global and accessable from anywhere in our program @@ -241,10 +240,12 @@ MemoryCardManager::MemoryCardManager() m_bCardsLocked = false; FOREACH_PlayerNumber( p ) { - m_bTooLate[p] = false; m_bMounted[p] = false; + m_State[p] = MEMORY_CARD_STATE_NO_CARD; } + m_bPlayersFinalized = false; + /* These can play at any time. Preload them, so we don't cause a skip in gameplay. */ m_soundReady.Load( THEME->GetPathS("MemoryCardManager","ready"), true ); m_soundError.Load( THEME->GetPathS("MemoryCardManager","error"), true ); @@ -269,7 +270,7 @@ void MemoryCardManager::Update( float fDelta ) const vector vOld = m_vStorageDevices; // copy if( !g_pWorker->StorageDevicesChanged( m_vStorageDevices ) ) return; - const vector &vNew = m_vStorageDevices; +/* const vector &vNew = m_vStorageDevices; vector vConnects; // fill these in below vector vDisconnects; // fill these in below @@ -305,35 +306,9 @@ void MemoryCardManager::Update( float fDelta ) vector::iterator iter = find( vDisconnects.begin(), vDisconnects.end(), assigned_device ); if( iter != vDisconnects.end() ) - { - UnmountCard( p ); - assigned_device.MakeBlank(); - m_soundDisconnect.Play(); - - if( PROFILEMAN->ProfileWasLoadedFromMemoryCard(p) ) - PROFILEMAN->UnloadProfile( p ); - } } - - // Update the status of already-assigned cards. It may contain updated info - // like WriteTest results and a new sName. - FOREACH_PlayerNumber( p ) - { - UsbStorageDevice &assigned_device = m_Device[p]; - if( assigned_device.IsBlank() ) // no card assigned to this player - continue; - - vector::iterator iter = find( m_vStorageDevices.begin(), m_vStorageDevices.end(), assigned_device ); - if( iter != m_vStorageDevices.end() ) - { - // play write test error sound if write test failed since we last checked - if( assigned_device.bNeedsWriteTest && !iter->bNeedsWriteTest && !iter->bWriteTestSucceeded ) - m_soundError.Play(); - assigned_device = *iter; - } - } - +*/ // make a list of unassigned vector vUnassignedDevices = m_vStorageDevices; // copy @@ -354,18 +329,32 @@ void MemoryCardManager::Update( float fDelta ) } } - // try to assign each device to a player + // Try to assign each device to a player. If a player already has a device + // assigned, and the device still exists, keep him on the same card. FOREACH_PlayerNumber( p ) { - LOG->Trace( "Looking for a card for Player %d", p+1 ); - - UsbStorageDevice &assigned_device = m_Device[p]; - if( !assigned_device.IsBlank() ) // they already have an assigned card + UsbStorageDevice &assigned_device = m_Device[p]; + if( !assigned_device.IsBlank() ) { - LOG->Trace( "Player %d already has a card: '%s'", p+1, assigned_device.sOsMountDir.c_str() ); - continue; // skip + /* The player has a card assigned. If it's been removed, clear it. */ + vector::iterator it = find( m_vStorageDevices.begin(), m_vStorageDevices.end(), assigned_device ); + if( it != m_vStorageDevices.end() ) + { + /* The player has a card, and it's still plugged in. Update any changed + * state, such as m_State. */ + LOG->Trace( "Player %d already has a card: '%s'", p+1, assigned_device.sOsMountDir.c_str() ); + assigned_device = *it; + continue; + } + + /* The assigned card has been removed; clear it and re-search. */ + LOG->Trace( "Player %i: disconnected bus %d port %d device %d path %s", + p+1, assigned_device.iBus, assigned_device.iPort, assigned_device.iLevel, assigned_device.sOsMountDir.c_str() ); + assigned_device.MakeBlank(); } - + + LOG->Trace( "Looking for a card for Player %d", p+1 ); + FOREACH( UsbStorageDevice, vUnassignedDevices, d ) { // search for card dir match @@ -386,39 +375,106 @@ void MemoryCardManager::Update( float fDelta ) PREFSMAN->m_iMemoryCardUsbLevel[p] != d->iLevel ) continue; // not a match - LOG->Trace( "device match: iScsiIndex: %d, iBus: %d, iLevel: %d, iPort: %d, sOsMountDir: %s", - d->iScsiIndex, d->iBus, d->iLevel, d->iPort, d->sOsMountDir.c_str() ); - + LOG->Trace( "Player %i: device match: iScsiIndex: %d, iBus: %d, iLevel: %d, iPort: %d, sOsMountDir: %s", + p+1, d->iScsiIndex, d->iBus, d->iLevel, d->iPort, d->sOsMountDir.c_str() ); + assigned_device = *d; // save a copy vUnassignedDevices.erase( d ); // remove the device so we don't match it for another player - m_bTooLate[p] = GAMESTATE->m_bPlayersFinalized; // the device is too late if inserted when cards were locked - - // play sound - if( m_bTooLate[p] ) - m_soundTooLate.Play(); - else if( !d->bNeedsWriteTest && !d->bWriteTestSucceeded ) - m_soundError.Play(); - else - m_soundReady.Play(); - break; } } - - SCREENMAN->RefreshCreditsMessages(); + + CheckStateChanges(); } -MemoryCardState MemoryCardManager::GetCardState( PlayerNumber pn ) +void MemoryCardManager::CheckStateChanges() { - UsbStorageDevice &d = m_Device[pn]; - if( d.IsBlank() ) - return MEMORY_CARD_STATE_NO_CARD; - else if( m_bTooLate[pn] ) - return MEMORY_CARD_STATE_TOO_LATE; - else if( !d.bNeedsWriteTest && !d.bWriteTestSucceeded ) - return MEMORY_CARD_STATE_WRITE_ERROR; - else - return MEMORY_CARD_STATE_READY; + /* Deal with assignment changes. */ + FOREACH_PlayerNumber( p ) + { + UsbStorageDevice &new_device = m_Device[p]; + + MemoryCardState state = MEMORY_CARD_STATE_INVALID; + + if( m_bPlayersFinalized ) + { + if( m_FinalDevice[p].m_State == UsbStorageDevice::STATE_NONE ) + { + /* We didn't have a card when we finalized, so we won't accept anything. + * If anything is inserted (even if it's still checking), say TOO LATE. */ + if( new_device.m_State == UsbStorageDevice::STATE_NONE ) + state = MEMORY_CARD_STATE_NO_CARD; + else + state = MEMORY_CARD_STATE_TOO_LATE; + } + else + { + /* We had a card inserted when we finalized. */ + if( new_device.m_State == UsbStorageDevice::STATE_NONE ) + state = MEMORY_CARD_STATE_REMOVED; + if( new_device.m_State == UsbStorageDevice::STATE_READY ) + { + if( m_FinalDevice[p].sSerial != new_device.sSerial ) + { + /* A different card is inserted than we had when we finalized; + * ignore it. */ + state = MEMORY_CARD_STATE_REMOVED; + } + } + + /* Otherwise, the card is checking or has an error. Use the regular logic. */ + } + } + + if( state == MEMORY_CARD_STATE_INVALID ) + { + switch( new_device.m_State ) + { + case UsbStorageDevice::STATE_NONE: + state = MEMORY_CARD_STATE_NO_CARD; + break; + + case UsbStorageDevice::STATE_CHECKING: + state = MEMORY_CARD_STATE_CHECKING; + break; + + case UsbStorageDevice::STATE_WRITE_ERROR: + state = MEMORY_CARD_STATE_WRITE_ERROR; + break; + + case UsbStorageDevice::STATE_READY: + state = MEMORY_CARD_STATE_READY; + break; + } + } + + MemoryCardState LastState = m_State[p]; + if( m_State[p] != state ) + { + // play sound + switch( state ) + { + case MEMORY_CARD_STATE_NO_CARD: + case MEMORY_CARD_STATE_REMOVED: + if( LastState == MEMORY_CARD_STATE_READY ) + m_soundDisconnect.Play(); + break; + case MEMORY_CARD_STATE_READY: + m_soundReady.Play(); + break; + case MEMORY_CARD_STATE_TOO_LATE: + m_soundTooLate.Play(); + break; + case MEMORY_CARD_STATE_WRITE_ERROR: + m_soundError.Play(); + break; + } + + m_State[p] = state; + } + } + + SCREENMAN->RefreshCreditsMessages(); } void MemoryCardManager::LockCards() @@ -434,8 +490,8 @@ void MemoryCardManager::UnlockCards() m_bCardsLocked = false; // clear too late flag - FOREACH_PlayerNumber( p ) - m_bTooLate[p] = false; +// FOREACH_PlayerNumber( p ) +// m_State[p] = MEMORY_CARD_STATE_NO_CARD; g_pWorker->SetMountThreadState( ThreadedMemoryCardWorker::detect_and_mount ); } @@ -518,7 +574,7 @@ void MemoryCardManager::FlushAndReset() UsbStorageDevice &d = m_Device[p]; if( d.IsBlank() ) // no card assigned continue; // skip - if( (!d.bNeedsWriteTest && !d.bWriteTestSucceeded) || m_bTooLate[p] ) + if( d.m_State == UsbStorageDevice::STATE_WRITE_ERROR ) continue; // skip g_pWorker->Flush( &m_Device[p] ); } @@ -526,6 +582,58 @@ void MemoryCardManager::FlushAndReset() g_pWorker->Reset(); // forces cards to be re-detected } +void MemoryCardManager::SetPlayersFinalized( bool bOn ) +{ + m_bPlayersFinalized = bOn; + + if( !bOn ) + { + m_bPlayersFinalized = false; + + /* If a memory card was inserted too late last game, allow it now. */ + CheckStateChanges(); + return; + } + +// g_pWorker->SetTimeout( 10 ); +// ASSERT( g_pWorker->TimeoutEnabled() ); + + /* XXX: wait */ + + FOREACH_PlayerNumber( p ) + { + /* If the card in this player's slot is ready, then use it. */ + // XXX: share code with Update(): keep updating until timed out or + // STATE_READY or STATE_NONE + UsbStorageDevice &d = m_Device[p]; + + /* Set the final state. */ + switch( d.m_State ) + { + case UsbStorageDevice::STATE_READY: + m_State[p] = MEMORY_CARD_STATE_READY; + m_FinalDevice[p] = d; + break; + + case UsbStorageDevice::STATE_NONE: + m_State[p] = MEMORY_CARD_STATE_NO_CARD; + break; + + case UsbStorageDevice::STATE_CHECKING: + case UsbStorageDevice::STATE_WRITE_ERROR: + m_State[p] = MEMORY_CARD_STATE_WRITE_ERROR; + break; + + default: + FAIL_M( ssprintf("%i", d.m_State) ); + } + + /* If there is no card ready when we finalize, the final device must be blank. */ + if( m_State[p] != MEMORY_CARD_STATE_READY ) + m_FinalDevice[p] = UsbStorageDevice(); + } +} + bool MemoryCardManager::PathIsMemCard( CString sDir ) const { FOREACH_PlayerNumber( p ) diff --git a/stepmania/src/MemoryCardManager.h b/stepmania/src/MemoryCardManager.h index e89823e8ee..720b05ef38 100644 --- a/stepmania/src/MemoryCardManager.h +++ b/stepmania/src/MemoryCardManager.h @@ -17,7 +17,7 @@ public: void Update( float fDelta ); - MemoryCardState GetCardState( PlayerNumber pn ); + MemoryCardState GetCardState( PlayerNumber pn ) const { return m_State[pn]; } void LockCards(); // prevent removing or changing of memory cards void UnlockCards(); @@ -26,6 +26,10 @@ public: void FlushAndReset(); // force all files to be flushed to mounted memory cards + /* If bOn is true, and a card is still initializing, block until it's ready. */ + void SetPlayersFinalized( bool bOn ); + bool GetPlayersFinalized() const { return m_bPlayersFinalized; } + bool PathIsMemCard( CString sDir ) const; bool IsNameAvailable( PlayerNumber pn ) const; @@ -34,13 +38,20 @@ public: protected: void PauseMountingThread(); // call this before mouting, reading, or writing to memory card void UnPauseMountingThread(); // call this when done mouting, reading, or writing to memory card + void CheckStateChanges(); vector m_vStorageDevices; // all currently connected + bool m_bCardsLocked; - bool m_bTooLate[NUM_PLAYERS]; // card was inserted after lock bool m_bMounted[NUM_PLAYERS]; // card is currently mounted - UsbStorageDevice m_Device[NUM_PLAYERS]; // device in the memory card slot, NULL if none + + UsbStorageDevice m_Device[NUM_PLAYERS]; // device in the memory card slot, blank if none + UsbStorageDevice m_FinalDevice[NUM_PLAYERS]; // device in the memory card slot when we finalized, blank if none + + MemoryCardState m_State[NUM_PLAYERS]; + + bool m_bPlayersFinalized; RageSound m_soundReady; RageSound m_soundError; diff --git a/stepmania/src/ScreenSystemLayer.cpp b/stepmania/src/ScreenSystemLayer.cpp index bcb3d5e7b7..363dd223b9 100644 --- a/stepmania/src/ScreenSystemLayer.cpp +++ b/stepmania/src/ScreenSystemLayer.cpp @@ -18,6 +18,8 @@ #define CREDITS_CARD_TOO_LATE THEME->GetMetric ("ScreenSystemLayer","CreditsCardTooLate") #define CREDITS_CARD_NO_NAME THEME->GetMetric ("ScreenSystemLayer","CreditsCardNoName") #define CREDITS_CARD_READY THEME->GetMetric ("ScreenSystemLayer","CreditsCardReady") +#define CREDITS_CARD_CHECKING THEME->GetMetric ("ScreenSystemLayer","CreditsCardChecking") +#define CREDITS_CARD_REMOVED THEME->GetMetric ("ScreenSystemLayer","CreditsCardRemoved") #define CREDITS_FREE_PLAY THEME->GetMetric ("ScreenSystemLayer","CreditsFreePlay") #define CREDITS_CREDITS THEME->GetMetric ("ScreenSystemLayer","CreditsCredits") #define CREDITS_NOT_PRESENT THEME->GetMetric ("ScreenSystemLayer","CreditsNotPresent") @@ -123,7 +125,7 @@ void ScreenSystemLayer::RefreshCreditsMessages() bool bShowCreditsMessage; if( GAMESTATE->m_bIsOnSystemMenu ) bShowCreditsMessage = true; - else if( GAMESTATE->m_bPlayersFinalized ) + else if( MEMCARDMAN->GetPlayersFinalized() ) bShowCreditsMessage = !GAMESTATE->IsPlayerEnabled( p ); else bShowCreditsMessage = !GAMESTATE->m_bSideIsJoined[p]; @@ -154,6 +156,12 @@ void ScreenSystemLayer::RefreshCreditsMessages() case MEMORY_CARD_STATE_TOO_LATE: sCredits = CREDITS_CARD_TOO_LATE; break; + case MEMORY_CARD_STATE_CHECKING: + sCredits = CREDITS_CARD_CHECKING; + break; + case MEMORY_CARD_STATE_REMOVED: + sCredits = CREDITS_CARD_REMOVED; + break; case MEMORY_CARD_STATE_READY: if( PROFILEMAN->LastLoadWasFromLastGood(p) && pProfile ) sCredits = pProfile->GetDisplayName() + CREDITS_LOADED_FROM_LAST_GOOD_APPEND; @@ -170,7 +178,7 @@ void ScreenSystemLayer::RefreshCreditsMessages() sCredits = CREDITS_CARD_NO_NAME; break; default: - ASSERT(0); + FAIL_M( ssprintf("%i",mcs) ); } } else // bShowCreditsMessage