#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" #include "RageFile.h" #include "RageFileManager.h" #include "IniFile.h" #include "GameConstantsAndTypes.h" #include "SongManager.h" #include "GameState.h" #include "song.h" #include "Course.h" #include "GameManager.h" #include "ProductInfo.h" #include "RageUtil.h" #include "ThemeManager.h" #include "Bookkeeper.h" #include 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" #define STYLE_CSS_FILE "style.css" #define NEW_MEM_CARD_NAME "NewCard" #define NEW_PROFILE_NAME "NewProfile" #define SM_300_STATISTICS_FILE BASE_PATH "statistics.ini" #define USER_PROFILES_DIR BASE_PATH "Data" SLASH "LocalProfiles" SLASH #define MACHINE_PROFILE_DIR BASE_PATH "Data" SLASH "MachineProfile" SLASH const int CATEGORY_RANKING_VERSION = 4; const int STEPS_SCORES_VERSION = 8; const int COURSE_SCORES_VERSION = 6; #define STATS_TITLE THEME->GetMetric("ProfileManager","StatsTitle") static const char *MemCardDirs[NUM_PLAYERS] = { /* @ is important; see RageFileManager LoadedDriver::GetPath */ "@mc1" SLASH, "@mc2" SLASH, }; ProfileManager::ProfileManager() { for( int p=0; pm_sMemoryCardDir[p] != "" ) FILEMAN->Mount( "dir", PREFSMAN->m_sMemoryCardDir[p], MemCardDirs[p] ); } InitMachineScoresFromDisk(); } ProfileManager::~ProfileManager() { SaveMachineScoresToDisk(); } void ProfileManager::GetLocalProfileIDs( vector &asProfileIDsOut ) { GetDirListing( USER_PROFILES_DIR "*", asProfileIDsOut, true, false ); } void ProfileManager::GetLocalProfileNames( vector &asNamesOut ) { CStringArray vsProfileIDs; GetLocalProfileIDs( vsProfileIDs ); for( unsigned i=0; iWarn( "Attempting to load profile from '%s' and does not exist", sProfileDir.c_str() ); UnloadProfile( pn ); return false; } // Load scores into SONGMAN PROFILEMAN->ReadCategoryScoresFromDir( m_sProfileDir[pn], (MemoryCard)pn ); PROFILEMAN->ReadSongScoresFromDir( m_sProfileDir[pn], (MemoryCard)pn ); PROFILEMAN->ReadCourseScoresFromDir( m_sProfileDir[pn], (MemoryCard)pn ); // 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 ); } return true; } bool ProfileManager::CreateProfile( CString sProfileDir, CString sName ) { ASSERT( !sName.empty() ); 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 ) { CString sProfileID = PREFSMAN->m_sDefaultLocalProfileID[pn]; if( sProfileID.empty() ) { m_sProfileDir[pn] = ""; return false; } CString sDir = USER_PROFILES_DIR + sProfileID + SLASH; return LoadProfile( pn, sDir, false ); } bool ProfileManager::IsMemoryCardInserted( PlayerNumber pn ) { return FILEMAN->MountpointIsReady( MemCardDirs[pn] ); } bool ProfileManager::LoadProfileFromMemoryCard( PlayerNumber pn ) { CString sDir = MemCardDirs[pn]; if( !FILEMAN->IsMounted(sDir) ) return false; m_bUsingMemoryCard[pn] = true; bool bResult; bResult = LoadProfile( pn, sDir, false ); return bResult; } bool ProfileManager::LoadFirstAvailableProfile( PlayerNumber pn ) { #ifndef _XBOX // mount card if( !PREFSMAN->m_sMemoryCardMountCommand[pn].empty() ) system( PREFSMAN->m_sMemoryCardMountCommand[pn] ); if( IsMemoryCardInserted(pn) ) { if( LoadProfileFromMemoryCard(pn) ) return true; CString sDir = MemCardDirs[pn]; CreateProfile( sDir, NEW_MEM_CARD_NAME ); if( LoadProfileFromMemoryCard(pn) ) return true; } #endif if( LoadDefaultProfileFromMachine(pn) ) return true; return false; } bool ProfileManager::SaveProfile( PlayerNumber pn ) { if( m_sProfileDir[pn].empty() ) return false; m_Profile[pn].SaveToIni( m_sProfileDir[pn]+PROFILE_FILE ); SaveCategoryScoresToDir( m_sProfileDir[pn], (MemoryCard)pn ); SaveSongScoresToDir( m_sProfileDir[pn], (MemoryCard)pn ); SaveCourseScoresToDir( m_sProfileDir[pn], (MemoryCard)pn ); SaveStatsWebPageToDir( m_sProfileDir[pn], (MemoryCard)pn ); return true; } void ProfileManager::UnloadProfile( PlayerNumber pn ) { m_sProfileDir[pn] = ""; m_bUsingMemoryCard[pn] = false; m_Profile[pn].Init(); } Profile* ProfileManager::GetProfile( PlayerNumber pn ) { if( m_sProfileDir[pn].empty() ) return NULL; else return &m_Profile[pn]; } bool Profile::LoadFromIni( CString sIniPath ) { Init(); CStringArray asBits; split( Dirname(sIniPath), SLASH, asBits, true ); CString sLastDir = asBits.back(); // this is a number name, e.g. "0000001" // Fill in a default value in case ini doesn't have it. m_sName = NEW_PROFILE_NAME; // // 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 ); ini.GetValue( "Profile", "CurrentCombo", m_iCurrentCombo ); unsigned i; for( i=0; iGetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByStyle[i] ); for( i=0; iGetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByDifficulty[i] ); for( i=0; iGetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByMeter[i] ); return true; } bool Profile::SaveToIni( CString sIniPath ) { 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 ); ini.SetValue( "Profile", "CurrentCombo", m_iCurrentCombo ); unsigned i; for( i=0; iGetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByStyle[i] ); for( i=0; iGetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByDifficulty[i] ); for( i=0; iGetStyleDefForStyle((Style)i)->m_szName), m_iNumSongsPlayedByMeter[i] ); ini.WriteFile(); return true; } bool ProfileManager::CreateLocalProfile( CString sName ) { ASSERT( !sName.empty() ); // // Find a free directory name in the profiles directory // CString sProfileID, sProfileDir; const int MAX_TRIES = 1000; int i; for( i=0; i= 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; } void ProfileManager::ReadSongScoresFromDir( CString sDir, MemoryCard mc ) { CString fn = sDir + SONG_SCORES_FILE; RageFile f; if( !f.Open(fn, RageFile::READ) ) { LOG->Warn( "Couldn't open file '%s'", fn.c_str() ); return; } int version; if( !FileRead(f, version) ) WARN_AND_RETURN; if( version != STEPS_SCORES_VERSION ) WARN_AND_RETURN; int iNumSongs; if( !FileRead(f, iNumSongs) ) WARN_AND_RETURN; for( int s=0; sGetSongFromDir( sSongDir ); int iNumNotes; if( !FileRead(f, iNumNotes) ) WARN_AND_RETURN; for( int n=0; nGetStepsByDescription( 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; if( pNotes ) pNotes->m_MemCardDatas[mc].vHighScores.resize(NUM_RANKING_LINES); for( int l=0; lm_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; } } } } } void ProfileManager::ReadCategoryScoresFromDir( CString sDir, MemoryCard mc ) { CString fn = sDir + CATEGORY_SCORES_FILE; RageFile f; if( !f.Open(fn, RageFile::READ) ) { LOG->Warn( "Couldn't open file '%s'", fn.c_str() ); return; } int version; if( !FileRead(f, version) ) WARN_AND_RETURN; if( version != CATEGORY_RANKING_VERSION ) WARN_AND_RETURN; for( int st=0; stWarn( "Couldn't open file '%s'", fn.c_str() ); 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; cGetCourseFromPath( 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; if( pCourse ) pCourse->m_MemCardDatas[st][mc].vHighScores.resize(NUM_RANKING_LINES); for( int l=0; lm_MemCardDatas[st][mc].vHighScores[l].sName = sName; pCourse->m_MemCardDatas[st][mc].vHighScores[l].iScore = iScore; pCourse->m_MemCardDatas[st][mc].vHighScores[l].fSurviveTime = fSurviveTime; } } } } } void ProfileManager::InitMachineScoresFromDisk() { // read old style notes scores ReadSM300NoteScores(); // category ranking ReadCategoryScoresFromDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE ); ReadSongScoresFromDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE ); ReadCourseScoresFromDir( MACHINE_PROFILE_DIR, MEMORY_CARD_MACHINE ); 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 ); } } } void ProfileManager::SaveCategoryScoresToDir( CString sDir, MemoryCard mc ) { CString fn = sDir + CATEGORY_SCORES_FILE; LOG->Trace("SongManager::SaveCategoryRankingsToFile"); 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; stTrace("SongManager::SaveCourseScoresToFile"); 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 vpCourses; SONGMAN->GetAllCourses( vpCourses, true ); FileWrite( f, vpCourses.size() ); for( unsigned c=0; cm_bIsAutogen ) FileWrite( f, pCourse->m_sName ); else FileWrite( f, pCourse->m_sPath ); int NumStepsPlayed = 0; int st; for( st=0; stm_MemCardDatas[st][mc].iNumTimesPlayed ) ++NumStepsPlayed; FileWrite( f, NumStepsPlayed ); for( st=0; stm_MemCardDatas[st][mc].iNumTimesPlayed ) continue; --NumStepsPlayed; FileWrite( f, st ); FileWrite( f, pCourse->m_MemCardDatas[st][mc].iNumTimesPlayed ); pCourse->m_MemCardDatas[st][mc].vHighScores.resize(NUM_RANKING_LINES); for( int l=0; lm_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].fSurviveTime ); } } ASSERT( !NumStepsPlayed ); } } void ProfileManager::SaveSongScoresToDir( CString sDir, MemoryCard mc ) { CString fn = sDir + SONG_SCORES_FILE; LOG->Trace("SongManager::SaveSongScoresToFile %s", fn.c_str()); RageFile f; if( !f.Open(fn, RageFile::WRITE) ) { LOG->Warn( "Couldn't open file '%s'", fn.c_str() ); return; } FileWrite( f, STEPS_SCORES_VERSION ); const vector &vpSongs = SONGMAN->GetAllSongs(); FileWrite( f, vpSongs.size() ); for( unsigned s=0; s vNotes; for( unsigned i=0; im_apNotes.size(); ++i ) { Steps* pNotes = pSong->m_apNotes[i]; if( !pNotes->m_MemCardDatas[mc].iNumTimesPlayed ) continue; vNotes.push_back( pNotes ); } FileWrite( f, pSong->GetSongDir() ); FileWrite( f, vNotes.size() ); if( vNotes.size() == 0 ) continue; // skip for( unsigned n=0; nm_StepsType ); FileWrite( f, pNotes->GetDifficulty() ); FileWrite( f, pNotes->GetDescription() ); FileWrite( f, pNotes->m_MemCardDatas[mc].iNumTimesPlayed ); pNotes->m_MemCardDatas[mc].vHighScores.resize(NUM_RANKING_LINES); for( int l=0; lm_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 ); } } } } /* static CString HTMLQuoteDoubleQuotes( CString str ) { str.Replace( "\"", """ ); return str; } */ void ProfileManager::SaveStatsWebPageToDir( CString sDir, MemoryCard mc ) { CString fn = sDir + STATS_HTML_FILE; // // 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; } // // Gather data // vector vpSongs = SONGMAN->GetAllSongs(); vector vpAllSteps; map mapStepsToSong; { for( unsigned i=0; i vpSteps = pSong->GetAllSteps(); for( unsigned j=0; jIsAutogen() ) continue; // skip vpAllSteps.push_back( pSteps ); mapStepsToSong[pSteps] = pSong; } } } vector vpCourses; SONGMAN->GetAllCourses( vpCourses, false ); // // Calculate which StepTypes to show // vector vStepsTypesToShow; { for( StepsType st=(StepsType)0; st vpSteps; pSong->GetSteps( vpSteps, st, DIFFICULTY_INVALID, -1, -1, "", false ); if( !vpSteps.empty() ) { bOneSongHasStepsForThisStepsType = true; break; } } if( bOneSongHasStepsForThisStepsType ) vStepsTypesToShow.push_back( st ); } } // // print HTML headers // { f.PutLine( "" ); f.PutLine( "" ); f.PutLine( "" ); f.PutLine( ssprintf("%s", STATS_TITLE.c_str() ) ); f.PutLine( ssprintf("",STYLE_CSS_FILE) ); f.PutLine( "" ); f.PutLine( "" ); } #define PRINT_SECTION_START(szName) f.Write( ssprintf("

