Added stats prefix feature to ProfileManager for managing scores that need to be separated.
This commit is contained in:
@@ -1353,6 +1353,7 @@
|
||||
<Function name='GetProfile'/>
|
||||
<Function name='GetProfileDir'/>
|
||||
<Function name='GetSongNumTimesPlayed'/>
|
||||
<Function name='GetStatsPrefix'/>
|
||||
<Function name='IsPersistentProfile'/>
|
||||
<Function name='IsSongNew'/>
|
||||
<Function name='LastLoadWasTamperedOrCorrupt'/>
|
||||
@@ -1362,6 +1363,7 @@
|
||||
<Function name='SaveLocalProfile'/>
|
||||
<Function name='SaveMachineProfile'/>
|
||||
<Function name='SaveProfile'/>
|
||||
<Function name='SetStatsPrefix'/>
|
||||
</Class>
|
||||
<Class name='RadarValues'>
|
||||
<Function name='GetValue'/>
|
||||
|
||||
@@ -4014,6 +4014,9 @@ save yourself some time, copy this for undocumented things:
|
||||
<Function name='GetSongNumTimesPlayed' return='int' arguments='Song s, ProfileSlot ps'>
|
||||
Returns the number of times Song s has been played with the specified ProfileSlot.
|
||||
</Function>
|
||||
<Function name='GetStatsPrefix' return='string' arguments=''>
|
||||
Returns the current stats prefix.
|
||||
</Function>
|
||||
<Function name='IsPersistentProfile' return='bool' arguments='PlayerNumber pn'>
|
||||
Returns <code>true</code> if player <code>pn</code>'s profile is persistent.
|
||||
</Function>
|
||||
@@ -4044,6 +4047,9 @@ save yourself some time, copy this for undocumented things:
|
||||
<Function name='SaveProfile' return='bool' arguments='PlayerNumber pn'>
|
||||
Saves the profile for player <code>pn</code>.
|
||||
</Function>
|
||||
<Function name='SetStatsPrefix' return='' arguments='string'>
|
||||
Sets the current stats prefix. The stats prefix is prepended to the Stats.xml file when loading or saving a profile. SetStatsPrefix will reload all profiles from the Stats.xml that has the given prefix. In general, score entries are the only thing not preserved when changing the stats prefix. Profile::HandleStatsPrefixChange in Profile.cpp lists the fields that are preserved.
|
||||
</Function>
|
||||
</Class>
|
||||
<Class name='RadarValues'>
|
||||
<Function name='GetValue' return='int' arguments='RadarCategory rc'>
|
||||
|
||||
+134
-76
@@ -1065,6 +1065,55 @@ void Profile::LoadCustomFunction( RString sDir )
|
||||
|
||||
LUA->Release(L);
|
||||
}
|
||||
|
||||
void Profile::HandleStatsPrefixChange(RString dir, bool require_signature)
|
||||
{
|
||||
// Temp variables to preserve stuff across the reload.
|
||||
// Some stuff intentionally left out because the original reason for the
|
||||
// stats prefix was to allow scores from different game types to coexist.
|
||||
ProfileType type= m_Type;
|
||||
int priority= m_ListPriority;
|
||||
RString guid= m_sGuid;
|
||||
map<RString, RString> default_mods= m_sDefaultModifiers;
|
||||
SortOrder sort_order= m_SortOrder;
|
||||
Difficulty last_diff= m_LastDifficulty;
|
||||
CourseDifficulty last_course_diff= m_LastCourseDifficulty;
|
||||
StepsType last_stepstype= m_LastStepsType;
|
||||
SongID last_song= m_lastSong;
|
||||
CourseID last_course= m_lastCourse;
|
||||
int total_sessions= m_iTotalSessions;
|
||||
int total_session_seconds= m_iTotalSessionSeconds;
|
||||
int total_gameplay_seconds= m_iTotalGameplaySeconds;
|
||||
float total_calories_burned= m_fTotalCaloriesBurned;
|
||||
LuaTable user_table= m_UserTable;
|
||||
bool need_to_create_file= false;
|
||||
if(IsAFile(dir + PROFILEMAN->GetStatsPrefix() + STATS_XML))
|
||||
{
|
||||
LoadAllFromDir(dir, require_signature);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearStats();
|
||||
need_to_create_file= true;
|
||||
}
|
||||
m_Type= type;
|
||||
m_ListPriority= priority;
|
||||
m_sGuid= guid;
|
||||
m_sDefaultModifiers= default_mods;
|
||||
m_SortOrder= sort_order;
|
||||
m_LastDifficulty= last_diff;
|
||||
m_LastCourseDifficulty= last_course_diff;
|
||||
m_LastStepsType= last_stepstype;
|
||||
m_lastSong= last_song;
|
||||
m_iTotalSessions= total_sessions;
|
||||
m_iTotalGameplaySeconds= total_gameplay_seconds;
|
||||
m_fTotalCaloriesBurned= total_calories_burned;
|
||||
m_UserTable= user_table;
|
||||
if(need_to_create_file)
|
||||
{
|
||||
SaveAllToDir(dir, require_signature);
|
||||
}
|
||||
}
|
||||
|
||||
ProfileLoadResult Profile::LoadAllFromDir( RString sDir, bool bRequireSignature )
|
||||
{
|
||||
@@ -1078,82 +1127,7 @@ ProfileLoadResult Profile::LoadAllFromDir( RString sDir, bool bRequireSignature
|
||||
// Not critical if this fails
|
||||
LoadEditableDataFromDir( sDir );
|
||||
|
||||
// Check for the existance of stats.xml
|
||||
RString fn = sDir + STATS_XML;
|
||||
bool bCompressed = false;
|
||||
if( !IsAFile(fn) )
|
||||
{
|
||||
// Check for the existance of stats.xml.gz
|
||||
fn = sDir + STATS_XML_GZ;
|
||||
bCompressed = true;
|
||||
if( !IsAFile(fn) )
|
||||
return ProfileLoadResult_FailedNoProfile;
|
||||
}
|
||||
|
||||
int iError;
|
||||
auto_ptr<RageFileBasic> pFile( FILEMAN->Open(fn, RageFile::READ, iError) );
|
||||
if( pFile.get() == NULL )
|
||||
{
|
||||
LOG->Trace( "Error opening %s: %s", fn.c_str(), strerror(iError) );
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
|
||||
if( bCompressed )
|
||||
{
|
||||
RString sError;
|
||||
uint32_t iCRC32;
|
||||
RageFileObjInflate *pInflate = GunzipFile( pFile.release(), sError, &iCRC32 );
|
||||
if( pInflate == NULL )
|
||||
{
|
||||
LOG->Trace( "Error opening %s: %s", fn.c_str(), sError.c_str() );
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
|
||||
pFile.reset( pInflate );
|
||||
}
|
||||
|
||||
// Don't load unreasonably large stats.xml files.
|
||||
if( !IsMachine() ) // only check stats coming from the player
|
||||
{
|
||||
int iBytes = pFile->GetFileSize();
|
||||
if( iBytes > MAX_PLAYER_STATS_XML_SIZE_BYTES )
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt( "The file '%s' is unreasonably large. It won't be loaded.", fn.c_str() );
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
}
|
||||
|
||||
if( bRequireSignature )
|
||||
{
|
||||
RString sStatsXmlSigFile = fn+SIGNATURE_APPEND;
|
||||
RString sDontShareFile = sDir + DONT_SHARE_SIG;
|
||||
|
||||
LOG->Trace( "Verifying don't share signature \"%s\" against \"%s\"", sDontShareFile.c_str(), sStatsXmlSigFile.c_str() );
|
||||
// verify the stats.xml signature with the "don't share" file
|
||||
if( !CryptManager::VerifyFileWithFile(sStatsXmlSigFile, sDontShareFile) )
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt( "The don't share check for '%s' failed. Data will be ignored.", sStatsXmlSigFile.c_str() );
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
LOG->Trace( "Done." );
|
||||
|
||||
// verify stats.xml
|
||||
LOG->Trace( "Verifying stats.xml signature" );
|
||||
if( !CryptManager::VerifyFileWithFile(fn, sStatsXmlSigFile) )
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt( "The signature check for '%s' failed. Data will be ignored.", fn.c_str() );
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
LOG->Trace( "Done." );
|
||||
}
|
||||
|
||||
LOG->Trace( "Loading %s", fn.c_str() );
|
||||
XNode xml;
|
||||
if( !XmlFileUtil::LoadFromFileShowErrors(xml, *pFile.get()) )
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
LOG->Trace( "Done." );
|
||||
|
||||
ProfileLoadResult ret = LoadStatsXmlFromNode(&xml);
|
||||
ProfileLoadResult ret= LoadStatsFromDir(sDir, bRequireSignature);
|
||||
if (ret != ProfileLoadResult_Success)
|
||||
return ret;
|
||||
|
||||
@@ -1162,6 +1136,89 @@ ProfileLoadResult Profile::LoadAllFromDir( RString sDir, bool bRequireSignature
|
||||
return ProfileLoadResult_Success;
|
||||
}
|
||||
|
||||
ProfileLoadResult Profile::LoadStatsFromDir(RString dir, bool require_signature)
|
||||
{
|
||||
dir= dir + PROFILEMAN->GetStatsPrefix();
|
||||
// Check for the existance of stats.xml
|
||||
RString fn = dir + STATS_XML;
|
||||
bool compressed = false;
|
||||
if(!IsAFile(fn))
|
||||
{
|
||||
// Check for the existance of stats.xml.gz
|
||||
fn = dir + STATS_XML_GZ;
|
||||
compressed = true;
|
||||
if(!IsAFile(fn))
|
||||
{
|
||||
return ProfileLoadResult_FailedNoProfile;
|
||||
}
|
||||
}
|
||||
|
||||
int iError;
|
||||
auto_ptr<RageFileBasic> pFile(FILEMAN->Open(fn, RageFile::READ, iError));
|
||||
if(pFile.get() == NULL)
|
||||
{
|
||||
LOG->Trace("Error opening %s: %s", fn.c_str(), strerror(iError));
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
|
||||
if(compressed)
|
||||
{
|
||||
RString sError;
|
||||
uint32_t iCRC32;
|
||||
RageFileObjInflate *pInflate = GunzipFile(pFile.release(), sError, &iCRC32);
|
||||
if(pInflate == NULL)
|
||||
{
|
||||
LOG->Trace("Error opening %s: %s", fn.c_str(), sError.c_str());
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
|
||||
pFile.reset(pInflate);
|
||||
}
|
||||
|
||||
// Don't load unreasonably large stats.xml files.
|
||||
if(!IsMachine()) // only check stats coming from the player
|
||||
{
|
||||
int iBytes = pFile->GetFileSize();
|
||||
if(iBytes > MAX_PLAYER_STATS_XML_SIZE_BYTES)
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt("The file '%s' is unreasonably large. It won't be loaded.", fn.c_str());
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
}
|
||||
|
||||
if(require_signature)
|
||||
{
|
||||
RString sStatsXmlSigFile = fn+SIGNATURE_APPEND;
|
||||
RString sDontShareFile = dir + DONT_SHARE_SIG;
|
||||
|
||||
LOG->Trace("Verifying don't share signature \"%s\" against \"%s\"", sDontShareFile.c_str(), sStatsXmlSigFile.c_str());
|
||||
// verify the stats.xml signature with the "don't share" file
|
||||
if(!CryptManager::VerifyFileWithFile(sStatsXmlSigFile, sDontShareFile))
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt("The don't share check for '%s' failed. Data will be ignored.", sStatsXmlSigFile.c_str());
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
LOG->Trace("Done.");
|
||||
|
||||
// verify stats.xml
|
||||
LOG->Trace("Verifying stats.xml signature");
|
||||
if(!CryptManager::VerifyFileWithFile(fn, sStatsXmlSigFile))
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt("The signature check for '%s' failed. Data will be ignored.", fn.c_str());
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
}
|
||||
LOG->Trace("Done.");
|
||||
}
|
||||
|
||||
LOG->Trace("Loading %s", fn.c_str());
|
||||
XNode xml;
|
||||
if(!XmlFileUtil::LoadFromFileShowErrors(xml, *pFile.get()))
|
||||
return ProfileLoadResult_FailedTampered;
|
||||
LOG->Trace("Done.");
|
||||
|
||||
return LoadStatsXmlFromNode(&xml);
|
||||
}
|
||||
|
||||
void Profile::LoadTypeFromDir(RString dir)
|
||||
{
|
||||
m_Type= ProfileType_Normal;
|
||||
@@ -1299,6 +1356,7 @@ bool Profile::SaveStatsXmlToDir( RString sDir, bool bSignData ) const
|
||||
LOG->Trace( "SaveStatsXmlToDir: %s", sDir.c_str() );
|
||||
auto_ptr<XNode> xml( SaveStatsXmlCreateNode() );
|
||||
|
||||
sDir= sDir + PROFILEMAN->GetStatsPrefix();
|
||||
// Save stats.xml
|
||||
RString fn = sDir + (g_bProfileDataCompress? STATS_XML_GZ:STATS_XML);
|
||||
|
||||
|
||||
@@ -390,7 +390,9 @@ public:
|
||||
void swap(Profile& other);
|
||||
|
||||
// Loading and saving
|
||||
void HandleStatsPrefixChange(RString dir, bool require_signature);
|
||||
ProfileLoadResult LoadAllFromDir( RString sDir, bool bRequireSignature );
|
||||
ProfileLoadResult LoadStatsFromDir(RString dir, bool require_signature);
|
||||
void LoadTypeFromDir(RString dir);
|
||||
void LoadCustomFunction( RString sDir );
|
||||
bool SaveAllToDir( RString sDir, bool bSignData ) const;
|
||||
|
||||
@@ -73,6 +73,7 @@ static ThemeMetric<int> NUM_FIXED_PROFILES ( "ProfileManager", "NumFixedProfile
|
||||
|
||||
|
||||
ProfileManager::ProfileManager()
|
||||
:m_stats_prefix("")
|
||||
{
|
||||
m_pMachineProfile = new Profile;
|
||||
FOREACH_PlayerNumber(pn)
|
||||
@@ -1004,6 +1005,24 @@ int ProfileManager::GetNumLocalProfiles() const
|
||||
return g_vLocalProfile.size();
|
||||
}
|
||||
|
||||
void ProfileManager::SetStatsPrefix(RString const& prefix)
|
||||
{
|
||||
m_stats_prefix= prefix;
|
||||
for(size_t i= 0; i < g_vLocalProfile.size(); ++i)
|
||||
{
|
||||
g_vLocalProfile[i].profile.HandleStatsPrefixChange(g_vLocalProfile[i].sDir, PREFSMAN->m_bSignProfileData);
|
||||
}
|
||||
FOREACH_PlayerNumber(pn)
|
||||
{
|
||||
if(ProfileWasLoadedFromMemoryCard(pn))
|
||||
{
|
||||
// This probably runs into a problem if the memory card has been removed. -Kyz
|
||||
GetProfile(pn)->HandleStatsPrefixChange(m_sProfileDir[pn], PREFSMAN->m_bSignProfileData);
|
||||
}
|
||||
}
|
||||
m_pMachineProfile->HandleStatsPrefixChange(MACHINE_PROFILE_DIR, false);
|
||||
}
|
||||
|
||||
// lua start
|
||||
#include "LuaBinding.h"
|
||||
|
||||
@@ -1011,6 +1030,17 @@ int ProfileManager::GetNumLocalProfiles() const
|
||||
class LunaProfileManager: public Luna<ProfileManager>
|
||||
{
|
||||
public:
|
||||
static int GetStatsPrefix(T* p, lua_State* L)
|
||||
{
|
||||
lua_pushstring(L, p->GetStatsPrefix().c_str());
|
||||
return 1;
|
||||
}
|
||||
static int SetStatsPrefix(T* p, lua_State* L)
|
||||
{
|
||||
RString prefix= SArg(1);
|
||||
p->SetStatsPrefix(prefix);
|
||||
COMMON_RETURN_SELF;
|
||||
}
|
||||
static int IsPersistentProfile( T* p, lua_State *L ) { lua_pushboolean(L, p->IsPersistentProfile(Enum::Check<PlayerNumber>(L, 1)) ); return 1; }
|
||||
static int GetProfile( T* p, lua_State *L ) { PlayerNumber pn = Enum::Check<PlayerNumber>(L, 1); Profile* pP = p->GetProfile(pn); ASSERT(pP != NULL); pP->PushSelf(L); return 1; }
|
||||
static int GetMachineProfile( T* p, lua_State *L ) { p->GetMachineProfile()->PushSelf(L); return 1; }
|
||||
@@ -1088,6 +1118,8 @@ public:
|
||||
|
||||
LunaProfileManager()
|
||||
{
|
||||
ADD_METHOD(GetStatsPrefix);
|
||||
ADD_METHOD(SetStatsPrefix);
|
||||
ADD_METHOD( IsPersistentProfile );
|
||||
ADD_METHOD( GetProfile );
|
||||
ADD_METHOD( GetMachineProfile );
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
int GetLocalProfileIndexFromID( RString sProfileID ) const;
|
||||
int GetNumLocalProfiles() const;
|
||||
|
||||
RString GetStatsPrefix() { return m_stats_prefix; }
|
||||
void SetStatsPrefix(RString const& prefix);
|
||||
|
||||
bool LoadFirstAvailableProfile( PlayerNumber pn, bool bLoadEdits = true ); // memory card or local profile
|
||||
bool LoadLocalProfileFromMachine( PlayerNumber pn );
|
||||
@@ -119,6 +121,8 @@ private:
|
||||
// MemoryCardProfileImportSubdirs name, if the profile was imported.
|
||||
RString m_sProfileDirImportedFrom[NUM_PLAYERS];
|
||||
|
||||
RString m_stats_prefix;
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user