diff --git a/stepmania/src/Profile.cpp b/stepmania/src/Profile.cpp index 130424d4e0..c1981b527d 100644 --- a/stepmania/src/Profile.cpp +++ b/stepmania/src/Profile.cpp @@ -563,11 +563,8 @@ void Profile::IncrementCategoryPlayCount( StepsType st, RankingCategory rc ) XNode* X = xml.GetChild(#X); \ if( X==NULL ) LOG->Warn("Failed to read section " #X); \ else Load##X##FromNode(X); } -int g_iOnceCtr; -#define FOR_ONCE for(g_iOnceCtr=0;g_iOnceCtr<1;g_iOnceCtr++) - -bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) +Profile::LoadResult Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) { CHECKPOINT; @@ -581,7 +578,7 @@ bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) // Check for the existance of stats.xml CString fn = sDir + STATS_XML; if( !IsAFile(fn) ) - return true; // This is a common case and not a load error, so return success. + return failed_no_profile; // // Don't unreasonably large stats.xml files. @@ -592,7 +589,7 @@ bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) if( iBytes > MAX_PLAYER_STATS_XML_SIZE_BYTES ) { LOG->Warn( "The file '%s' is unreasonably large. It won't be loaded.", fn.c_str() ); - return false; // error + return failed_tampered; } } @@ -606,7 +603,7 @@ bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) if( !CryptManager::VerifyFileWithFile(sStatsXmlSigFile, sDontShareFile) ) { LOG->Warn( "The don't share check for '%s' failed. Data will be ignored.", sStatsXmlSigFile.c_str() ); - return false; // error + return failed_tampered; } LOG->Trace( "Done." ); @@ -615,7 +612,7 @@ bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) if( !CryptManager::VerifyFileWithFile(fn, sStatsXmlSigFile) ) { LOG->Warn( "The signature check for '%s' failed. Data will be ignored.", fn.c_str() ); - return false; // error + return failed_tampered; } LOG->Trace( "Done." ); } @@ -625,19 +622,19 @@ bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) if( !xml.LoadFromFile( fn ) ) { LOG->Warn( "Couldn't open file '%s' for reading.", fn.c_str() ); - return false; // error + return failed_tampered; } LOG->Trace( "Done." ); /* The placeholder stats.xml file has an tag. Don't load it, but don't * warn about it. */ if( xml.name == "html" ) - return true; // success + return failed_no_profile; if( xml.name != "Stats" ) { WARN_M( xml.name ); - return false; // error + return failed_tampered; } LOAD_NODE( GeneralData ); @@ -649,7 +646,7 @@ bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature ) LOAD_NODE( RecentSongScores ); LOAD_NODE( RecentCourseScores ); - return true; // success + return success; } bool Profile::SaveAllToDir( CString sDir, bool bSignData ) const @@ -1578,11 +1575,21 @@ XNode* Profile::SaveCoinDataCreateNode() const void Profile::BackupToDir( CString sFromDir, CString sToDir ) { + FileCopy( sFromDir+EDITABLE_INI, sToDir+EDITABLE_INI ); FileCopy( sFromDir+STATS_XML, sToDir+STATS_XML ); FileCopy( sFromDir+STATS_XML+SIGNATURE_APPEND, sToDir+STATS_XML+SIGNATURE_APPEND ); FileCopy( sFromDir+DONT_SHARE_SIG, sToDir+DONT_SHARE_SIG ); } +bool Profile::CreateNewProfile( CString sProfileDir, CString sName ) +{ + Profile pro; + pro.m_sDisplayName = sName; + bool bResult = pro.SaveAllToDir( sProfileDir, PREFSMAN->m_bSignProfileData ); + FlushDirCache(); + return bResult; +} + /* * (c) 2001-2004 Chris Danford * All rights reserved. diff --git a/stepmania/src/Profile.h b/stepmania/src/Profile.h index 0dc0300170..726d63022d 100644 --- a/stepmania/src/Profile.h +++ b/stepmania/src/Profile.h @@ -260,7 +260,8 @@ public: // // Loading and saving // - bool LoadAllFromDir( CString sDir, bool bRequireSignature ); // return false on a tamper or corruption error + enum LoadResult { success, failed_no_profile, failed_tampered }; + LoadResult LoadAllFromDir( CString sDir, bool bRequireSignature ); bool SaveAllToDir( CString sDir, bool bSignData ) const; void LoadEditableDataFromDir( CString sDir ); @@ -289,6 +290,7 @@ public: void SaveStatsWebPageToDir( CString sDir ) const; void SaveMachinePublicKeyToDir( CString sDir ) const; + static bool CreateNewProfile( CString sProfileDir, CString sName ); static void BackupToDir( CString sFromDir, CString sToDir ); private: diff --git a/stepmania/src/ProfileManager.cpp b/stepmania/src/ProfileManager.cpp index 604db30d3d..44030a3d27 100644 --- a/stepmania/src/ProfileManager.cpp +++ b/stepmania/src/ProfileManager.cpp @@ -28,7 +28,7 @@ ProfileManager* PROFILEMAN = NULL; // global and accessable from anywhere in our #define NEW_MEM_CARD_NAME "" #define USER_PROFILES_DIR "Data/LocalProfiles/" #define MACHINE_PROFILE_DIR "Data/MachineProfile/" - +const CString LAST_GOOD_DIR = "LastGood/"; ProfileManager::ProfileManager() { @@ -44,6 +44,7 @@ void ProfileManager::Init() { m_bWasLoadedFromMemoryCard[p] = false; m_bLastLoadWasTamperedOrCorrupt[p] = false; + m_bLastLoadWasFromLastGood[p] = false; } LoadMachineProfile(); @@ -77,44 +78,37 @@ bool ProfileManager::LoadProfile( PlayerNumber pn, CString sProfileDir, bool bIs ASSERT( !sProfileDir.empty() ); ASSERT( sProfileDir.Right(1) == "/" ); - if( bIsMemCard ) - MEMCARDMAN->PauseMountingThread(); m_sProfileDir[pn] = sProfileDir; m_bWasLoadedFromMemoryCard[pn] = bIsMemCard; - bool bSuccess = m_Profile[pn].LoadAllFromDir( m_sProfileDir[pn], PREFSMAN->m_bSignProfileData ); + m_bLastLoadWasFromLastGood[pn] = false; - m_bLastLoadWasTamperedOrCorrupt[pn] = !bSuccess; + // Try to load the original, non-backup data. + Profile::LoadResult lr = m_Profile[pn].LoadAllFromDir( m_sProfileDir[pn], PREFSMAN->m_bSignProfileData ); + + CString sBackupDir = m_sProfileDir[pn] + LAST_GOOD_DIR; - // Save a backup of the profile now that we've loaded it and know it's good. - // This should be reasonably fast because we're only saving Stats.xml and - // signatures - not all of the files in the Profile. - if( bSuccess ) + // Save a backup of the non-backup profile now that we've loaded it and know + // it's good. This should be reasonably fast because we're only saving Stats.xml + // and signatures - not all of the files in the Profile. + if( lr == Profile::success ) { - CString sBackupDir = m_sProfileDir[pn] + "LastGood/"; Profile::BackupToDir( m_sProfileDir[pn], sBackupDir ); } - if( bIsMemCard ) - MEMCARDMAN->UnPauseMountingThread(); + m_bLastLoadWasTamperedOrCorrupt[pn] = lr == Profile::failed_tampered; - LOG->Trace( "Done loading profile." ); + // Try to load from the backup if the original data fails to load + // + if( lr == Profile::failed_tampered ) + { + lr = m_Profile[pn].LoadAllFromDir( sBackupDir, PREFSMAN->m_bSignProfileData ); + m_bLastLoadWasFromLastGood[pn] = lr == Profile::success; + } - return true; -} + LOG->Trace( "Done loading profile - result %d", lr ); -bool ProfileManager::CreateProfile( CString sProfileDir, CString sName ) -{ - bool bResult; - - Profile pro; - pro.m_sDisplayName = sName; - bResult = pro.SaveAllToDir( sProfileDir, PREFSMAN->m_bSignProfileData ); - if( !bResult ) - return false; - - FlushDirCache(); - return true; + return lr == Profile::success; } bool ProfileManager::LoadLocalProfileFromMachine( PlayerNumber pn ) @@ -138,43 +132,25 @@ bool ProfileManager::LoadProfileFromMemoryCard( PlayerNumber pn ) // mount slot if( MEMCARDMAN->GetCardState(pn) == MEMORY_CARD_STATE_READY ) { + CString sDir = MEM_CARD_MOUNT_POINT[pn]; // tack on a subdirectory so that we don't write everything to the root sDir += PREFSMAN->m_sMemoryCardProfileSubdir; sDir += '/'; - bool bResult; - bResult = LoadProfile( pn, sDir, true ); - if( bResult ) - return true; - - CreateMemoryCardProfile( pn ); - - bResult = LoadProfile( pn, sDir, true ); - return bResult; + MEMCARDMAN->PauseMountingThread(); + bool bSuccess; + bSuccess = LoadProfile( pn, sDir, true ); + + MEMCARDMAN->UnPauseMountingThread(); + + return true; // If a card is inserted, we want to use the memory card to save - even if the Profile load failed. } return false; } -bool ProfileManager::CreateMemoryCardProfile( PlayerNumber pn ) -{ -// CString sDir = MEM_CARD_DIR[pn]; - - ASSERT( MEMCARDMAN->GetCardState(pn) == MEMORY_CARD_STATE_READY ); - - CString sDir = MEM_CARD_MOUNT_POINT[pn]; - - DEBUG_ASSERT( FILEMAN->IsMounted(sDir) ); // should be called only if we've already mounted - - // tack on a subdirectory so that we don't write everything to the root - sDir += PREFSMAN->m_sMemoryCardProfileSubdir; - sDir += '/'; - - return CreateProfile( sDir, NEW_MEM_CARD_NAME ); -} - bool ProfileManager::LoadFirstAvailableProfile( PlayerNumber pn ) { if( LoadProfileFromMemoryCard(pn) ) @@ -192,7 +168,7 @@ void ProfileManager::SaveAllProfiles() const FOREACH_HumanPlayer( pn ) { - if( !this->IsUsingProfile(pn) ) + if( !IsUsingProfile(pn) ) continue; this->SaveProfile( pn ); @@ -220,6 +196,7 @@ void ProfileManager::UnloadProfile( PlayerNumber pn ) m_sProfileDir[pn] = ""; m_bWasLoadedFromMemoryCard[pn] = false; m_bLastLoadWasTamperedOrCorrupt[pn] = false; + m_bLastLoadWasFromLastGood[pn] = false; m_Profile[pn].InitAll(); } @@ -261,7 +238,7 @@ bool ProfileManager::CreateLocalProfile( CString sName ) return false; sProfileDir += "/"; - return CreateProfile( sProfileDir, sName ); + return Profile::CreateNewProfile( sProfileDir, sName ); } bool ProfileManager::RenameLocalProfile( CString sProfileID, CString sNewName ) @@ -271,13 +248,14 @@ bool ProfileManager::RenameLocalProfile( CString sProfileID, CString sNewName ) CString sProfileDir = USER_PROFILES_DIR + sProfileID; Profile pro; - bool bResult; - bResult = pro.LoadAllFromDir( sProfileDir, PREFSMAN->m_bSignProfileData ); - if( !bResult ) + Profile::LoadResult lr; + lr = pro.LoadAllFromDir( sProfileDir, PREFSMAN->m_bSignProfileData ); + if( lr != Profile::success ) return false; pro.m_sDisplayName = sNewName; - bResult = pro.SaveAllToDir( sProfileDir, PREFSMAN->m_bSignProfileData ); - if( !bResult ) + bool bSuccess; + bSuccess = pro.SaveAllToDir( sProfileDir, PREFSMAN->m_bSignProfileData ); + if( !bSuccess ) return false; return true; @@ -314,14 +292,12 @@ void ProfileManager::SaveMachineProfile() const m_MachineProfile.SaveAllToDir( MACHINE_PROFILE_DIR, false ); /* don't sign machine profiles */ } - - - void ProfileManager::LoadMachineProfile() { - if( !m_MachineProfile.LoadAllFromDir(MACHINE_PROFILE_DIR, false) ) + Profile::LoadResult lr = m_MachineProfile.LoadAllFromDir(MACHINE_PROFILE_DIR, false); + if( lr == Profile::failed_no_profile ) { - CreateProfile(MACHINE_PROFILE_DIR, "Machine"); + Profile::CreateNewProfile(MACHINE_PROFILE_DIR, "Machine"); m_MachineProfile.LoadAllFromDir( MACHINE_PROFILE_DIR, false ); } @@ -339,6 +315,11 @@ bool ProfileManager::LastLoadWasTamperedOrCorrupt( PlayerNumber pn ) const return GetProfile(pn) && m_bLastLoadWasTamperedOrCorrupt[pn]; } +bool ProfileManager::LastLoadWasFromLastGood( PlayerNumber pn ) const +{ + return GetProfile(pn) && m_bLastLoadWasFromLastGood[pn]; +} + CString ProfileManager::GetProfileDir( ProfileSlot slot ) const { switch( slot ) diff --git a/stepmania/src/ProfileManager.h b/stepmania/src/ProfileManager.h index a709ef779e..10cd264992 100644 --- a/stepmania/src/ProfileManager.h +++ b/stepmania/src/ProfileManager.h @@ -58,6 +58,7 @@ public: CString GetPlayerName( PlayerNumber pn ) const; bool ProfileWasLoadedFromMemoryCard( PlayerNumber pn ) const; bool LastLoadWasTamperedOrCorrupt( PlayerNumber pn ) const; + bool LastLoadWasFromLastGood( PlayerNumber pn ) const; // @@ -84,9 +85,7 @@ public: private: - bool CreateMemoryCardProfile( PlayerNumber pn ); bool LoadProfile( PlayerNumber pn, CString sProfileDir, bool bIsMemCard ); - bool CreateProfile( CString sProfileDir, CString sName ); // Directory that contains the profile. Either on local machine or // on a memory card. @@ -94,6 +93,7 @@ private: bool m_bWasLoadedFromMemoryCard[NUM_PLAYERS]; bool m_bLastLoadWasTamperedOrCorrupt[NUM_PLAYERS]; // true if Stats.xml was present, but failed to load (probably because of a signature failure) + bool m_bLastLoadWasFromLastGood[NUM_PLAYERS]; // actual loaded profile data Profile m_Profile[NUM_PLAYERS]; diff --git a/stepmania/src/ScreenManager.cpp b/stepmania/src/ScreenManager.cpp index 35358a5e6d..681012e43a 100644 --- a/stepmania/src/ScreenManager.cpp +++ b/stepmania/src/ScreenManager.cpp @@ -53,6 +53,7 @@ ScreenManager* SCREENMAN = NULL; // global and accessable from anywhere in our p #define CREDITS_CREDITS THEME->GetMetric ("ScreenSystemLayer","CreditsCredits") #define CREDITS_NOT_PRESENT THEME->GetMetric ("ScreenSystemLayer","CreditsNotPresent") #define CREDITS_LOAD_FAILED THEME->GetMetric ("ScreenSystemLayer","CreditsLoadFailed") +#define CREDITS_LOADED_FROM_LAST_GOOD_APPEND THEME->GetMetric ("ScreenSystemLayer","CreditsLoadedFromLastGoodAppend") #define CREDITS_JOIN_ONLY THEME->GetMetricB("ScreenSystemLayer","CreditsJoinOnly") const int NUM_SKIPS_TO_SHOW = 5; @@ -196,7 +197,13 @@ void ScreenSystemLayer::RefreshCreditsMessages() switch( mcs ) { case MEMORY_CARD_STATE_NO_CARD: - if( pProfile ) // this is a local machine profile + // this is a local machine profile + if( PROFILEMAN->LastLoadWasFromLastGood(p) && pProfile ) + sCredits = pProfile->GetDisplayName() + CREDITS_LOADED_FROM_LAST_GOOD_APPEND; + else if( PROFILEMAN->LastLoadWasTamperedOrCorrupt(p) ) + sCredits = CREDITS_LOAD_FAILED; + // Prefer the name of the profile over the name of the card. + else if( pProfile ) sCredits = pProfile->GetDisplayName(); else if( GAMESTATE->PlayersCanJoin() ) sCredits = CREDITS_INSERT_CARD; @@ -210,7 +217,9 @@ void ScreenSystemLayer::RefreshCreditsMessages() sCredits = CREDITS_CARD_TOO_LATE; break; case MEMORY_CARD_STATE_READY: - if( PROFILEMAN->LastLoadWasTamperedOrCorrupt(p) ) + if( PROFILEMAN->LastLoadWasFromLastGood(p) && pProfile ) + sCredits = pProfile->GetDisplayName() + CREDITS_LOADED_FROM_LAST_GOOD_APPEND; + else if( PROFILEMAN->LastLoadWasTamperedOrCorrupt(p) ) sCredits = CREDITS_LOAD_FAILED; // Prefer the name of the profile over the name of the card. else if( pProfile )