"szName" (top)

\n", szName) ) #define PRINT_SECTION_END f.Write( "\n" ) #define PRINT_DIV_START(szName) f.Write( ssprintf("
\n" "

%s

\n", szName) ) #define PRINT_DIV_START_ANCHOR(uAnchor,szName) f.Write( ssprintf("
\n" "

%s

\n", uAnchor, szName) ) #define PRINT_DIV_END f.Write( "
\n" ) #define PRINT_DIV2_START(szName) f.Write( ssprintf("
\n" "

%s

\n", szName) ) #define PRINT_DIV2_START_ANCHOR(uAnchor,szName) f.Write( ssprintf("
\n" "

%s

\n", uAnchor, szName) ) #define PRINT_DIV2_END f.Write( "
\n" ) #define PRINT_LINK(szName,szLink) f.Write( ssprintf("

%s

\n",szLink,szName) ) #define PRINT_LINE_S(szName,sVal) f.Write( ssprintf("

%s = %s

\n",szName,sVal.c_str()) ) #define PRINT_LINE_B(szName,bVal) f.Write( ssprintf("

%s = %s

\n",szName,(bVal)?"yes":"no") ) #define PRINT_LINE_I(szName,iVal) f.Write( ssprintf("

%s = %d

\n",szName,iVal) ) #define PRINT_LINE_RANK(iRank,sName,iVal) f.Write( ssprintf("

