From 87ddfb4c41cac6011e6370c159d379e820255c6b Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Thu, 29 Sep 2005 00:33:22 +0000 Subject: [PATCH] Up until now, we've been backing up profile data by copying it to the backup directory immediately after a successful load. This ensures that the data we're copying is valid, so after a successful copy, the backup is valid, too. This has a couple problems: - Copying takes a while. It's much faster to move data. - Data shouldn't be written during a profile read. Players should be able to assume that it's safe to remove the memory card at any time except when the game is explicitly saving. - Copying good data from one place to another is just as prone to error as initially writing it. It's at least as easy for the backup copy to fail and result in a bad backup as it is for a save to fail in the first place. Instead, when we successfully load profile data (and not from a backup), set a flag. Later, when we save over that data, we'll clear the flag and move the data we're about to overwrite to the backup. --- stepmania/src/Profile.cpp | 10 +++++----- stepmania/src/Profile.h | 2 +- stepmania/src/ProfileManager.cpp | 23 +++++++++++++++++++---- stepmania/src/ProfileManager.h | 1 + 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/stepmania/src/Profile.cpp b/stepmania/src/Profile.cpp index b622626924..0c28548a9a 100644 --- a/stepmania/src/Profile.cpp +++ b/stepmania/src/Profile.cpp @@ -1924,12 +1924,12 @@ XNode* Profile::SaveCoinDataCreateNode() const return pNode; } -void Profile::BackupToDir( CString sFromDir, CString sToDir ) +void Profile::MoveBackupToDir( 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 ); + FILEMAN->MoveFile( sFromDir+EDITABLE_INI, sToDir+EDITABLE_INI ); + FILEMAN->MoveFile( sFromDir+STATS_XML, sToDir+STATS_XML ); + FILEMAN->MoveFile( sFromDir+STATS_XML+SIGNATURE_APPEND, sToDir+STATS_XML+SIGNATURE_APPEND ); + FILEMAN->MoveFile( sFromDir+DONT_SHARE_SIG, sToDir+DONT_SHARE_SIG ); } // lua start diff --git a/stepmania/src/Profile.h b/stepmania/src/Profile.h index ed58a617cb..6b0d110b6f 100644 --- a/stepmania/src/Profile.h +++ b/stepmania/src/Profile.h @@ -332,7 +332,7 @@ public: void SaveStatsWebPageToDir( CString sDir ) const; void SaveMachinePublicKeyToDir( CString sDir ) const; - static void BackupToDir( CString sFromDir, CString sToDir ); + static void MoveBackupToDir( CString sFromDir, CString sToDir ); // Lua void PushSelf( lua_State *L ); diff --git a/stepmania/src/ProfileManager.cpp b/stepmania/src/ProfileManager.cpp index 15ba9e35dc..1d2df22b9c 100644 --- a/stepmania/src/ProfileManager.cpp +++ b/stepmania/src/ProfileManager.cpp @@ -75,6 +75,7 @@ void ProfileManager::Init() m_bWasLoadedFromMemoryCard[p] = false; m_bLastLoadWasTamperedOrCorrupt[p] = false; m_bLastLoadWasFromLastGood[p] = false; + m_bNeedToBackUpLastLoad[p] = false; } LoadMachineProfile(); @@ -119,18 +120,17 @@ ProfileLoadResult ProfileManager::LoadProfile( PlayerNumber pn, CString sProfile m_sProfileDir[pn] = sProfileDir; m_bWasLoadedFromMemoryCard[pn] = bIsMemCard; m_bLastLoadWasFromLastGood[pn] = false; + m_bNeedToBackUpLastLoad[pn] = false; // Try to load the original, non-backup data. ProfileLoadResult lr = GetProfile(pn)->LoadAllFromDir( m_sProfileDir[pn], PREFSMAN->m_bSignProfileData ); CString sBackupDir = m_sProfileDir[pn] + LAST_GOOD_SUBDIR; - // 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 == ProfileLoadResult_Success ) { - Profile::BackupToDir( m_sProfileDir[pn], sBackupDir ); + /* Next time the profile is written, move this good profile into LastGood. */ + m_bNeedToBackUpLastLoad[pn] = true; } m_bLastLoadWasTamperedOrCorrupt[pn] = lr == ProfileLoadResult_FailedTampered; @@ -297,6 +297,20 @@ bool ProfileManager::SaveProfile( PlayerNumber pn ) const if( m_sProfileDir[pn].empty() ) return false; + /* + * If the profile we're writing was loaded from the primary (non-backup) + * data, then we've validated it and know it's good. Before writing our + * new data, move the old, good data to the backup. (Only do this once; + * if we save the profile more than once, we havn't re-validated the + * newly written data.) + */ + if( m_bNeedToBackUpLastLoad[pn] ) + { + m_bNeedToBackUpLastLoad[pn] = false; + CString sBackupDir = m_sProfileDir[pn] + LAST_GOOD_DIR; + Profile::MoveBackupToDir( m_sProfileDir[pn], sBackupDir ); + } + bool b = GetProfile(pn)->SaveAllToDir( m_sProfileDir[pn], PREFSMAN->m_bSignProfileData ); return b; @@ -318,6 +332,7 @@ void ProfileManager::UnloadProfile( PlayerNumber pn ) m_bWasLoadedFromMemoryCard[pn] = false; m_bLastLoadWasTamperedOrCorrupt[pn] = false; m_bLastLoadWasFromLastGood[pn] = false; + m_bNeedToBackUpLastLoad[pn] = false; m_pMemoryCardProfile[pn]->InitAll(); SONGMAN->FreeAllLoadedFromProfile( (ProfileSlot) pn ); } diff --git a/stepmania/src/ProfileManager.h b/stepmania/src/ProfileManager.h index 049b16c81e..9ca23dd8e5 100644 --- a/stepmania/src/ProfileManager.h +++ b/stepmania/src/ProfileManager.h @@ -125,6 +125,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]; // if true, then m_bLastLoadWasTamperedOrCorrupt is also true + mutable bool m_bNeedToBackUpLastLoad[NUM_PLAYERS]; // if true, back up profile on next save Profile *m_pMemoryCardProfile[NUM_PLAYERS]; // holds Profile for the currently inserted card Profile *m_pMachineProfile;