Show message when a Profile load fails so that the player doesn't accidentally save over their possibly-good data.
Save a copy of Stats.xml to the memory card after a successful load. I have seen one instance where the digital signature on save is bogus, and there's no way for the player to recover their data. Update Stats.xsl text help text.
This commit is contained in:
+77
-70
@@ -575,81 +575,81 @@ bool Profile::LoadAllFromDir( CString sDir, bool bRequireSignature )
|
||||
|
||||
InitAll();
|
||||
|
||||
// Not critical if this fails
|
||||
LoadEditableDataFromDir( sDir );
|
||||
|
||||
// Read stats.xml
|
||||
FOR_ONCE
|
||||
// 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.
|
||||
|
||||
//
|
||||
// Don't unreasonably large stats.xml files.
|
||||
//
|
||||
if( !IsMachine() ) // only check stats coming from the player
|
||||
{
|
||||
CString fn = sDir + STATS_XML;
|
||||
if( !IsAFile(fn) )
|
||||
break;
|
||||
|
||||
//
|
||||
// Don't unreasonably large stats.xml files.
|
||||
//
|
||||
if( !IsMachine() ) // only check stats coming from the player
|
||||
int iBytes = FILEMAN->GetFileSizeInBytes( fn );
|
||||
if( iBytes > MAX_PLAYER_STATS_XML_SIZE_BYTES )
|
||||
{
|
||||
int iBytes = FILEMAN->GetFileSizeInBytes( fn );
|
||||
if( iBytes > MAX_PLAYER_STATS_XML_SIZE_BYTES )
|
||||
{
|
||||
LOG->Warn( "The file '%s' is unreasonably large. It won't be loaded.", fn.c_str() );
|
||||
break;
|
||||
}
|
||||
LOG->Warn( "The file '%s' is unreasonably large. It won't be loaded.", fn.c_str() );
|
||||
return false; // error
|
||||
}
|
||||
}
|
||||
|
||||
if( bRequireSignature )
|
||||
if( bRequireSignature )
|
||||
{
|
||||
CString sStatsXmlSigFile = fn+SIGNATURE_APPEND;
|
||||
CString sDontShareFile = sDir + DONT_SHARE_SIG;
|
||||
|
||||
LOG->Trace( "Verifying don't share signature" );
|
||||
// verify the stats.xml signature with the "don't share" file
|
||||
if( !CryptManager::VerifyFileWithFile(sStatsXmlSigFile, sDontShareFile) )
|
||||
{
|
||||
|
||||
CString sStatsXmlSigFile = fn+SIGNATURE_APPEND;
|
||||
CString sDontShareFile = sDir + DONT_SHARE_SIG;
|
||||
|
||||
LOG->Trace( "Verifying don't share signature" );
|
||||
// verify the stats.xml signature with the "don't share" file
|
||||
if( !CryptManager::VerifyFileWithFile(sStatsXmlSigFile, sDontShareFile) )
|
||||
{
|
||||
LOG->Warn( "The don't share check for '%s' failed. Data will be ignored.", sStatsXmlSigFile.c_str() );
|
||||
break;
|
||||
}
|
||||
LOG->Trace( "Done." );
|
||||
|
||||
// verify stats.xml
|
||||
LOG->Trace( "Verifying stats.xml signature" );
|
||||
if( !CryptManager::VerifyFileWithFile(fn, sStatsXmlSigFile) )
|
||||
{
|
||||
LOG->Warn( "The signature check for '%s' failed. Data will be ignored.", fn.c_str() );
|
||||
break;
|
||||
}
|
||||
LOG->Trace( "Done." );
|
||||
}
|
||||
|
||||
LOG->Trace( "Loading %s", fn.c_str() );
|
||||
XNode xml;
|
||||
if( !xml.LoadFromFile( fn ) )
|
||||
{
|
||||
LOG->Warn( "Couldn't open file '%s' for reading.", fn.c_str() );
|
||||
break;
|
||||
LOG->Warn( "The don't share check for '%s' failed. Data will be ignored.", sStatsXmlSigFile.c_str() );
|
||||
return false; // error
|
||||
}
|
||||
LOG->Trace( "Done." );
|
||||
|
||||
/* The placeholder stats.xml file has an <html> tag. Don't load it, but don't
|
||||
* warn about it. */
|
||||
if( xml.name == "html" )
|
||||
break;
|
||||
|
||||
if( xml.name != "Stats" )
|
||||
WARN_AND_BREAK_M( xml.name );
|
||||
|
||||
LOAD_NODE( GeneralData );
|
||||
LOAD_NODE( SongScores );
|
||||
LOAD_NODE( CourseScores );
|
||||
LOAD_NODE( CategoryScores );
|
||||
LOAD_NODE( ScreenshotData );
|
||||
LOAD_NODE( CalorieData );
|
||||
LOAD_NODE( RecentSongScores );
|
||||
LOAD_NODE( RecentCourseScores );
|
||||
// verify stats.xml
|
||||
LOG->Trace( "Verifying stats.xml signature" );
|
||||
if( !CryptManager::VerifyFileWithFile(fn, sStatsXmlSigFile) )
|
||||
{
|
||||
LOG->Warn( "The signature check for '%s' failed. Data will be ignored.", fn.c_str() );
|
||||
return false; // error
|
||||
}
|
||||
LOG->Trace( "Done." );
|
||||
}
|
||||
|
||||
LOG->Trace( "Loading %s", fn.c_str() );
|
||||
XNode xml;
|
||||
if( !xml.LoadFromFile( fn ) )
|
||||
{
|
||||
LOG->Warn( "Couldn't open file '%s' for reading.", fn.c_str() );
|
||||
return false; // error
|
||||
}
|
||||
LOG->Trace( "Done." );
|
||||
|
||||
/* The placeholder stats.xml file has an <html> tag. Don't load it, but don't
|
||||
* warn about it. */
|
||||
if( xml.name == "html" )
|
||||
return true; // success
|
||||
|
||||
if( xml.name != "Stats" )
|
||||
{
|
||||
WARN_M( xml.name );
|
||||
return false; // error
|
||||
}
|
||||
|
||||
LOAD_NODE( GeneralData );
|
||||
LOAD_NODE( SongScores );
|
||||
LOAD_NODE( CourseScores );
|
||||
LOAD_NODE( CategoryScores );
|
||||
LOAD_NODE( ScreenshotData );
|
||||
LOAD_NODE( CalorieData );
|
||||
LOAD_NODE( RecentSongScores );
|
||||
LOAD_NODE( RecentCourseScores );
|
||||
|
||||
return true; // FIXME? Investigate what happens if we always return true.
|
||||
return true; // success
|
||||
}
|
||||
|
||||
bool Profile::SaveAllToDir( CString sDir, bool bSignData ) const
|
||||
@@ -657,9 +657,22 @@ bool Profile::SaveAllToDir( CString sDir, bool bSignData ) const
|
||||
m_sLastPlayedMachineGuid = PROFILEMAN->GetMachineProfile()->m_sGuid;
|
||||
m_LastPlayedDate = DateTime::GetNowDate();
|
||||
|
||||
// Save editable.xml
|
||||
// Save editable.ini
|
||||
SaveEditableDataToDir( sDir );
|
||||
|
||||
bool bSaved = SaveStatsXmlToDir( sDir, bSignData );
|
||||
|
||||
SaveStatsWebPageToDir( sDir );
|
||||
|
||||
// Empty directories if none exist.
|
||||
FILEMAN->CreateDir( sDir + EDITS_SUBDIR );
|
||||
FILEMAN->CreateDir( sDir + SCREENSHOTS_SUBDIR );
|
||||
|
||||
return bSaved;
|
||||
}
|
||||
|
||||
bool Profile::SaveStatsXmlToDir( CString sDir, bool bSignData ) const
|
||||
{
|
||||
// Save stats.xml
|
||||
CString fn = sDir + STATS_XML;
|
||||
|
||||
@@ -697,12 +710,6 @@ bool Profile::SaveAllToDir( CString sDir, bool bSignData ) const
|
||||
CryptManager::SignFileToFile(sStatsXmlSigFile, sDontShareFile);
|
||||
}
|
||||
|
||||
SaveStatsWebPageToDir( sDir );
|
||||
|
||||
// Empty directories if none exist.
|
||||
FILEMAN->CreateDir( sDir + EDITS_SUBDIR );
|
||||
FILEMAN->CreateDir( sDir + SCREENSHOTS_SUBDIR );
|
||||
|
||||
return bSaved;
|
||||
}
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ public:
|
||||
//
|
||||
// Loading and saving
|
||||
//
|
||||
bool LoadAllFromDir( CString sDir, bool bRequireSignature ); // return false on error
|
||||
bool LoadAllFromDir( CString sDir, bool bRequireSignature ); // return false on a tamper or corruption error
|
||||
bool SaveAllToDir( CString sDir, bool bSignData ) const;
|
||||
|
||||
void LoadEditableDataFromDir( CString sDir );
|
||||
@@ -274,6 +274,7 @@ public:
|
||||
void LoadRecentCourseScoresFromNode( const XNode* pNode );
|
||||
|
||||
void SaveEditableDataToDir( CString sDir ) const;
|
||||
bool SaveStatsXmlToDir( CString sDir, bool bSignData ) const;
|
||||
XNode* SaveGeneralDataCreateNode() const;
|
||||
XNode* SaveSongScoresCreateNode() const;
|
||||
XNode* SaveCourseScoresCreateNode() const;
|
||||
|
||||
@@ -41,7 +41,10 @@ ProfileManager::~ProfileManager()
|
||||
void ProfileManager::Init()
|
||||
{
|
||||
FOREACH_PlayerNumber( p )
|
||||
{
|
||||
m_bWasLoadedFromMemoryCard[p] = false;
|
||||
m_bLastLoadWasTamperedOrCorrupt[p] = false;
|
||||
}
|
||||
|
||||
LoadMachineProfile();
|
||||
}
|
||||
@@ -79,8 +82,18 @@ bool ProfileManager::LoadProfile( PlayerNumber pn, CString sProfileDir, bool bIs
|
||||
|
||||
m_sProfileDir[pn] = sProfileDir;
|
||||
m_bWasLoadedFromMemoryCard[pn] = bIsMemCard;
|
||||
bool bSuccess = m_Profile[pn].LoadAllFromDir( m_sProfileDir[pn], PREFSMAN->m_bSignProfileData );
|
||||
|
||||
m_Profile[pn].LoadAllFromDir( m_sProfileDir[pn], PREFSMAN->m_bSignProfileData );
|
||||
m_bLastLoadWasTamperedOrCorrupt[pn] = !bSuccess;
|
||||
|
||||
// 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 )
|
||||
{
|
||||
CString sBackupDir = m_sProfileDir[pn] + "LastGood/";
|
||||
m_Profile[pn].SaveStatsXmlToDir( sBackupDir, PREFSMAN->m_bSignProfileData );
|
||||
}
|
||||
|
||||
if( bIsMemCard )
|
||||
MEMCARDMAN->UnPauseMountingThread();
|
||||
@@ -206,6 +219,7 @@ void ProfileManager::UnloadProfile( PlayerNumber pn )
|
||||
{
|
||||
m_sProfileDir[pn] = "";
|
||||
m_bWasLoadedFromMemoryCard[pn] = false;
|
||||
m_bLastLoadWasTamperedOrCorrupt[pn] = false;
|
||||
m_Profile[pn].InitAll();
|
||||
}
|
||||
|
||||
@@ -320,6 +334,11 @@ bool ProfileManager::ProfileWasLoadedFromMemoryCard( PlayerNumber pn ) const
|
||||
return GetProfile(pn) && m_bWasLoadedFromMemoryCard[pn];
|
||||
}
|
||||
|
||||
bool ProfileManager::LastLoadWasTamperedOrCorrupt( PlayerNumber pn ) const
|
||||
{
|
||||
return GetProfile(pn) && m_bLastLoadWasTamperedOrCorrupt[pn];
|
||||
}
|
||||
|
||||
CString ProfileManager::GetProfileDir( ProfileSlot slot ) const
|
||||
{
|
||||
switch( slot )
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
|
||||
CString GetPlayerName( PlayerNumber pn ) const;
|
||||
bool ProfileWasLoadedFromMemoryCard( PlayerNumber pn ) const;
|
||||
bool LastLoadWasTamperedOrCorrupt( PlayerNumber pn ) const;
|
||||
|
||||
|
||||
//
|
||||
@@ -81,7 +82,6 @@ public:
|
||||
void IncrementCategoryPlayCount( StepsType st, RankingCategory rc, PlayerNumber pn );
|
||||
|
||||
|
||||
// void ReadSM300NoteScores();
|
||||
|
||||
private:
|
||||
bool CreateMemoryCardProfile( PlayerNumber pn );
|
||||
@@ -93,6 +93,7 @@ private:
|
||||
CString m_sProfileDir[NUM_PLAYERS];
|
||||
|
||||
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)
|
||||
|
||||
// actual loaded profile data
|
||||
Profile m_Profile[NUM_PLAYERS];
|
||||
|
||||
@@ -52,6 +52,7 @@ ScreenManager* SCREENMAN = NULL; // global and accessable from anywhere in our p
|
||||
#define CREDITS_FREE_PLAY THEME->GetMetric ("ScreenSystemLayer","CreditsFreePlay")
|
||||
#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_JOIN_ONLY THEME->GetMetricB("ScreenSystemLayer","CreditsJoinOnly")
|
||||
|
||||
const int NUM_SKIPS_TO_SHOW = 5;
|
||||
@@ -209,7 +210,12 @@ void ScreenSystemLayer::RefreshCreditsMessages()
|
||||
sCredits = CREDITS_CARD_TOO_LATE;
|
||||
break;
|
||||
case MEMORY_CARD_STATE_READY:
|
||||
if( !MEMCARDMAN->GetName(p).empty() )
|
||||
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( !MEMCARDMAN->GetName(p).empty() )
|
||||
sCredits = MEMCARDMAN->GetName(p);
|
||||
else
|
||||
sCredits = CREDITS_CARD_NO_NAME;
|
||||
|
||||
Reference in New Issue
Block a user