%d - %s (%d)

\n",iRank,sName.c_str(),iVal) ) // // Print table of contents // { CString sName = pProfile->m_sLastUsedHighScoreName.empty() ? pProfile->m_sName : pProfile->m_sLastUsedHighScoreName; time_t ltime = time( NULL ); CString sTime = ctime( <ime ); f.Write( ssprintf("

%s for %s - %s

\n",STATS_TITLE.c_str(), sName.c_str(), sTime.c_str()) ); PRINT_DIV_START("Table of Contents"); PRINT_LINK( "Statistics", "#Statistics" ); PRINT_LINK( "Popularity Lists", "#Popularity Lists" ); PRINT_LINK( "Difficulty Table", "#Difficulty Table" ); PRINT_LINK( "Song/Steps List", "#Song/Steps List" ); PRINT_LINK( "Bookkeeping", "#Bookkeeping" ); PRINT_DIV_END; } // // Print Statistics // { PRINT_SECTION_START( "Statistics" ); // Memory card stats { PRINT_DIV_START( "This Profile" ); 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; im_iNumSongsPlayedByPlayMode[i] ); PRINT_DIV_END; } // Num Songs Played by Style { PRINT_DIV_START( "Num Songs Played by Style" ); for( int i=0; iGetStyleDefForStyle(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; im_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; } PRINT_SECTION_END; } // // Print Popularity Lists // { PRINT_SECTION_START( "Popularity Lists" ); // Songs by popularity { unsigned uNumToShow = min( vpSongs.size(), (unsigned)100 ); SortSongPointerArrayByMostPlayed( vpSongs, mc ); PRINT_DIV_START( "Songs by Popularity" ); for( unsigned i=0; iGetFullDisplayTitle(), pSong->GetNumTimesPlayed(mc) ); } PRINT_DIV_END; } // Steps by popularity { unsigned uNumToShow = min( vpAllSteps.size(), (unsigned)100 ); SortStepsPointerArrayByMostPlayed( vpAllSteps, mc ); PRINT_DIV_START( "Steps by Popularity" ); for( unsigned i=0; iGetFullDisplayTitle(); s += " - "; s += GAMEMAN->NotesTypeToString(pSteps->m_StepsType); s += " "; s += DifficultyToString(pSteps->GetDifficulty()); PRINT_LINE_RANK( i+1, s, pSteps->GetNumTimesPlayed(mc) ); } PRINT_DIV_END; } // Course by popularity { unsigned uNumToShow = min( vpCourses.size(), (unsigned)100 ); SortCoursePointerArrayByMostPlayed( vpCourses, mc ); PRINT_DIV_START( "Courses by Popularity" ); for( unsigned i=0; im_sName, pCourse->GetNumTimesPlayed(mc) ); } PRINT_DIV_END; } PRINT_SECTION_END; } // // Print Difficulty tables // { SortSongPointerArrayByGroupAndTitle( vpSongs ); PRINT_SECTION_START( "Difficulty Table" ); for( unsigned s=0; sNotesTypeToString(st).c_str() ); f.PutLine( "\n" ); // table header row f.Write( "" ); for( unsigned k=0; k%s", Capitalize(DifficultyToString(d).Left(3)).c_str()) ); } f.PutLine( "" ); // table body rows for( i=0; i" ); f.Write( ssprintf("", pSong, // use pointer value as the hash pSong->GetFullDisplayTitle().c_str()) ); for( Difficulty dc=(Difficulty)0; dcGetStepsByDifficulty( st, dc, false ); if( pSteps ) f.PutLine( ssprintf("", pSteps, // use pointer value as the hash pSteps->GetMeter()) ); else f.PutLine( "" ); } f.Write( "" ); } f.PutLine( "
 
%s

%d

 
\n" ); PRINT_DIV_END; } PRINT_SECTION_END; } // // Print song list // { PRINT_SECTION_START( "Song/Steps List" ); for( unsigned i=0; i 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; jIsAutogen() ) 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 ); f.PutLine( "
\n" ); PRINT_DIV2_END; } } PRINT_SECTION_END; } // // 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; iGetCoinsLastWeeks( coins ); PRINT_DIV_START( ssprintf("Coins for Last %d Weeks",NUM_LAST_WEEKS).c_str() ); for( int i=0; iGetCoinsByHour( coins ); PRINT_DIV_START( ssprintf("Coins for Last %d Weeks",HOURS_PER_DAY).c_str() ); for( int i=0; i" ); f.PutLine( "" ); // // Copy CSS file from theme. If the copy fails, oh well... // CString sStyleFile = THEME->GetPathToO("ProfileManager style.css"); CopyFile2( sStyleFile, sDir+STYLE_CSS_FILE ); }