Files
itgmania212121/stepmania/src/Profile.cpp
T
Glenn Maynard ac3613a811 fix incorrect warnings
don't warn on "file not found"
2004-02-18 05:32:02 +00:00

1103 lines
28 KiB
C++

#include "global.h"
/*
-----------------------------------------------------------------------------
Class: Profile
Desc: See header.
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
Chris Danford
-----------------------------------------------------------------------------
*/
#include "Profile.h"
#include "RageUtil.h"
#include "PrefsManager.h"
#include "XmlFile.h"
#include "IniFile.h"
#include "GameManager.h"
#include "RageLog.h"
#include "RageFile.h"
#include "song.h"
#include "SongManager.h"
#include "Steps.h"
#include "Course.h"
#include <time.h>
#include "ThemeManager.h"
#include "CryptManager.h"
#include "PrefsManager.h"
#include "ProfileHtml.h"
#include "ProfileManager.h"
//
// Old file versions for backward compatibility
//
#define SM_300_STATISTICS_INI "statistics.ini"
#define SM_390A12_CATEGORY_SCORES_DAT "CategoryScores.dat"
#define SM_390A12_SONG_SCORES_DAT "SongScores.dat"
#define SM_390A12_COURSE_SCORES_DAT "CourseScores.dat"
const int SM_390A12_CATEGORY_RANKING_VERSION = 6;
const int SM_390A12_SONG_SCORES_VERSION = 9;
const int SM_390A12_COURSE_SCORES_VERSION = 8;
//
// Current file versions
//
#define PROFILE_INI "Profile.ini"
#define CATEGORY_SCORES_XML "CategoryScores.xml"
#define SONG_SCORES_XML "SongScores.xml"
#define COURSE_SCORES_XML "CourseScores.xml"
#define SCREENSHOT_DATA_XML "ScreenshotData.xml"
#define DEFAULT_PROFILE_NAME ""
void Profile::InitGeneralData()
{
// Fill in a default value in case ini doesn't have it.
m_sName = DEFAULT_PROFILE_NAME;
m_sLastUsedHighScoreName = "";
m_bUsingProfileDefaultModifiers = false;
m_sDefaultModifiers = "";
m_iTotalPlays = 0;
m_iTotalPlaySeconds = 0;
m_iTotalGameplaySeconds = 0;
m_iCurrentCombo = 0;
m_fWeightPounds = 0;
m_fCaloriesBurned = 0;
m_sLastMachinePlayed = "";
int i;
for( i=0; i<NUM_PLAY_MODES; i++ )
m_iNumSongsPlayedByPlayMode[i] = 0;
for( i=0; i<NUM_STYLES; i++ )
m_iNumSongsPlayedByStyle[i] = 0;
for( i=0; i<NUM_DIFFICULTIES; i++ )
m_iNumSongsPlayedByDifficulty[i] = 0;
for( i=0; i<MAX_METER+1; i++ )
m_iNumSongsPlayedByMeter[i] = 0;
}
void Profile::InitSongScores()
{
m_StepsHighScores.clear();
}
void Profile::InitCourseScores()
{
m_CourseHighScores.clear();
}
void Profile::InitCategoryScores()
{
for( int st=0; st<NUM_STEPS_TYPES; st++ )
for( int rc=0; rc<NUM_RANKING_CATEGORIES; rc++ )
m_CategoryHighScores[st][rc].Init();
}
void Profile::InitScreenshotData()
{
m_vScreenshots.clear();
}
CString Profile::GetDisplayName() const
{
if( !m_sName.empty() )
return m_sName;
else if( !m_sLastUsedHighScoreName.empty() )
return m_sLastUsedHighScoreName;
else
return "NoName";
}
CString Profile::GetDisplayCaloriesBurned()
{
if( m_fWeightPounds == 0 ) // weight not entered
return "N/A";
else
return ssprintf("%iCal",m_fCaloriesBurned);
}
int Profile::GetTotalNumSongsPlayed()
{
int iTotal = 0;
for( int i=0; i<NUM_PLAY_MODES; i++ )
iTotal += m_iNumSongsPlayedByPlayMode[i];
return iTotal;
}
CString Profile::GetProfileDisplayNameFromDir( CString sDir )
{
Profile pro;
pro.LoadGeneralDataFromDir( sDir );
return pro.GetDisplayName();
}
int Profile::GetSongNumTimesPlayed( const Song* pSong ) const
{
int iTotalNumTimesPlayed = 0;
vector<Steps*> vpSteps;
pSong->GetSteps( vpSteps );
for( unsigned i=0; i<vpSteps.size(); i++ )
{
const Steps* pSteps = vpSteps[i];
iTotalNumTimesPlayed += ((Profile*) this)->GetStepsHighScoreList(pSteps).iNumTimesPlayed;
}
return iTotalNumTimesPlayed;
}
//
// Steps high scores
//
void Profile::AddStepsHighScore( const Steps* pSteps, HighScore hs, int &iIndexOut )
{
std::map<const Steps*,HighScoresForASteps>::iterator iter = m_StepsHighScores.find( pSteps );
if( iter == m_StepsHighScores.end() )
m_StepsHighScores[pSteps].hs.AddHighScore( hs, iIndexOut ); // operator[] inserts into map
else
iter->second.hs.AddHighScore( hs, iIndexOut );
}
const HighScoreList& Profile::GetStepsHighScoreList( const Steps* pSteps ) const
{
/* We're const, but insert a blank entry anyway if the requested pointer doesn't exist. */
return ((Profile *) this)->m_StepsHighScores[pSteps].hs;
}
HighScoreList& Profile::GetStepsHighScoreList( const Steps* pSteps )
{
std::map<const Steps*,HighScoresForASteps>::iterator iter = m_StepsHighScores.find( pSteps );
if( iter == m_StepsHighScores.end() )
return m_StepsHighScores[pSteps].hs; // operator[] inserts into map
else
return iter->second.hs;
}
int Profile::GetStepsNumTimesPlayed( const Steps* pSteps ) const
{
std::map<const Steps*,HighScoresForASteps>::const_iterator iter = m_StepsHighScores.find( pSteps );
if( iter == m_StepsHighScores.end() )
return 0;
else
return iter->second.hs.iNumTimesPlayed;
}
void Profile::IncrementStepsPlayCount( const Steps* pSteps )
{
GetStepsHighScoreList(pSteps).iNumTimesPlayed++;
}
//
// Course high scores
//
void Profile::AddCourseHighScore( const Course* pCourse, StepsType st, HighScore hs, int &iIndexOut )
{
std::map<const Course*,HighScoresForACourse>::iterator iter = m_CourseHighScores.find( pCourse );
if( iter == m_CourseHighScores.end() )
m_CourseHighScores[pCourse].hs[st].AddHighScore( hs, iIndexOut ); // operator[] inserts into map
else
iter->second.hs[st].AddHighScore( hs, iIndexOut );
}
const HighScoreList& Profile::GetCourseHighScoreList( const Course* pCourse, StepsType st ) const
{
return ((Profile *)this)->m_CourseHighScores[pCourse].hs[st];
}
HighScoreList& Profile::GetCourseHighScoreList( const Course* pCourse, StepsType st )
{
std::map<const Course*,HighScoresForACourse>::iterator iter = m_CourseHighScores.find( pCourse );
if( iter == m_CourseHighScores.end() )
return m_CourseHighScores[pCourse].hs[st]; // operator[] inserts into map
else
return iter->second.hs[st];
}
int Profile::GetCourseNumTimesPlayed( const Course* pCourse ) const
{
std::map<const Course*,HighScoresForACourse>::const_iterator iter = m_CourseHighScores.find( pCourse );
if( iter == m_CourseHighScores.end() )
{
return 0;
}
else
{
int iTotalNumTimesPlayed = 0;
for( unsigned st = 0; st < NUM_STEPS_TYPES; ++st )
iTotalNumTimesPlayed += iter->second.hs[st].iNumTimesPlayed;
return iTotalNumTimesPlayed;
}
}
void Profile::IncrementCoursePlayCount( const Course* pCourse, StepsType st )
{
GetCourseHighScoreList(pCourse,st).iNumTimesPlayed++;
}
//
// Category high scores
//
void Profile::AddCategoryHighScore( StepsType st, RankingCategory rc, HighScore hs, int &iIndexOut )
{
m_CategoryHighScores[st][rc].AddHighScore( hs, iIndexOut );
}
const HighScoreList& Profile::GetCategoryHighScoreList( StepsType st, RankingCategory rc ) const
{
return ((Profile *)this)->m_CategoryHighScores[st][rc];
}
HighScoreList& Profile::GetCategoryHighScoreList( StepsType st, RankingCategory rc )
{
return m_CategoryHighScores[st][rc];
}
int Profile::GetCategoryNumTimesPlayed( StepsType st ) const
{
int iNumTimesPlayed = 0;
FOREACH_RankingCategory( rc )
iNumTimesPlayed += m_CategoryHighScores[st][rc].iNumTimesPlayed;
return iNumTimesPlayed;
}
void Profile::IncrementCategoryPlayCount( StepsType st, RankingCategory rc )
{
m_CategoryHighScores[st][rc].iNumTimesPlayed++;
}
//
// Loading and saving
//
bool Profile::LoadAllFromDir( CString sDir )
{
InitAll();
bool bResult = LoadGeneralDataFromDir( sDir );
// Only try to load old score formats if we're allowing unsigned data.
if( !PREFSMAN->m_bSignProfileData )
{
LoadSongScoresFromDirSM390a12( sDir );
LoadCourseScoresFromDirSM390a12( sDir );
LoadCategoryScoresFromDirSM390a12( sDir );
}
LoadSongScoresFromDir( sDir );
LoadCourseScoresFromDir( sDir );
LoadCategoryScoresFromDir( sDir );
LoadScreenshotDataFromDir( sDir );
return bResult;
}
bool Profile::SaveAllToDir( CString sDir ) const
{
m_sLastMachinePlayed = PREFSMAN->m_sMachineName;
// Delete old files after saving new ones so we don't try to load old
// and make duplicate records.
// If the save fails, the delete will fail too... probably :-)
bool bResult = SaveGeneralDataToDir( sDir );
SaveSongScoresToDir( sDir );
DeleteSongScoresFromDirSM390a12( sDir );
SaveCourseScoresToDir( sDir );
DeleteCourseScoresFromDirSM390a12( sDir );
SaveCategoryScoresToDir( sDir );
DeleteCategoryScoresFromDirSM390a12( sDir );
SaveScreenshotDataToDir( sDir );
SaveStatsWebPageToDir( sDir );
SaveMachinePublicKeyToDir( sDir );
return bResult;
}
#define WARN LOG->Warn("Error parsing file '%s' at %s:%d",fn.c_str(),__FILE__,__LINE__);
#define WARN_AND_RETURN { WARN; return; }
#define WARN_AND_CONTINUE { WARN; continue; }
#define CRYPT_VERIFY_FILE \
if( PREFSMAN->m_bSignProfileData && !CryptManager::VerifyFile(fn) ) { \
LOG->Warn("Signature check failed for '%s' at %s:%d",fn.c_str(),__FILE__,__LINE__); return; }
#define CRYPT_VERIFY_FILE_BOOL \
if( PREFSMAN->m_bSignProfileData && !CryptManager::VerifyFile(fn) ) { \
LOG->Warn("Signature check failed for '%s' at %s:%d",fn.c_str(),__FILE__,__LINE__); return false; }
#define CRYPT_WRITE_SIG if( PREFSMAN->m_bSignProfileData ) { CryptManager::SignFile(fn); }
bool Profile::LoadGeneralDataFromDir( CString sDir )
{
CString fn = sDir + PROFILE_INI;
InitGeneralData();
CRYPT_VERIFY_FILE_BOOL;
//
// read ini
//
IniFile ini( fn );
if( !ini.ReadFile() )
return false;
ini.GetValue( "Profile", "DisplayName", m_sName );
ini.GetValue( "Profile", "LastUsedHighScoreName", m_sLastUsedHighScoreName );
ini.GetValue( "Profile", "UsingProfileDefaultModifiers", m_bUsingProfileDefaultModifiers );
ini.GetValue( "Profile", "DefaultModifiers", m_sDefaultModifiers );
ini.GetValue( "Profile", "TotalPlays", m_iTotalPlays );
ini.GetValue( "Profile", "TotalPlaySeconds", m_iTotalPlaySeconds );
ini.GetValue( "Profile", "TotalGameplaySeconds", m_iTotalGameplaySeconds );
ini.GetValue( "Profile", "CurrentCombo", m_iCurrentCombo );
ini.GetValue( "Profile", "WeightPounds", m_fWeightPounds );
ini.GetValue( "Profile", "CaloriesBurned", m_fCaloriesBurned );
ini.GetValue( "Profile", "LastMachinePlayed", m_sLastMachinePlayed );
unsigned i;
for( i=0; i<NUM_PLAY_MODES; i++ )
ini.GetValue( "Profile", "NumSongsPlayedByPlayMode"+Capitalize(PlayModeToString((PlayMode)i)), m_iNumSongsPlayedByPlayMode[i] );
for( i=0; i<NUM_STYLES; i++ )
ini.GetValue( "Profile", "NumSongsPlayedByStyle"+Capitalize(GAMEMAN->GetGameDefForGame(GAMEMAN->GetStyleDefForStyle((Style)i)->m_Game)->m_szName)+Capitalize(GAMEMAN->GetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByStyle[i] );
for( i=0; i<NUM_DIFFICULTIES; i++ )
ini.GetValue( "Profile", "NumSongsPlayedByDifficulty"+Capitalize(DifficultyToString((Difficulty)i)), m_iNumSongsPlayedByDifficulty[i] );
for( i=0; i<MAX_METER+1; i++ )
ini.GetValue( "Profile", "NumSongsPlayedByMeter"+ssprintf("%d",i), m_iNumSongsPlayedByMeter[i] );
return true;
}
bool Profile::SaveGeneralDataToDir( CString sDir ) const
{
CString fn = sDir + PROFILE_INI;
IniFile ini( fn );
ini.SetValue( "Profile", "DisplayName", m_sName );
ini.SetValue( "Profile", "LastUsedHighScoreName", m_sLastUsedHighScoreName );
ini.SetValue( "Profile", "UsingProfileDefaultModifiers", m_bUsingProfileDefaultModifiers );
ini.SetValue( "Profile", "DefaultModifiers", m_sDefaultModifiers );
ini.SetValue( "Profile", "TotalPlays", m_iTotalPlays );
ini.SetValue( "Profile", "TotalPlaySeconds", m_iTotalPlaySeconds );
ini.SetValue( "Profile", "TotalGameplaySeconds", m_iTotalGameplaySeconds );
ini.SetValue( "Profile", "CurrentCombo", m_iCurrentCombo );
ini.SetValue( "Profile", "WeightPounds", m_fWeightPounds );
ini.SetValue( "Profile", "CaloriesBurned", m_fCaloriesBurned );
ini.SetValue( "Profile", "LastMachinePlayed", m_sLastMachinePlayed );
unsigned i;
for( i=0; i<NUM_PLAY_MODES; i++ )
ini.SetValue( "Profile", "NumSongsPlayedByPlayMode"+Capitalize(PlayModeToString((PlayMode)i)), m_iNumSongsPlayedByPlayMode[i] );
for( i=0; i<NUM_STYLES; i++ )
ini.SetValue( "Profile", "NumSongsPlayedByStyle"+Capitalize(GAMEMAN->GetGameDefForGame(GAMEMAN->GetStyleDefForStyle((Style)i)->m_Game)->m_szName)+Capitalize(GAMEMAN->GetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByStyle[i] );
for( i=0; i<NUM_DIFFICULTIES; i++ )
ini.SetValue( "Profile", "NumSongsPlayedByDifficulty"+Capitalize(DifficultyToString((Difficulty)i)), m_iNumSongsPlayedByDifficulty[i] );
for( i=0; i<MAX_METER+1; i++ )
ini.SetValue( "Profile", "NumSongsPlayedByMeter"+ssprintf("%d",i), m_iNumSongsPlayedByMeter[i] );
bool bResult = ini.WriteFile();
CRYPT_WRITE_SIG;
return bResult;
}
void Profile::SaveSongScoresToDir( CString sDir ) const
{
CHECKPOINT;
const Profile* pProfile = this;
ASSERT( pProfile );
CString fn = sDir + SONG_SCORES_XML;
XNode xml;
xml.name = "SongScores";
const vector<Song*> &vpSongs = SONGMAN->GetAllSongs();
for( unsigned s=0; s<vpSongs.size(); s++ ) // foreach song
{
Song* pSong = vpSongs[s];
ASSERT(pSong);
// skip songs that have never been played
if( pProfile->GetSongNumTimesPlayed(pSong) == 0 )
continue;
LPXNode pSongNode = xml.AppendChild( "Song" );
pSongNode->AppendChild( "SongDir", pSong->GetSongDir() );
const vector<Steps*> vSteps = pSong->GetAllSteps();
for( unsigned n=0; n<vSteps.size(); n++ )
{
Steps* pSteps = vSteps[n];
// skip steps that have never been played
if( pProfile->GetStepsHighScoreList(pSteps).iNumTimesPlayed == 0 )
continue;
LPXNode pStepsNode = pSongNode->AppendChild( "Steps" );
const HighScoreList &hsl = pProfile->GetStepsHighScoreList( pSteps );
pStepsNode->AppendChild( "StepsType", pSteps->m_StepsType );
pStepsNode->AppendChild( "Difficulty", pSteps->GetDifficulty() );
pStepsNode->AppendChild( "Description", pSteps->GetDescription() );
pStepsNode->AppendChild( hsl.CreateNode() );
}
}
xml.SaveToFile( fn );
CRYPT_WRITE_SIG;
}
void Profile::LoadSongScoresFromDir( CString sDir )
{
CHECKPOINT;
CString fn = sDir + SONG_SCORES_XML;
if( !IsAFile(fn) )
return;
CRYPT_VERIFY_FILE;
XNode xml;
if( !xml.LoadFromFile( fn ) )
{
LOG->Warn( "Couldn't open file \"%s\" for reading.", fn.c_str() );
return;
}
if( xml.name != "SongScores" )
WARN_AND_RETURN;
for( XNodes::iterator song = xml.childs.begin();
song != xml.childs.end();
song++ )
{
if( (*song)->name != "Song" )
continue;
CString sSongDir;
if( !(*song)->GetChildValue("SongDir", sSongDir) )
WARN_AND_CONTINUE;
Song* pSong = SONGMAN->GetSongFromDir( sSongDir );
if( pSong == NULL )
WARN_AND_CONTINUE;
for( XNodes::iterator steps = (*song)->childs.begin();
steps != (*song)->childs.end();
steps++ )
{
if( (*steps)->name != "Steps" )
continue;
StepsType st;
if( !(*steps)->GetChildValue("StepsType", (int&)st) )
WARN_AND_CONTINUE;
Difficulty dc;
if( !(*steps)->GetChildValue("Difficulty", (int&)dc) )
WARN_AND_CONTINUE;
CString sDescription;
if( !(*steps)->GetChildValue("Description", sDescription) )
WARN_AND_CONTINUE;
// Even if pSong or pSteps is null, we still have to skip over that data.
Steps* pSteps = NULL;
if( dc == DIFFICULTY_EDIT )
pSteps = pSong->GetStepsByDescription( st, sDescription );
else
pSteps = pSong->GetStepsByDifficulty( st, dc );
if( pSteps == NULL )
WARN_AND_CONTINUE;
XNode *pHighScoreListNode = (*steps)->GetChild("HighScoreList");
if( pHighScoreListNode == NULL )
WARN_AND_CONTINUE;
HighScoreList &hsl = this->GetStepsHighScoreList( pSteps );
hsl.LoadFromNode( pHighScoreListNode );
}
}
}
void Profile::LoadSongScoresFromDirSM390a12( CString sDir )
{
Profile* pProfile = this;
ASSERT( pProfile );
CString fn = sDir + SM_390A12_SONG_SCORES_DAT;
if( !IsAFile(fn) )
return;
RageFile f;
if( !f.Open(fn, RageFile::READ) )
{
LOG->Warn( "Couldn't open file \"%s\": %s", fn.c_str(), f.GetError().c_str() );
return;
}
int version;
if( !FileRead(f, version) )
WARN_AND_RETURN;
if( version != SM_390A12_SONG_SCORES_VERSION )
WARN_AND_RETURN;
int iNumSongs;
if( !FileRead(f, iNumSongs) )
WARN_AND_RETURN;
for( int s=0; s<iNumSongs; s++ )
{
CString sSongDir;
if( !FileRead(f, sSongDir) )
WARN_AND_RETURN;
Song* pSong = SONGMAN->GetSongFromDir( sSongDir );
int iNumNotes;
if( !FileRead(f, iNumNotes) )
WARN_AND_RETURN;
for( int n=0; n<iNumNotes; n++ )
{
StepsType st;
if( !FileRead(f, (int&)st) )
WARN_AND_RETURN;
Difficulty dc;
if( !FileRead(f, (int&)dc) )
WARN_AND_RETURN;
CString sDescription;
if( !FileRead(f, sDescription) )
WARN_AND_RETURN;
// Even if pSong or pSteps is null, we still have to skip over that data.
Steps* pSteps = NULL;
if( pSong )
{
if( dc==DIFFICULTY_INVALID )
pSteps = pSong->GetStepsByDescription( st, sDescription );
else
pSteps = pSong->GetStepsByDifficulty( st, dc );
}
int iNumTimesPlayed;
if( !FileRead(f, iNumTimesPlayed) )
WARN_AND_RETURN;
HighScoreList &hsl = pProfile->GetStepsHighScoreList(pSteps);
if( pSteps )
hsl.iNumTimesPlayed = iNumTimesPlayed;
int iNumHighScores;
if( !FileRead(f, iNumHighScores) )
WARN_AND_RETURN;
if( pSteps )
hsl.vHighScores.resize( iNumHighScores );
int l;
for( l=0; l<iNumHighScores; l++ )
{
CString sName;
if( !FileRead(f, sName) )
WARN_AND_RETURN;
Grade grade;
if( !FileRead(f, (int&)grade) )
WARN_AND_RETURN;
CLAMP( grade, (Grade)0, (Grade)(NUM_GRADES-1) );
int iScore;
if( !FileRead(f, iScore) )
WARN_AND_RETURN;
float fPercentDP;
if( !FileRead(f, fPercentDP) )
WARN_AND_RETURN;
if( pSteps == NULL )
continue; // ignore this high score
hsl.vHighScores[l].sName = sName;
hsl.vHighScores[l].grade = grade;
hsl.vHighScores[l].iScore = iScore;
hsl.vHighScores[l].fPercentDP = fPercentDP;
}
// ignore all high scores that are 0
for( l=0; l<iNumHighScores; l++ )
{
if( pSteps && hsl.vHighScores[l].iScore <= 0 )
{
hsl.vHighScores.resize(l);
break;
}
}
}
}
}
void Profile::LoadCategoryScoresFromDirSM390a12( CString sDir )
{
CHECKPOINT;
Profile* pProfile = this;
ASSERT( pProfile );
CString fn = sDir + SM_390A12_CATEGORY_SCORES_DAT;
if( !IsAFile(fn) )
return;
RageFile f;
if( !f.Open(fn, RageFile::READ) )
{
LOG->Warn( "Couldn't open file \"%s\": %s", fn.c_str(), f.GetError().c_str() );
return;
}
int version;
if( !FileRead(f, version) )
WARN_AND_RETURN;
if( version != SM_390A12_CATEGORY_RANKING_VERSION )
WARN_AND_RETURN;
for( int st=0; st<NUM_STEPS_TYPES; st++ )
{
for( int rc=0; rc<NUM_RANKING_CATEGORIES; rc++ )
{
int iNumHighScores;
if( !FileRead(f, iNumHighScores) )
WARN_AND_RETURN;
HighScoreList &hsl = pProfile->GetCategoryHighScoreList( (StepsType)st, (RankingCategory)rc );
hsl.vHighScores.resize( iNumHighScores );
for( int l=0; l<iNumHighScores; l++ )
{
CString sName;
if( !FileRead(f, sName) )
WARN_AND_RETURN;
int iScore;
if( !FileRead(f, iScore) )
WARN_AND_RETURN;
float fPercentDP;
if( !FileRead(f, fPercentDP) )
WARN_AND_RETURN;
hsl.vHighScores[l].sName = sName;
hsl.vHighScores[l].iScore = iScore;
hsl.vHighScores[l].fPercentDP = fPercentDP;
}
}
}
}
void Profile::LoadCourseScoresFromDirSM390a12( CString sDir )
{
CHECKPOINT;
Profile* pProfile = this;
ASSERT( pProfile );
CString fn = sDir + SM_390A12_COURSE_SCORES_DAT;
if( !IsAFile(fn) )
return;
RageFile f;
if( !f.Open(fn, RageFile::READ) )
{
LOG->Warn( "Couldn't open file \"%s\": %s", fn.c_str(), f.GetError().c_str() );
return;
}
int version;
if( !FileRead(f, version) )
WARN_AND_RETURN;
if( version != SM_390A12_COURSE_SCORES_VERSION )
WARN_AND_RETURN;
int iNumCourses;
if( !FileRead(f, iNumCourses) )
WARN_AND_RETURN;
for( int c=0; c<iNumCourses; c++ )
{
CString sPath;
if( !FileRead(f, sPath) )
WARN_AND_RETURN;
Course* pCourse = SONGMAN->GetCourseFromPath( sPath );
if( pCourse == NULL )
pCourse = SONGMAN->GetCourseFromName( sPath );
// even if we don't find the Course*, we still have to read past the input
int NumStepsTypesPlayed = 0;
if( !FileRead(f, NumStepsTypesPlayed) )
WARN_AND_RETURN;
while( NumStepsTypesPlayed-- )
{
int st;
if( !FileRead(f, st) )
WARN_AND_RETURN;
int iNumTimesPlayed;
if( !FileRead(f, iNumTimesPlayed) )
WARN_AND_RETURN;
HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, (StepsType)st );
if( pCourse )
hsl.iNumTimesPlayed = iNumTimesPlayed;
int iNumHighScores;
if( !FileRead(f, iNumHighScores) )
WARN_AND_RETURN;
if( pCourse )
hsl.vHighScores.resize(iNumHighScores);
for( int l=0; l<iNumHighScores; l++ )
{
CString sName;
if( !FileRead(f, sName) )
WARN_AND_RETURN;
int iScore;
if( !FileRead(f, iScore) )
WARN_AND_RETURN;
float fPercentDP;
if( !FileRead(f, fPercentDP) )
WARN_AND_RETURN;
float fSurviveSeconds;
if( !FileRead(f, fSurviveSeconds) )
WARN_AND_RETURN;
if( pCourse && st < NUM_STEPS_TYPES )
{
hsl.vHighScores[l].sName = sName;
hsl.vHighScores[l].iScore = iScore;
hsl.vHighScores[l].fPercentDP = fPercentDP;
hsl.vHighScores[l].fSurviveSeconds = fSurviveSeconds;
}
}
}
}
}
void Profile::SaveCourseScoresToDir( CString sDir ) const
{
CHECKPOINT;
const Profile* pProfile = this;
ASSERT( pProfile );
CString fn = sDir + COURSE_SCORES_XML;
XNode xml;
xml.name = "CourseScores";
vector<Course*> vpCourses;
SONGMAN->GetAllCourses( vpCourses, true );
for( unsigned c=0; c<vpCourses.size(); c++ ) // foreach course
{
Course *pCourse = vpCourses[c];
// skip courses that have never been played
if( pProfile->GetCourseNumTimesPlayed(pCourse) == 0 )
continue;
XNode* pCourseNode = xml.AppendChild( "Course", pCourse->m_bIsAutogen ? pCourse->m_sName : pCourse->m_sPath );
for( StepsType st=(StepsType)0; st<NUM_STEPS_TYPES; ((int&)st)++ )
{
// skip course/steps types that have never been played
if( pProfile->GetCourseHighScoreList(pCourse, st).iNumTimesPlayed == 0 )
continue;
LPXNode pStepsTypeNode = pCourseNode->AppendChild( "StepsType", st );
const HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, st );
pStepsTypeNode->AppendChild( hsl.CreateNode() );
}
}
xml.SaveToFile( fn );
CRYPT_WRITE_SIG;
}
void Profile::LoadCourseScoresFromDir( CString sDir )
{
CHECKPOINT;
CString fn = sDir + COURSE_SCORES_XML;
if( !IsAFile(fn) )
return;
CRYPT_VERIFY_FILE;
XNode xml;
if( !xml.LoadFromFile( fn ) )
{
LOG->Warn( "Couldn't open file \"%s\" for reading.", fn.c_str() );
return;
}
if( xml.name != "CourseScores" )
WARN_AND_RETURN;
for( XNodes::iterator course = xml.childs.begin();
course != xml.childs.end();
course++ )
{
if( (*course)->name != "Course" )
continue;
CString sCourse;
(*course)->GetValue(sCourse);
Course* pCourse = SONGMAN->GetCourseFromPath( sCourse );
if( pCourse == NULL )
pCourse = SONGMAN->GetCourseFromName( sCourse );
for( XNodes::iterator stepsType = (*course)->childs.begin();
stepsType != (*course)->childs.end();
stepsType++ )
{
if( (*stepsType)->name != "StepsType" )
continue;
StepsType st;
(*stepsType)->GetValue((int&)st);
XNode *pHighScoreListNode = (*stepsType)->GetChild("HighScoreList");
if( pHighScoreListNode == NULL )
WARN_AND_CONTINUE;
HighScoreList &hsl = this->GetCourseHighScoreList( pCourse, st );
hsl.LoadFromNode( pHighScoreListNode );
}
}
}
void Profile::SaveCategoryScoresToDir( CString sDir ) const
{
CHECKPOINT;
const Profile* pProfile = this;
ASSERT( pProfile );
CString fn = sDir + CATEGORY_SCORES_XML;
XNode xml;
xml.name = "CategoryScores";
FOREACH_StepsType( st )
{
// skip steps types that have never been played
if( pProfile->GetCategoryNumTimesPlayed( st ) == 0 )
continue;
XNode* pStepsTypeNode = xml.AppendChild( "StepsType", st );
FOREACH_RankingCategory( rc )
{
// skip steps types/categories that have never been played
if( pProfile->GetCategoryHighScoreList(st,rc).iNumTimesPlayed == 0 )
continue;
XNode* pRankingCategoryNode = pStepsTypeNode->AppendChild( "RankingCategory", rc );
const HighScoreList &hsl = pProfile->GetCategoryHighScoreList( (StepsType)st, (RankingCategory)rc );
pRankingCategoryNode->AppendChild( hsl.CreateNode() );
}
}
xml.SaveToFile( fn );
CRYPT_WRITE_SIG;
}
void Profile::LoadCategoryScoresFromDir( CString sDir )
{
CHECKPOINT;
CString fn = sDir + CATEGORY_SCORES_XML;
if( !IsAFile(fn) )
return;
CRYPT_VERIFY_FILE;
XNode xml;
if( !xml.LoadFromFile( fn ) )
{
LOG->Warn( "Couldn't open file \"%s\" for reading.", fn.c_str() );
return;
}
if( xml.name != "CategoryScores" )
WARN_AND_RETURN;
for( XNodes::iterator stepsType = xml.childs.begin();
stepsType != xml.childs.end();
stepsType++ )
{
if( (*stepsType)->name != "StepsType" )
continue;
StepsType st;
(*stepsType)->GetValue((int&)st);
for( XNodes::iterator radarCategory = (*stepsType)->childs.begin();
radarCategory != (*stepsType)->childs.end();
radarCategory++ )
{
if( (*radarCategory)->name != "RankingCategory" )
continue;
RankingCategory rc;
(*radarCategory)->GetValue( (int&)rc );
XNode *pHighScoreListNode = (*radarCategory)->GetChild("HighScoreList");
if( pHighScoreListNode == NULL )
WARN_AND_CONTINUE;
HighScoreList &hsl = this->GetCategoryHighScoreList( st, rc );
hsl.LoadFromNode( pHighScoreListNode );
}
}
}
void Profile::DeleteSongScoresFromDirSM390a12( CString sDir ) const
{
CString fn = sDir + SM_390A12_SONG_SCORES_DAT;
// FIXME
}
void Profile::DeleteCourseScoresFromDirSM390a12( CString sDir ) const
{
CString fn = sDir + SM_390A12_COURSE_SCORES_DAT;
// FIXME
}
void Profile::DeleteCategoryScoresFromDirSM390a12( CString sDir ) const
{
CString fn = sDir + SM_390A12_CATEGORY_SCORES_DAT;
// FIXME
}
void Profile::SaveStatsWebPageToDir( CString sDir ) const
{
// UGLY...
bool bThisIsMachineProfile = this == PROFILEMAN->GetMachineProfile();
if( bThisIsMachineProfile )
{
SaveMachineHtmlToDir( sDir, this );
// SavePlayerHtmlToDir( sDir+"temp/", this, PROFILEMAN->GetMachineProfile() ); // remove this when done debugging
}
else
{
SavePlayerHtmlToDir( sDir, this, PROFILEMAN->GetMachineProfile() );
}
}
void Profile::SaveMachinePublicKeyToDir( CString sDir ) const
{
if( IsAFile(CRYPTMAN->GetPublicKeyFileName()) )
FileCopy( CRYPTMAN->GetPublicKeyFileName(), sDir+"public.key.rsa" );
}
void Profile::AddScreenshot( Screenshot screenshot )
{
m_vScreenshots.push_back( screenshot );
}
void Profile::LoadScreenshotDataFromDir( CString sDir )
{
CHECKPOINT;
CString fn = sDir + SCREENSHOT_DATA_XML;
if( !IsAFile(fn) )
return;
CRYPT_VERIFY_FILE;
XNode xml;
if( !xml.LoadFromFile( fn ) )
{
LOG->Warn( "Couldn't open file \"%s\" for reading.", fn.c_str() );
return;
}
if( xml.name != "ScreenshotData" )
WARN_AND_RETURN;
for( XNodes::iterator screenshot = xml.childs.begin();
screenshot != xml.childs.end();
screenshot++ )
{
if( (*screenshot)->name != "Screenshot" )
WARN_AND_CONTINUE;
Screenshot ss;
if( !(*screenshot)->GetChildValue("FileName",ss.sFileName) )
WARN_AND_CONTINUE;
if( !(*screenshot)->GetChildValue("Signature",ss.sSignature) )
WARN_AND_CONTINUE;
XNode *pHighScoreNode = (*screenshot)->GetChild("HighScore");
if( pHighScoreNode == NULL )
WARN_AND_CONTINUE;
HighScore &hs = ss.highScore;
hs.LoadFromNode( pHighScoreNode );
m_vScreenshots.push_back( ss );
}
}
void Profile::SaveScreenshotDataToDir( CString sDir ) const
{
CHECKPOINT;
const Profile* pProfile = this;
ASSERT( pProfile );
CString fn = sDir + SCREENSHOT_DATA_XML;
XNode xml;
xml.name = "ScreenshotData";
for( unsigned i=0; i<m_vScreenshots.size(); i++ )
{
const Screenshot &ss = m_vScreenshots[i];
XNode* pScreenshotNode = xml.AppendChild( "Screenshot" );
pScreenshotNode->AppendChild( "FileName", ss.sFileName );
pScreenshotNode->AppendChild( "Signature", ss.sSignature );
pScreenshotNode->AppendChild( ss.highScore.CreateNode() );
}
xml.SaveToFile( fn );
CRYPT_WRITE_SIG;
}