Files
itgmania212121/stepmania/src/ProfileManager.cpp
T

1449 lines
40 KiB
C++
Raw Normal View History

#include "global.h"
/*
-----------------------------------------------------------------------------
Class: ProfileManager
Desc: See header.
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
Chris Danford
-----------------------------------------------------------------------------
*/
#include "ProfileManager.h"
#include "RageUtil.h"
#include "arch/arch.h"
#include "PrefsManager.h"
#include "RageLog.h"
2003-12-05 00:07:18 +00:00
#include "RageFile.h"
#include "RageFileManager.h"
#include "IniFile.h"
2003-09-08 07:21:41 +00:00
#include "GameConstantsAndTypes.h"
#include "SongManager.h"
2003-11-10 04:32:12 +00:00
#include "GameState.h"
#include "song.h"
2003-12-18 03:40:57 +00:00
#include "Steps.h"
#include "Course.h"
#include "GameManager.h"
#include "ProductInfo.h"
2003-12-07 08:19:10 +00:00
#include "RageUtil.h"
2003-12-08 10:27:45 +00:00
#include "ThemeManager.h"
2003-12-10 05:03:17 +00:00
#include "Bookkeeper.h"
#include <time.h>
#include "MemoryCardManager.h"
2003-11-14 17:17:36 +00:00
ProfileManager* PROFILEMAN = NULL; // global and accessable from anywhere in our program
#define PROFILE_FILE "Profile.ini"
#define CATEGORY_SCORES_FILE "CategoryScores.dat"
#define SONG_SCORES_FILE "SongScores.dat"
#define COURSE_SCORES_FILE "CourseScores.dat"
#define STATS_HTML_FILE "stats.html"
2003-12-08 10:27:45 +00:00
#define STYLE_CSS_FILE "style.css"
#define NEW_MEM_CARD_NAME "NewCard"
#define NEW_PROFILE_NAME "NewProfile"
2003-12-10 09:15:40 +00:00
#define SM_300_STATISTICS_FILE "statistics.ini"
2003-12-10 09:15:40 +00:00
#define USER_PROFILES_DIR "Data/LocalProfiles/"
#define MACHINE_PROFILE_DIR "Data/MachineProfile/"
const int CATEGORY_RANKING_VERSION = 6;
const int SONG_SCORES_VERSION = 9;
const int COURSE_SCORES_VERSION = 8;
#define STATS_TITLE THEME->GetMetric("ProfileManager","StatsTitle")
static const char *MEM_CARD_DIR[NUM_PLAYERS] =
2003-12-05 00:07:18 +00:00
{
/* @ is important; see RageFileManager LoadedDriver::GetPath */
2003-12-10 09:26:05 +00:00
"@mc1/",
"@mc2/",
2003-12-05 00:07:18 +00:00
};
ProfileManager::ProfileManager()
{
for( int p=0; p<NUM_PLAYERS; p++ )
2003-12-05 00:07:18 +00:00
{
m_bUsingMemoryCard[p] = false;
2003-12-05 00:07:18 +00:00
}
InitMachineScoresFromDisk();
}
ProfileManager::~ProfileManager()
{
SaveMachineScoresToDisk();
}
2003-12-07 08:19:10 +00:00
void ProfileManager::GetLocalProfileIDs( vector<CString> &asProfileIDsOut )
{
GetDirListing( USER_PROFILES_DIR "*", asProfileIDsOut, true, false );
}
2003-12-07 08:19:10 +00:00
void ProfileManager::GetLocalProfileNames( vector<CString> &asNamesOut )
2003-09-08 07:21:41 +00:00
{
2003-11-01 19:36:52 +00:00
CStringArray vsProfileIDs;
2003-12-07 08:19:10 +00:00
GetLocalProfileIDs( vsProfileIDs );
2003-11-01 19:36:52 +00:00
for( unsigned i=0; i<vsProfileIDs.size(); i++ )
2003-09-08 07:21:41 +00:00
{
2003-11-01 19:36:52 +00:00
CString sProfileID = vsProfileIDs[i];
2003-09-08 07:21:41 +00:00
Profile pro;
2003-12-10 09:26:05 +00:00
pro.LoadFromIni( USER_PROFILES_DIR + sProfileID + "/" + PROFILE_FILE );
2003-11-01 19:36:52 +00:00
asNamesOut.push_back( pro.m_sName );
2003-09-08 07:21:41 +00:00
}
}
2003-11-01 22:04:43 +00:00
bool ProfileManager::LoadProfile( PlayerNumber pn, CString sProfileDir, bool bIsMemCard )
{
2003-11-01 22:04:43 +00:00
ASSERT( !sProfileDir.empty() );
2003-12-10 09:26:05 +00:00
ASSERT( sProfileDir.Right(1) == "/" );
2003-11-01 22:04:43 +00:00
m_sProfileDir[pn] = sProfileDir;
m_bUsingMemoryCard[pn] = bIsMemCard;
2003-11-01 19:36:52 +00:00
bool bResult = m_Profile[pn].LoadFromIni( m_sProfileDir[pn]+PROFILE_FILE );
if( !bResult )
{
2003-11-01 22:04:43 +00:00
LOG->Warn( "Attempting to load profile from '%s' and does not exist", sProfileDir.c_str() );
2003-11-01 19:36:52 +00:00
UnloadProfile( pn );
return false;
}
ReadCategoryScoresFromDir( m_sProfileDir[pn], (MemoryCard)pn );
ReadSongScoresFromDir( m_sProfileDir[pn], (MemoryCard)pn );
ReadCourseScoresFromDir( m_sProfileDir[pn], (MemoryCard)pn );
2003-11-01 19:36:52 +00:00
2003-11-10 04:32:12 +00:00
// apply saved default modifiers if any
if( m_Profile[pn].m_bUsingProfileDefaultModifiers )
{
GAMESTATE->m_PlayerOptions[pn].Init();
GAMESTATE->ApplyModifiers( pn, m_Profile[pn].m_sDefaultModifiers );
}
2003-11-01 19:36:52 +00:00
return true;
}
2003-11-01 22:04:43 +00:00
bool ProfileManager::CreateProfile( CString sProfileDir, CString sName )
{
2003-12-08 06:41:30 +00:00
ASSERT( !sName.empty() );
2003-11-01 22:04:43 +00:00
bool bResult;
Profile pro;
pro.m_sName = sName;
bResult = pro.SaveToIni( sProfileDir + PROFILE_FILE );
if( !bResult )
return false;
FlushDirCache();
return true;
}
bool ProfileManager::LoadDefaultProfileFromMachine( PlayerNumber pn )
{
2003-12-07 08:19:10 +00:00
CString sProfileID = PREFSMAN->m_sDefaultLocalProfileID[pn];
2003-11-01 22:04:43 +00:00
if( sProfileID.empty() )
{
m_sProfileDir[pn] = "";
return false;
}
2003-12-10 09:26:05 +00:00
CString sDir = USER_PROFILES_DIR + sProfileID + "/";
2003-11-01 22:04:43 +00:00
return LoadProfile( pn, sDir, false );
}
bool ProfileManager::LoadProfileFromMemoryCard( PlayerNumber pn )
{
CString sDir = MEM_CARD_DIR[pn];
DEBUG_ASSERT( FILEMAN->IsMounted(sDir) ); // should be called only if we've already mounted
2003-11-01 22:04:43 +00:00
// tack on a subdirectory so that we don't write everything to the root
sDir += PREFSMAN->m_sMemoryCardProfileSubdir;
sDir += '/';
2003-11-01 22:04:43 +00:00
m_bUsingMemoryCard[pn] = true;
bool bResult;
bResult = LoadProfile( pn, sDir, false );
2003-11-08 15:49:48 +00:00
return bResult;
2003-11-01 22:04:43 +00:00
}
2003-11-09 21:39:54 +00:00
bool ProfileManager::CreateMemoryCardProfile( PlayerNumber pn )
{
CString sDir = MEM_CARD_DIR[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 );
}
2003-11-01 22:04:43 +00:00
bool ProfileManager::LoadFirstAvailableProfile( PlayerNumber pn )
{
2003-11-13 00:39:36 +00:00
#ifndef _XBOX
// mount card
if( MEMCARDMAN->GetCardState(pn) == MEMORY_CARD_STATE_READY )
2003-11-08 15:49:48 +00:00
{
FILEMAN->Mount( "dir", MEMCARDMAN->GetOsMountDir(pn), MEM_CARD_DIR[pn] );
if( LoadProfileFromMemoryCard(pn) )
return true;
CreateMemoryCardProfile( pn );
2003-11-08 15:49:48 +00:00
if( LoadProfileFromMemoryCard(pn) )
return true;
}
2003-11-13 00:39:36 +00:00
#endif
2003-11-08 15:49:48 +00:00
if( LoadDefaultProfileFromMachine(pn) )
2003-11-01 22:04:43 +00:00
return true;
2003-11-08 15:49:48 +00:00
return false;
2003-11-01 22:04:43 +00:00
}
2003-11-01 19:36:52 +00:00
bool ProfileManager::SaveProfile( PlayerNumber pn )
{
if( m_sProfileDir[pn].empty() )
2003-11-01 19:36:52 +00:00
return false;
2003-11-01 19:36:52 +00:00
m_Profile[pn].SaveToIni( m_sProfileDir[pn]+PROFILE_FILE );
2003-12-08 10:27:45 +00:00
SaveCategoryScoresToDir( m_sProfileDir[pn], (MemoryCard)pn );
SaveSongScoresToDir( m_sProfileDir[pn], (MemoryCard)pn );
SaveCourseScoresToDir( m_sProfileDir[pn], (MemoryCard)pn );
SaveStatsWebPageToDir( m_sProfileDir[pn], (MemoryCard)pn );
2003-11-01 19:36:52 +00:00
return true;
}
2003-11-01 19:36:52 +00:00
void ProfileManager::UnloadProfile( PlayerNumber pn )
{
m_sProfileDir[pn] = "";
m_bUsingMemoryCard[pn] = false;
m_Profile[pn].Init();
2003-09-08 07:21:41 +00:00
}
2003-11-01 19:36:52 +00:00
Profile* ProfileManager::GetProfile( PlayerNumber pn )
2003-09-08 07:21:41 +00:00
{
2003-11-01 19:36:52 +00:00
if( m_sProfileDir[pn].empty() )
return NULL;
else
return &m_Profile[pn];
2003-09-08 07:21:41 +00:00
}
2003-12-17 09:42:31 +00:00
CString ProfileManager::GetPlayerName( PlayerNumber pn )
{
Profile *prof = ProfileManager::GetProfile( pn );
if( prof )
return prof->m_sLastUsedHighScoreName;
const char *names[NUM_PLAYERS] = { "PLAYER 1", "PLAYER 2" };
return names[pn];
}
2003-09-08 07:21:41 +00:00
bool Profile::LoadFromIni( CString sIniPath )
{
Init();
CStringArray asBits;
2003-12-10 09:26:05 +00:00
split( Dirname(sIniPath), "/", asBits, true );
2003-09-08 07:21:41 +00:00
CString sLastDir = asBits.back(); // this is a number name, e.g. "0000001"
// Fill in a default value in case ini doesn't have it.
2003-11-01 22:04:43 +00:00
m_sName = NEW_PROFILE_NAME;
2003-09-08 07:21:41 +00:00
//
// read ini
//
IniFile ini( sIniPath );
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 );
2003-12-08 04:39:29 +00:00
ini.GetValue( "Profile", "CurrentCombo", m_iCurrentCombo );
2003-12-09 10:20:18 +00:00
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++ )
2003-12-10 08:57:30 +00:00
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] );
2003-12-09 10:20:18 +00:00
for( i=0; i<NUM_DIFFICULTIES; i++ )
2003-12-10 08:57:30 +00:00
ini.GetValue( "Profile", "NumSongsPlayedByDifficulty"+Capitalize(DifficultyToString((Difficulty)i)), m_iNumSongsPlayedByDifficulty[i] );
2003-12-09 10:20:18 +00:00
for( i=0; i<MAX_METER+1; i++ )
2003-12-10 08:57:30 +00:00
ini.GetValue( "Profile", "NumSongsPlayedByMeter"+ssprintf("%d",i), m_iNumSongsPlayedByMeter[i] );
2003-09-08 07:21:41 +00:00
return true;
}
2003-11-01 19:36:52 +00:00
bool Profile::SaveToIni( CString sIniPath )
2003-09-08 07:21:41 +00:00
{
IniFile ini( sIniPath );
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 );
2003-12-08 04:39:29 +00:00
ini.SetValue( "Profile", "CurrentCombo", m_iCurrentCombo );
2003-12-09 10:20:18 +00:00
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++ )
2003-12-10 08:57:30 +00:00
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] );
2003-12-09 10:20:18 +00:00
for( i=0; i<NUM_DIFFICULTIES; i++ )
2003-12-10 08:57:30 +00:00
ini.SetValue( "Profile", "NumSongsPlayedByDifficulty"+Capitalize(DifficultyToString((Difficulty)i)), m_iNumSongsPlayedByDifficulty[i] );
2003-12-09 10:20:18 +00:00
for( i=0; i<MAX_METER+1; i++ )
2003-12-10 08:57:30 +00:00
ini.SetValue( "Profile", "NumSongsPlayedByMeter"+ssprintf("%d",i), m_iNumSongsPlayedByMeter[i] );
2003-12-09 10:20:18 +00:00
ini.WriteFile();
2003-09-08 07:21:41 +00:00
return true;
}
2003-12-07 08:19:10 +00:00
bool ProfileManager::CreateLocalProfile( CString sName )
2003-09-08 07:21:41 +00:00
{
2003-12-07 08:19:10 +00:00
ASSERT( !sName.empty() );
2003-11-01 22:04:43 +00:00
2003-09-08 07:21:41 +00:00
//
// Find a free directory name in the profiles directory
//
2003-11-01 19:36:52 +00:00
CString sProfileID, sProfileDir;
const int MAX_TRIES = 1000;
2003-12-07 08:19:10 +00:00
int i;
for( i=0; i<MAX_TRIES; i++ )
2003-09-08 07:21:41 +00:00
{
2003-11-01 19:36:52 +00:00
sProfileID = ssprintf("%08d",i);
sProfileDir = USER_PROFILES_DIR + sProfileID;
2003-11-01 19:36:52 +00:00
if( !DoesFileExist(sProfileDir) )
2003-09-08 07:21:41 +00:00
break;
}
2003-11-01 19:36:52 +00:00
if( i == MAX_TRIES )
return false;
2003-12-10 09:26:05 +00:00
sProfileDir += "/";
2003-09-08 07:21:41 +00:00
Profile pro;
2003-11-01 19:36:52 +00:00
pro.m_sName = sName;
2003-12-07 04:16:10 +00:00
bool bResult;
2003-11-01 22:04:43 +00:00
bResult = pro.SaveToIni( sProfileDir + PROFILE_FILE );
if( !bResult )
return false;
2003-09-08 07:21:41 +00:00
FlushDirCache();
2003-11-01 19:36:52 +00:00
return true;
}
2003-12-07 08:19:10 +00:00
bool ProfileManager::RenameLocalProfile( CString sProfileID, CString sNewName )
2003-11-01 19:36:52 +00:00
{
ASSERT( !sProfileID.empty() );
CString sProfileDir = USER_PROFILES_DIR + sProfileID;
2003-12-10 09:26:05 +00:00
CString sProfileFile = sProfileDir + "/" + PROFILE_FILE;
2003-11-01 19:36:52 +00:00
Profile pro;
bool bResult;
bResult = pro.LoadFromIni( sProfileFile );
if( !bResult )
return false;
pro.m_sName = sNewName;
bResult = pro.SaveToIni( sProfileFile );
if( !bResult )
return false;
return true;
}
2003-11-01 19:36:52 +00:00
2003-12-07 08:19:10 +00:00
bool ProfileManager::DeleteLocalProfile( CString sProfileID )
2003-11-01 19:36:52 +00:00
{
// delete all files in profile dir
CString sProfileDir = USER_PROFILES_DIR + sProfileID;
2003-11-01 19:36:52 +00:00
CStringArray asFilesToDelete;
2003-12-10 09:26:05 +00:00
GetDirListing( sProfileDir + "/*", asFilesToDelete, false, true );
2003-11-01 19:36:52 +00:00
for( unsigned i=0; i<asFilesToDelete.size(); i++ )
2003-12-16 07:59:32 +00:00
FILEMAN->Remove( asFilesToDelete[i] );
2003-11-01 19:36:52 +00:00
// remove profile dir
2003-12-16 07:59:32 +00:00
return FILEMAN->Remove( sProfileDir );
}
void ProfileManager::SaveMachineScoresToDisk()
{
2003-12-08 06:41:30 +00:00
m_MachineProfile.SaveToIni( MACHINE_PROFILE_DIR PROFILE_FILE );
2003-12-08 10:27:45 +00:00
SaveCategoryScoresToDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE );
SaveSongScoresToDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE );
SaveCourseScoresToDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE );
SaveStatsWebPageToDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE );
}
void ProfileManager::CategoryData::AddHighScore( HighScore hs, int &iIndexOut )
{
int i;
for( i=0; i<(int)vHighScores.size(); i++ )
{
if( hs >= vHighScores[i] )
break;
}
if( i < NUM_RANKING_LINES )
{
vHighScores.insert( vHighScores.begin()+i, hs );
iIndexOut = i;
if( int(vHighScores.size()) > NUM_RANKING_LINES )
vHighScores.erase( vHighScores.begin()+NUM_RANKING_LINES, vHighScores.end() );
}
}
#define WARN_AND_RETURN { LOG->Warn("Error parsing file '%s' at %s:%d",fn.c_str(),__FILE__,__LINE__); return; }
2003-12-08 10:27:45 +00:00
void ProfileManager::ReadSongScoresFromDir( CString sDir, MemoryCard mc )
{
2003-12-08 10:27:45 +00:00
CString fn = sDir + SONG_SCORES_FILE;
2003-12-07 08:19:10 +00:00
RageFile f;
if( !f.Open(fn, RageFile::READ) )
{
LOG->Warn( "Couldn't open file '%s'", fn.c_str() );
2003-12-07 08:19:10 +00:00
return;
}
int version;
if( !FileRead(f, version) )
WARN_AND_RETURN;
if( version != 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 nt;
if( !FileRead(f, (int&)nt) )
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 pNotes is null, we still have to skip over that data.
Steps* pNotes = NULL;
if( pSong )
{
if( dc==DIFFICULTY_INVALID )
pNotes = pSong->GetStepsByDescription( nt, sDescription );
else
pNotes = pSong->GetStepsByDifficulty( nt, dc );
}
int iNumTimesPlayed;
if( !FileRead(f, iNumTimesPlayed) )
WARN_AND_RETURN;
if( pNotes )
pNotes->m_MemCardDatas[mc].iNumTimesPlayed = iNumTimesPlayed;
int iNumHighScores;
if( !FileRead(f, iNumHighScores) )
WARN_AND_RETURN;
if( pNotes )
pNotes->m_MemCardDatas[mc].vHighScores.resize( iNumHighScores );
2003-12-10 09:15:40 +00:00
int l;
2003-12-10 08:32:16 +00:00
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( pNotes == NULL )
continue; // ignore this high score
pNotes->m_MemCardDatas[mc].vHighScores[l].sName = sName;
pNotes->m_MemCardDatas[mc].vHighScores[l].grade = grade;
pNotes->m_MemCardDatas[mc].vHighScores[l].iScore = iScore;
pNotes->m_MemCardDatas[mc].vHighScores[l].fPercentDP = fPercentDP;
}
2003-12-10 08:32:16 +00:00
// ignore all high scores that are 0
for( l=0; l<iNumHighScores; l++ )
{
2003-12-12 05:38:10 +00:00
if( pNotes && pNotes->m_MemCardDatas[mc].vHighScores[l].iScore <= 0 )
2003-12-10 08:32:16 +00:00
{
pNotes->m_MemCardDatas[mc].vHighScores.resize(l);
break;
}
}
}
}
}
2003-12-08 10:27:45 +00:00
void ProfileManager::ReadCategoryScoresFromDir( CString sDir, MemoryCard mc )
{
2003-12-08 10:27:45 +00:00
CString fn = sDir + CATEGORY_SCORES_FILE;
2003-12-07 08:19:10 +00:00
RageFile f;
if( !f.Open(fn, RageFile::READ) )
{
LOG->Warn( "Couldn't open file '%s'", fn.c_str() );
2003-12-07 08:19:10 +00:00
return;
}
int version;
if( !FileRead(f, version) )
WARN_AND_RETURN;
if( version != 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;
m_CategoryDatas[mc][st][rc].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;
m_CategoryDatas[mc][st][rc].vHighScores[l].sName = sName;
m_CategoryDatas[mc][st][rc].vHighScores[l].iScore = iScore;
m_CategoryDatas[mc][st][rc].vHighScores[l].fPercentDP = fPercentDP;
}
}
}
}
2003-12-08 10:27:45 +00:00
void ProfileManager::ReadCourseScoresFromDir( CString sDir, MemoryCard mc )
{
2003-12-08 10:27:45 +00:00
CString fn = sDir + COURSE_SCORES_FILE;
2003-12-07 08:19:10 +00:00
RageFile f;
if( !f.Open(fn, RageFile::READ) )
{
LOG->Warn( "Couldn't open file '%s'", fn.c_str() );
2003-12-07 08:19:10 +00:00
return;
}
int version;
if( !FileRead(f, version) )
WARN_AND_RETURN;
if( version != 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 NumStepsPlayed = 0;
if( !FileRead(f, NumStepsPlayed) )
WARN_AND_RETURN;
while( NumStepsPlayed-- )
{
int st;
if( !FileRead(f, st) )
WARN_AND_RETURN;
int iNumTimesPlayed;
if( !FileRead(f, iNumTimesPlayed) )
WARN_AND_RETURN;
if( pCourse )
pCourse->m_MemCardDatas[st][mc].iNumTimesPlayed = iNumTimesPlayed;
int iNumHighScores;
if( !FileRead(f, iNumHighScores) )
WARN_AND_RETURN;
if( pCourse )
pCourse->m_MemCardDatas[st][mc].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 fSurviveTime;
if( !FileRead(f, fSurviveTime) )
WARN_AND_RETURN;
if( pCourse && st < NUM_STEPS_TYPES )
{
pCourse->m_MemCardDatas[st][mc].vHighScores[l].sName = sName;
pCourse->m_MemCardDatas[st][mc].vHighScores[l].iScore = iScore;
pCourse->m_MemCardDatas[st][mc].vHighScores[l].fPercentDP = fPercentDP;
pCourse->m_MemCardDatas[st][mc].vHighScores[l].fSurviveTime = fSurviveTime;
}
}
}
}
}
void ProfileManager::InitMachineScoresFromDisk()
{
// read old style notes scores
ReadSM300NoteScores();
// category ranking
2003-12-08 10:27:45 +00:00
ReadCategoryScoresFromDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE );
ReadSongScoresFromDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE );
ReadCourseScoresFromDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE );
2003-12-08 06:41:30 +00:00
if( !m_MachineProfile.LoadFromIni(MACHINE_PROFILE_DIR PROFILE_FILE) )
{
CreateProfile(MACHINE_PROFILE_DIR, "Machine");
m_MachineProfile.LoadFromIni(MACHINE_PROFILE_DIR PROFILE_FILE);
}
}
void ProfileManager::ReadSM300NoteScores()
{
if( !DoesFileExist(SM_300_STATISTICS_FILE) )
return;
IniFile ini;
ini.SetPath( SM_300_STATISTICS_FILE );
// load song statistics
const IniFile::key* pKey = ini.GetKey( "Statistics" );
if( pKey )
{
for( IniFile::key::const_iterator iter = pKey->begin();
iter != pKey->end();
iter++ )
{
CString name = iter->first;
CString value = iter->second;
// Each value has the format "SongName::StepsType::StepsDescription=TimesPlayed::TopGrade::TopScore::MaxCombo".
char szSongDir[256];
char szStepsType[256];
char szStepsDescription[256];
int iRetVal;
// Parse for Song name and Notes name
iRetVal = sscanf( name, "%[^:]::%[^:]::%[^:]", szSongDir, szStepsType, szStepsDescription );
if( iRetVal != 3 )
continue; // this line doesn't match what is expected
CString sSongDir = FixSlashes( szSongDir );
// Search for the corresponding Song pointer.
Song* pSong = SONGMAN->GetSongFromDir( sSongDir );
if( pSong == NULL ) // didn't find a match
continue; // skip this entry
StepsType st = GAMEMAN->StringToNotesType( szStepsType );
Difficulty dc = StringToDifficulty( szStepsDescription );
// Search for the corresponding Notes pointer.
Steps* pNotes = pSong->GetStepsByDifficulty( st, dc );
if( pNotes == NULL ) // didn't find a match
continue; // skip this entry
// Parse the Notes statistics.
char szGradeLetters[10]; // longest possible string is "AAA"
int iMaxCombo; // throw away
pNotes->m_MemCardDatas[MEMORY_CARD_MACHINE].vHighScores.resize(1);
iRetVal = sscanf(
value,
"%d::%[^:]::%d::%d",
&pNotes->m_MemCardDatas[MEMORY_CARD_MACHINE].iNumTimesPlayed,
szGradeLetters,
&pNotes->m_MemCardDatas[MEMORY_CARD_MACHINE].vHighScores[0].iScore,
&iMaxCombo
);
if( iRetVal != 4 )
continue;
pNotes->m_MemCardDatas[MEMORY_CARD_MACHINE].vHighScores[0].grade = StringToGrade( szGradeLetters );
}
}
}
2003-12-08 10:27:45 +00:00
void ProfileManager::SaveCategoryScoresToDir( CString sDir, MemoryCard mc )
{
2003-12-08 10:27:45 +00:00
CString fn = sDir + CATEGORY_SCORES_FILE;
LOG->Trace("SongManager::SaveCategoryRankingsToFile");
2003-12-07 08:19:10 +00:00
RageFile f;
if( !f.Open(fn, RageFile::WRITE) )
{
LOG->Warn( "Couldn't open file '%s'", fn.c_str() );
return;
}
FileWrite( f, CATEGORY_RANKING_VERSION );
for( int st=0; st<NUM_STEPS_TYPES; st++ )
{
for( int rc=0; rc<NUM_RANKING_CATEGORIES; rc++ )
{
FileWrite( f, m_CategoryDatas[mc][st][rc].vHighScores.size() );
for( unsigned l=0; l<m_CategoryDatas[mc][st][rc].vHighScores.size(); l++ )
{
// tricky: wipe out "name to fill in" markers
if( IsRankingToFillIn(m_CategoryDatas[mc][st][rc].vHighScores[l].sName) )
m_CategoryDatas[mc][st][rc].vHighScores[l].sName = "";
FileWrite( f, m_CategoryDatas[mc][st][rc].vHighScores[l].sName );
FileWrite( f, m_CategoryDatas[mc][st][rc].vHighScores[l].iScore );
FileWrite( f, m_CategoryDatas[mc][st][rc].vHighScores[l].fPercentDP );
}
}
}
}
2003-12-08 10:27:45 +00:00
void ProfileManager::SaveCourseScoresToDir( CString sDir, MemoryCard mc )
{
2003-12-08 10:27:45 +00:00
CString fn = sDir + COURSE_SCORES_FILE;
LOG->Trace("SongManager::SaveCourseScoresToFile");
2003-12-07 08:19:10 +00:00
RageFile f;
if( !f.Open(fn, RageFile::WRITE) )
{
LOG->Warn( "Couldn't open file '%s'", fn.c_str() );
return;
}
FileWrite( f, COURSE_SCORES_VERSION );
vector<Course*> vpCourses;
SONGMAN->GetAllCourses( vpCourses, true );
FileWrite( f, vpCourses.size() );
for( unsigned c=0; c<vpCourses.size(); c++ ) // foreach course
{
Course* pCourse = vpCourses[c];
ASSERT(pCourse);
if( pCourse->m_bIsAutogen )
FileWrite( f, pCourse->m_sName );
else
FileWrite( f, pCourse->m_sPath );
int NumStepsPlayed = 0;
int st;
for( st=0; st<NUM_STEPS_TYPES; st++ )
if( pCourse->m_MemCardDatas[st][mc].iNumTimesPlayed )
++NumStepsPlayed;
FileWrite( f, NumStepsPlayed );
for( st=0; st<NUM_STEPS_TYPES; st++ )
{
if( !pCourse->m_MemCardDatas[st][mc].iNumTimesPlayed )
continue;
--NumStepsPlayed;
FileWrite( f, st );
FileWrite( f, pCourse->m_MemCardDatas[st][mc].iNumTimesPlayed );
FileWrite( f, pCourse->m_MemCardDatas[st][mc].vHighScores.size() );
for( unsigned l=0; l<pCourse->m_MemCardDatas[st][mc].vHighScores.size(); l++ )
{
// tricky: wipe out "name to fill in" markers
if( IsRankingToFillIn(pCourse->m_MemCardDatas[st][mc].vHighScores[l].sName) )
pCourse->m_MemCardDatas[st][mc].vHighScores[l].sName = "";
FileWrite( f, pCourse->m_MemCardDatas[st][mc].vHighScores[l].sName );
FileWrite( f, pCourse->m_MemCardDatas[st][mc].vHighScores[l].iScore );
FileWrite( f, pCourse->m_MemCardDatas[st][mc].vHighScores[l].fPercentDP );
FileWrite( f, pCourse->m_MemCardDatas[st][mc].vHighScores[l].fSurviveTime );
}
}
ASSERT( !NumStepsPlayed );
}
}
2003-12-08 10:27:45 +00:00
void ProfileManager::SaveSongScoresToDir( CString sDir, MemoryCard mc )
{
2003-12-08 10:27:45 +00:00
CString fn = sDir + SONG_SCORES_FILE;
LOG->Trace("SongManager::SaveSongScoresToFile %s", fn.c_str());
2003-12-07 08:19:10 +00:00
RageFile f;
if( !f.Open(fn, RageFile::WRITE) )
{
LOG->Warn( "Couldn't open file '%s'", fn.c_str() );
return;
}
FileWrite( f, SONG_SCORES_VERSION );
const vector<Song*> &vpSongs = SONGMAN->GetAllSongs();
FileWrite( f, vpSongs.size() );
for( unsigned s=0; s<vpSongs.size(); s++ ) // foreach song
{
Song* pSong = vpSongs[s];
ASSERT(pSong);
/* If the song has never been played, don't write anything. This keeps
* us from saving a dozen copies of each song for all autogen difficulties,
* since most people only use a couple game modes. */
vector<Steps*> vNotes;
for( unsigned i=0; i<pSong->m_apNotes.size(); ++i )
{
Steps* pNotes = pSong->m_apNotes[i];
if( pNotes->m_MemCardDatas[mc].iNumTimesPlayed == 0 && pNotes->m_MemCardDatas[mc].vHighScores.empty() )
continue;
vNotes.push_back( pNotes );
}
FileWrite( f, pSong->GetSongDir() );
FileWrite( f, vNotes.size() );
if( vNotes.size() == 0 )
continue; // skip
for( unsigned n=0; n<vNotes.size(); n++ )
{
Steps* pNotes = vNotes[n];
ASSERT(pNotes);
FileWrite( f, pNotes->m_StepsType );
FileWrite( f, pNotes->GetDifficulty() );
FileWrite( f, pNotes->GetDescription() );
FileWrite( f, pNotes->m_MemCardDatas[mc].iNumTimesPlayed );
FileWrite( f, pNotes->m_MemCardDatas[mc].vHighScores.size() );
2003-12-10 09:15:40 +00:00
for( int l=0; l<(int)pNotes->m_MemCardDatas[mc].vHighScores.size(); l++ )
{
// tricky: wipe out "name to fill in" markers
if( IsRankingToFillIn(pNotes->m_MemCardDatas[mc].vHighScores[l].sName) )
pNotes->m_MemCardDatas[mc].vHighScores[l].sName = "";
FileWrite( f, pNotes->m_MemCardDatas[mc].vHighScores[l].sName );
FileWrite( f, pNotes->m_MemCardDatas[mc].vHighScores[l].grade );
FileWrite( f, pNotes->m_MemCardDatas[mc].vHighScores[l].iScore );
FileWrite( f, pNotes->m_MemCardDatas[mc].vHighScores[l].fPercentDP );
}
}
}
}
2003-12-10 05:49:34 +00:00
/* static CString HTMLQuoteDoubleQuotes( CString str )
{
str.Replace( "\"", "&quot;" );
return str;
2003-12-10 05:49:34 +00:00
} */
2003-12-08 10:27:45 +00:00
void ProfileManager::SaveStatsWebPageToDir( CString sDir, MemoryCard mc )
{
2003-12-08 10:27:45 +00:00
CString fn = sDir + STATS_HTML_FILE;
LOG->Trace( "Writing %s ...", fn.c_str() );
2003-12-08 06:41:30 +00:00
//
// Get Profile
//
Profile* pProfile;
if( mc == MEMORY_CARD_MACHINE )
pProfile = GetMachineProfile();
else
pProfile = GetProfile( (PlayerNumber)mc );
ASSERT(pProfile);
//
// Open file
//
RageFile f;
if( !f.Open( fn, RageFile::WRITE ) )
{
LOG->Warn( "Couldn't open file '%s'", fn.c_str() );
return;
}
2003-12-08 06:41:30 +00:00
//
2003-12-09 07:12:28 +00:00
// Gather data
2003-12-08 06:41:30 +00:00
//
vector<Song*> vpSongs = SONGMAN->GetAllSongs();
2003-12-09 07:12:28 +00:00
vector<Steps*> vpAllSteps;
map<Steps*,Song*> mapStepsToSong;
{
for( unsigned i=0; i<vpSongs.size(); i++ )
{
Song* pSong = vpSongs[i];
vector<Steps*> vpSteps = pSong->GetAllSteps();
for( unsigned j=0; j<vpSteps.size(); j++ )
{
Steps* pSteps = vpSteps[j];
if( pSteps->IsAutogen() )
continue; // skip
vpAllSteps.push_back( pSteps );
mapStepsToSong[pSteps] = pSong;
}
}
}
vector<Course*> vpCourses;
SONGMAN->GetAllCourses( vpCourses, false );
2003-12-08 06:41:30 +00:00
//
2003-12-09 07:12:28 +00:00
// Calculate which StepTypes to show
2003-12-08 06:41:30 +00:00
//
2003-12-09 07:12:28 +00:00
vector<StepsType> vStepsTypesToShow;
{
for( StepsType st=(StepsType)0; st<NUM_STEPS_TYPES; st=(StepsType)(st+1) )
{
// don't show if there are no Steps of this StepsType
bool bOneSongHasStepsForThisStepsType = false;
for( unsigned i=0; i<vpSongs.size(); i++ )
{
Song* pSong = vpSongs[i];
vector<Steps*> vpSteps;
pSong->GetSteps( vpSteps, st, DIFFICULTY_INVALID, -1, -1, "", false );
if( !vpSteps.empty() )
{
bOneSongHasStepsForThisStepsType = true;
break;
}
}
if( bOneSongHasStepsForThisStepsType )
vStepsTypesToShow.push_back( st );
}
}
2003-12-08 06:41:30 +00:00
//
2003-12-09 07:12:28 +00:00
// print HTML headers
2003-12-08 06:41:30 +00:00
//
2003-12-09 07:12:28 +00:00
{
f.PutLine( "<html>" );
f.PutLine( "<head>" );
f.PutLine( "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">" );
2003-12-10 05:03:17 +00:00
f.PutLine( ssprintf("<title>%s</title>", STATS_TITLE.c_str() ) );
f.PutLine( ssprintf("<link rel='stylesheet' type='text/css' href='%s'>",STYLE_CSS_FILE) );
2003-12-09 07:12:28 +00:00
f.PutLine( "</head>" );
f.PutLine( "<body>" );
}
2003-12-08 10:27:45 +00:00
2003-12-09 10:20:18 +00:00
#define PRINT_SECTION_START(szName) f.Write( ssprintf("<h2><a name='%s'>"szName"</a> <a href='#top'>(top)</a></h2>\n", szName) )
2003-12-09 07:12:28 +00:00
#define PRINT_SECTION_END f.Write( "\n" )
#define PRINT_DIV_START(szName) f.Write( ssprintf("<div class='section1'>\n" "<h3>%s</h3>\n", szName) )
2003-12-17 05:04:45 +00:00
#define PRINT_DIV_START_ANCHOR(uAnchor,szName) f.Write( ssprintf("<div class='section1'>\n" "<h3><a name='%u'>%s</a></h3>\n", (unsigned)uAnchor, szName) )
2003-12-09 07:12:28 +00:00
#define PRINT_DIV_END f.Write( "</div>\n" )
#define PRINT_DIV2_START(szName) f.Write( ssprintf("<div class='section2'>\n" "<h3>%s</h3>\n", szName) )
2003-12-17 05:04:45 +00:00
#define PRINT_DIV2_START_ANCHOR(uAnchor,szName) f.Write( ssprintf("<div class='section2'>\n" "<h3><a name='%u'>%s</a></h3>\n", (unsigned)uAnchor, szName) )
2003-12-09 07:12:28 +00:00
#define PRINT_DIV2_END f.Write( "</div>\n" )
#define PRINT_LINK(szName,szLink) f.Write( ssprintf("<p><a href='%s'>%s</a></p>\n",szLink,szName) )
#define PRINT_LINE_S(szName,sVal) f.Write( ssprintf("<p>%s = <b>%s</b></p>\n",szName,sVal.c_str()) )
#define PRINT_LINE_B(szName,bVal) f.Write( ssprintf("<p>%s = <b>%s</b></p>\n",szName,(bVal)?"yes":"no") )
#define PRINT_LINE_I(szName,iVal) f.Write( ssprintf("<p>%s = <b>%d</b></p>\n",szName,iVal) )
#define PRINT_LINE_RANK(iRank,sName,iVal) f.Write( ssprintf("<p><b>%d</b> - %s (%d)</p>\n",iRank,sName.c_str(),iVal) )
2003-12-10 06:49:27 +00:00
#define PRINT_LINE_RANK_LINK(iRank,sName,szLink,iVal) f.Write( ssprintf("<p><b>%d</b> - <a href='%s'>%s</a> (%d)</p>\n",iRank,szLink,sName.c_str(),iVal) )
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
//
// Print table of contents
//
{
2003-12-10 05:03:17 +00:00
CString sName =
pProfile->m_sLastUsedHighScoreName.empty() ?
pProfile->m_sName :
pProfile->m_sLastUsedHighScoreName;
time_t ltime = time( NULL );
CString sTime = ctime( &ltime );
f.Write( ssprintf("<h1><a name='top'>%s for %s - %s</a></h1>\n",STATS_TITLE.c_str(), sName.c_str(), sTime.c_str()) );
2003-12-09 10:20:18 +00:00
PRINT_DIV_START("Table of Contents");
PRINT_LINK( "Statistics", "#Statistics" );
2003-12-09 07:12:28 +00:00
PRINT_LINK( "Popularity Lists", "#Popularity Lists" );
PRINT_LINK( "Difficulty Table", "#Difficulty Table" );
2003-12-24 12:51:55 +00:00
PRINT_LINK( "High Scores Table", "#High Scores Table" );
2003-12-09 07:12:28 +00:00
PRINT_LINK( "Song/Steps List", "#Song/Steps List" );
2003-12-10 05:03:17 +00:00
PRINT_LINK( "Bookkeeping", "#Bookkeeping" );
2003-12-09 07:12:28 +00:00
PRINT_DIV_END;
}
//
2003-12-09 10:20:18 +00:00
// Print Statistics
//
LOG->Trace( "Writing stats ..." );
2003-12-09 07:12:28 +00:00
{
2003-12-09 10:20:18 +00:00
PRINT_SECTION_START( "Statistics" );
// Memory card stats
{
2003-12-10 05:03:17 +00:00
PRINT_DIV_START( "This Profile" );
2003-12-09 10:20:18 +00:00
PRINT_LINE_S( "Name", pProfile->m_sName );
PRINT_LINE_S( "LastUsedHighScoreName", pProfile->m_sLastUsedHighScoreName );
PRINT_LINE_B( "UsingProfileDefaultModifiers", pProfile->m_bUsingProfileDefaultModifiers );
PRINT_LINE_S( "DefaultModifiers", pProfile->m_sDefaultModifiers );
PRINT_LINE_I( "TotalPlays", pProfile->m_iTotalPlays );
PRINT_LINE_I( "TotalPlaySeconds", pProfile->m_iTotalPlaySeconds );
PRINT_LINE_I( "TotalGameplaySeconds", pProfile->m_iTotalGameplaySeconds );
PRINT_LINE_I( "CurrentCombo", pProfile->m_iCurrentCombo );
PRINT_DIV_END;
}
// Num Songs Played by PlayMode
{
PRINT_DIV_START( "Num Songs Played by PlayMode" );
for( int i=0; i<NUM_PLAY_MODES; i++ )
PRINT_LINE_I( PlayModeToString((PlayMode)i).c_str(), pProfile->m_iNumSongsPlayedByPlayMode[i] );
PRINT_DIV_END;
}
// Num Songs Played by Style
{
PRINT_DIV_START( "Num Songs Played by Style" );
for( int i=0; i<NUM_STYLES; i++ )
{
Style style = (Style)i;
const StyleDef* pStyleDef = GAMEMAN->GetStyleDefForStyle(style);
StepsType st = pStyleDef->m_StepsType;
if( !pStyleDef->m_bUsedForGameplay )
continue; // skip
// only show if this style plays a StepsType that we're showing
if( find(vStepsTypesToShow.begin(),vStepsTypesToShow.end(),st) == vStepsTypesToShow.end() )
continue; // skip
PRINT_LINE_I( pStyleDef->m_szName, pProfile->m_iNumSongsPlayedByStyle[i] );
}
PRINT_DIV_END;
}
// Num Songs Played by Difficulty
{
PRINT_DIV_START( "Num Songs Played by Difficulty" );
for( int i=0; i<NUM_DIFFICULTIES; i++ )
PRINT_LINE_I( DifficultyToString((Difficulty)i).c_str(), pProfile->m_iNumSongsPlayedByDifficulty[i] );
PRINT_DIV_END;
}
// Num Songs Played by Meter
{
PRINT_DIV_START( "Num Songs Played by Meter" );
for( int i=MAX_METER; i>=MIN_METER; i-- )
PRINT_LINE_I( ssprintf("%d",i).c_str(), pProfile->m_iNumSongsPlayedByMeter[i] );
PRINT_DIV_END;
}
2003-12-09 07:12:28 +00:00
PRINT_SECTION_END;
}
2003-12-08 10:27:45 +00:00
//
2003-12-09 07:12:28 +00:00
// Print Popularity Lists
2003-12-08 10:27:45 +00:00
//
{
2003-12-09 07:12:28 +00:00
PRINT_SECTION_START( "Popularity Lists" );
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
// Songs by popularity
2003-12-08 10:27:45 +00:00
{
2003-12-09 07:12:28 +00:00
unsigned uNumToShow = min( vpSongs.size(), (unsigned)100 );
SortSongPointerArrayByMostPlayed( vpSongs, mc );
PRINT_DIV_START( "Songs by Popularity" );
for( unsigned i=0; i<uNumToShow; i++ )
2003-12-08 10:27:45 +00:00
{
2003-12-09 07:12:28 +00:00
Song* pSong = vpSongs[i];
2003-12-17 05:04:45 +00:00
PRINT_LINE_RANK_LINK( i+1, pSong->GetFullDisplayTitle(), ssprintf("#%u",(unsigned)pSong).c_str(), pSong->GetNumTimesPlayed(mc) );
2003-12-08 10:27:45 +00:00
}
2003-12-09 07:12:28 +00:00
PRINT_DIV_END;
2003-12-08 10:27:45 +00:00
}
2003-12-09 07:12:28 +00:00
// Steps by popularity
{
unsigned uNumToShow = min( vpAllSteps.size(), (unsigned)100 );
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
SortStepsPointerArrayByMostPlayed( vpAllSteps, mc );
PRINT_DIV_START( "Steps by Popularity" );
for( unsigned i=0; i<uNumToShow; i++ )
{
Steps* pSteps = vpAllSteps[i];
Song* pSong = mapStepsToSong[pSteps];
CString s;
s += pSong->GetFullDisplayTitle();
s += " - ";
s += GAMEMAN->NotesTypeToString(pSteps->m_StepsType);
s += " ";
s += DifficultyToString(pSteps->GetDifficulty());
2003-12-17 05:04:45 +00:00
PRINT_LINE_RANK_LINK( i+1, s, ssprintf("#%u",(unsigned)pSteps).c_str(), pSteps->GetNumTimesPlayed(mc) );
2003-12-09 07:12:28 +00:00
}
PRINT_DIV_END;
}
// Course by popularity
2003-12-08 10:27:45 +00:00
{
2003-12-09 07:12:28 +00:00
unsigned uNumToShow = min( vpCourses.size(), (unsigned)100 );
SortCoursePointerArrayByMostPlayed( vpCourses, mc );
PRINT_DIV_START( "Courses by Popularity" );
for( unsigned i=0; i<uNumToShow; i++ )
{
Course* pCourse = vpCourses[i];
2003-12-17 05:04:45 +00:00
PRINT_LINE_RANK_LINK( i+1, pCourse->m_sName, ssprintf("#%u",(unsigned)pCourse).c_str(), pCourse->GetNumTimesPlayed(mc) );
2003-12-09 07:12:28 +00:00
}
PRINT_DIV_END;
2003-12-08 10:27:45 +00:00
}
2003-12-09 10:20:18 +00:00
2003-12-09 07:12:28 +00:00
PRINT_SECTION_END;
}
2003-12-24 12:51:55 +00:00
//
// Print High score tables
//
{
SortSongPointerArrayByGroupAndTitle( vpSongs );
PRINT_SECTION_START( "High Scores Table" );
for( unsigned s=0; s<vStepsTypesToShow.size(); s++ )
{
StepsType st = vStepsTypesToShow[s];
unsigned i;
PRINT_DIV_START( GAMEMAN->NotesTypeToString(st).c_str() );
f.PutLine( "<table border='1' cellpadding='2' cellspacing='0'>\n" );
// table header row
f.Write( "<tr><td>&nbsp;</td>" );
for( unsigned k=0; k<NUM_DIFFICULTIES; k++ )
{
Difficulty d = (Difficulty)k;
f.PutLine( ssprintf("<td>%s</td>", Capitalize(DifficultyToString(d).Left(3)).c_str()) );
}
f.PutLine( "</tr>" );
// table body rows
for( i=0; i<vpSongs.size(); i++ )
{
Song* pSong = vpSongs[i];
f.PutLine( "<tr>" );
f.Write( ssprintf("<td><a href='#%u'>%s</a></td>",
(unsigned)pSong, // use pointer value as the hash
pSong->GetFullDisplayTitle().c_str()) );
for( Difficulty dc=(Difficulty)0; dc<NUM_DIFFICULTIES; dc=(Difficulty)(dc+1) )
{
Steps* pSteps = pSong->GetStepsByDifficulty( st, dc, false );
if( pSteps )
{
CString sHighScore;
if( !pSteps->m_MemCardDatas[mc].vHighScores.empty() )
{
sHighScore += pSteps->m_MemCardDatas[mc].vHighScores[0].sName;
sHighScore += "<br>";
sHighScore += GradeToString( pSteps->m_MemCardDatas[mc].vHighScores[0].grade );
sHighScore += "<br>";
sHighScore += ssprintf("%d",pSteps->m_MemCardDatas[mc].vHighScores[0].iScore);
}
f.PutLine( ssprintf("<td><p align='right'><a href='#%u'>%s</a></p></td>",
(unsigned)pSteps, // use pointer value as the hash
sHighScore.c_str()) );
}
else
{
f.PutLine( "<td>&nbsp;</td>" );
}
}
f.Write( "</tr>" );
}
f.PutLine( "</table>\n" );
PRINT_DIV_END;
}
PRINT_SECTION_END;
}
2003-12-09 07:12:28 +00:00
//
// Print Difficulty tables
//
{
SortSongPointerArrayByGroupAndTitle( vpSongs );
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
PRINT_SECTION_START( "Difficulty Table" );
for( unsigned s=0; s<vStepsTypesToShow.size(); s++ )
2003-12-08 10:27:45 +00:00
{
2003-12-09 07:12:28 +00:00
StepsType st = vStepsTypesToShow[s];
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
unsigned i;
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
PRINT_DIV_START( GAMEMAN->NotesTypeToString(st).c_str() );
f.PutLine( "<table border='1' cellpadding='2' cellspacing='0'>\n" );
// table header row
f.Write( "<tr><td>&nbsp;</td>" );
for( unsigned k=0; k<NUM_DIFFICULTIES; k++ )
2003-12-08 10:27:45 +00:00
{
2003-12-09 07:12:28 +00:00
Difficulty d = (Difficulty)k;
f.PutLine( ssprintf("<td>%s</td>", Capitalize(DifficultyToString(d).Left(3)).c_str()) );
2003-12-08 10:27:45 +00:00
}
2003-12-09 07:12:28 +00:00
f.PutLine( "</tr>" );
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
// table body rows
for( i=0; i<vpSongs.size(); i++ )
{
Song* pSong = vpSongs[i];
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
f.PutLine( "<tr>" );
f.Write( ssprintf("<td><a href='#%u'>%s</a></td>",
2003-12-17 05:04:45 +00:00
(unsigned)pSong, // use pointer value as the hash
2003-12-09 07:12:28 +00:00
pSong->GetFullDisplayTitle().c_str()) );
for( Difficulty dc=(Difficulty)0; dc<NUM_DIFFICULTIES; dc=(Difficulty)(dc+1) )
{
Steps* pSteps = pSong->GetStepsByDifficulty( st, dc, false );
if( pSteps )
2003-12-24 12:51:55 +00:00
{
2003-12-09 07:12:28 +00:00
f.PutLine( ssprintf("<td><p align='right'><a href='#%u'>%d</a></p></td>",
2003-12-17 05:04:45 +00:00
(unsigned)pSteps, // use pointer value as the hash
2003-12-09 07:12:28 +00:00
pSteps->GetMeter()) );
2003-12-24 12:51:55 +00:00
}
2003-12-09 07:12:28 +00:00
else
2003-12-24 12:51:55 +00:00
{
2003-12-09 07:12:28 +00:00
f.PutLine( "<td>&nbsp;</td>" );
2003-12-24 12:51:55 +00:00
}
2003-12-09 07:12:28 +00:00
}
2003-12-08 10:27:45 +00:00
2003-12-09 07:12:28 +00:00
f.Write( "</tr>" );
}
f.PutLine( "</table>\n" );
PRINT_DIV_END;
}
PRINT_SECTION_END;
}
2003-12-08 06:41:30 +00:00
//
// Print song list
//
LOG->Trace( "Writing song list ..." );
{
2003-12-09 07:12:28 +00:00
PRINT_SECTION_START( "Song/Steps List" );
for( unsigned i=0; i<vpSongs.size(); i++ )
2003-12-08 06:41:30 +00:00
{
2003-12-09 07:12:28 +00:00
Song* pSong = vpSongs[i];
vector<Steps*> vpSteps = pSong->GetAllSteps();
/* XXX: We can't call pSong->HasBanner on every song; it'll effectively re-traverse the entire
* song directory tree checking if each banner file really exists.
*
* (Note for testing this: remember that we'll cache directories for a time; this is only slow if
* the directory cache expires before we get here.) */
/* Don't print the song banner anyway since this is going on the memory card. -Chris */
//CString sImagePath = pSong->HasBanner() ? pSong->GetBannerPath() : (pSong->HasBackground() ? pSong->GetBackgroundPath() : "" );
PRINT_DIV_START_ANCHOR( /*Song primary key*/pSong, pSong->GetFullDisplayTitle().c_str() );
PRINT_LINE_S( "Artist", pSong->GetDisplayArtist() );
PRINT_LINE_S( "GroupName", pSong->m_sGroupName );
float fMinBPM, fMaxBPM;
pSong->GetDisplayBPM( fMinBPM, fMaxBPM );
CString sBPM = (fMinBPM==fMaxBPM) ? ssprintf("%.1f",fMinBPM) : ssprintf("%.1f - %.1f",fMinBPM,fMaxBPM);
PRINT_LINE_S( "BPM", sBPM );
PRINT_LINE_I( "NumTimesPlayed", pSong->GetNumTimesPlayed(mc) );
PRINT_LINE_S( "Credit", pSong->m_sCredit );
PRINT_LINE_S( "MusicLength", SecondsToTime(pSong->m_fMusicLengthSeconds) );
PRINT_LINE_B( "Lyrics", !pSong->m_sLyricsFile.empty() );
PRINT_DIV_END;
//
// Print Steps list
//
for( unsigned j=0; j<vpSteps.size(); j++ )
{
Steps* pSteps = vpSteps[j];
if( pSteps->IsAutogen() )
continue; // skip autogen
CString s =
GAMEMAN->NotesTypeToString(pSteps->m_StepsType) +
" - " +
DifficultyToString(pSteps->GetDifficulty());
PRINT_DIV2_START_ANCHOR( /*Steps primary key*/pSteps, s.c_str() ); // use pointer value as the hash
PRINT_LINE_I( "NumTimesPlayed", pSteps->m_MemCardDatas[mc].iNumTimesPlayed );
2003-12-10 06:49:27 +00:00
vector<Steps::MemCardData::HighScore>& vHighScores = pSteps->m_MemCardDatas[mc].vHighScores;
2003-12-10 07:34:08 +00:00
for( unsigned i=0; i<vHighScores.size(); i++ )
2003-12-10 06:49:27 +00:00
{
Steps::MemCardData::HighScore &hs = vHighScores[i];
CString sName = ssprintf("#%d",i+1);
2003-12-10 08:32:16 +00:00
CString sHSName = hs.sName.empty() ? "????" : hs.sName;
CString sValue = ssprintf("%s, %s, %i, %.2f%%", sHSName.c_str(), GradeToString(hs.grade).c_str(), hs.iScore, hs.fPercentDP*100);
2003-12-10 06:49:27 +00:00
PRINT_LINE_S( sName.c_str(), sValue );
}
2003-12-09 07:12:28 +00:00
f.PutLine( "</div>\n" );
PRINT_DIV2_END;
}
2003-12-08 06:41:30 +00:00
}
2003-12-09 07:12:28 +00:00
PRINT_SECTION_END;
}
2003-12-10 05:03:17 +00:00
//
// Print Bookkeeping
//
{
PRINT_SECTION_START( "Bookkeeping" );
// GetCoinsLastDays
{
int coins[NUM_LAST_DAYS];
BOOKKEEPER->GetCoinsLastDays( coins );
PRINT_DIV_START( ssprintf("Coins for Last %d Days",NUM_LAST_DAYS).c_str() );
for( int i=0; i<NUM_LAST_DAYS; i++ )
{
CString sDay = (i==0) ? "Today" : ssprintf("%d day(s) ago",i);
PRINT_LINE_I( sDay.c_str(), coins[i] );
}
PRINT_DIV_END;
}
// GetCoinsLastWeeks
{
int coins[NUM_LAST_WEEKS];
BOOKKEEPER->GetCoinsLastWeeks( coins );
PRINT_DIV_START( ssprintf("Coins for Last %d Weeks",NUM_LAST_WEEKS).c_str() );
for( int i=0; i<NUM_LAST_WEEKS; i++ )
{
CString sWeek = (i==0) ? "This week" : ssprintf("%d week(s) ago",i);
PRINT_LINE_I( sWeek.c_str(), coins[i] );
}
PRINT_DIV_END;
}
2003-12-10 10:30:53 +00:00
// GetCoinsByDayOfWeek
{
int coins[DAYS_IN_WEEK];
BOOKKEEPER->GetCoinsByDayOfWeek( coins );
PRINT_DIV_START( "Coins by Day of Week" );
for( int i=0; i<DAYS_IN_WEEK; i++ )
{
CString sDay = DAY_OF_WEEK_TO_NAME[i];
PRINT_LINE_I( sDay.c_str(), coins[i] );
}
PRINT_DIV_END;
}
2003-12-10 05:03:17 +00:00
// GetCoinsByHour
{
int coins[HOURS_PER_DAY];
BOOKKEEPER->GetCoinsByHour( coins );
2003-12-10 10:30:53 +00:00
PRINT_DIV_START( ssprintf("Coins for Last %d Hours",HOURS_PER_DAY).c_str() );
2003-12-10 05:03:17 +00:00
for( int i=0; i<HOURS_PER_DAY; i++ )
{
CString sHour = ssprintf("hour %d",i);
PRINT_LINE_I( sHour.c_str(), coins[i] );
}
PRINT_DIV_END;
}
PRINT_SECTION_END;
}
PRINT_SECTION_START( "End of File" );
PRINT_SECTION_END;
2003-12-08 10:27:45 +00:00
f.PutLine( "</body>" );
f.PutLine( "</html>" );
//
2003-12-08 10:27:45 +00:00
// Copy CSS file from theme. If the copy fails, oh well...
//
2003-12-10 05:03:17 +00:00
CString sStyleFile = THEME->GetPathToO("ProfileManager style.css");
FileCopy( sStyleFile, sDir+STYLE_CSS_FILE );
LOG->Trace( "Done." );
}
bool ProfileManager::CategoryData::HighScore::operator>=( const HighScore& other ) const
{
if( PREFSMAN->m_bPercentageScoring )
return fPercentDP >= other.fPercentDP;
else
return iScore >= other.iScore;
}