From 3906ea166fa17cf783bf45fc2130cdda57ad5ad4 Mon Sep 17 00:00:00 2001 From: Chris Danford Date: Mon, 16 Feb 2004 02:57:55 +0000 Subject: [PATCH] Profile HTML stats overhaul --- stepmania/src/Makefile.am | 2 +- stepmania/src/Profile.cpp | 514 +--------------------- stepmania/src/Profile.h | 1 + stepmania/src/ProfileHtml.cpp | 749 +++++++++++++++++++++++++++++++++ stepmania/src/ProfileHtml.h | 20 + stepmania/src/StepMania.dsp | 23 +- stepmania/src/StepMania.vcproj | 10 +- 7 files changed, 807 insertions(+), 512 deletions(-) create mode 100644 stepmania/src/ProfileHtml.cpp create mode 100644 stepmania/src/ProfileHtml.h diff --git a/stepmania/src/Makefile.am b/stepmania/src/Makefile.am index 83cc1f03c0..9ec9aa0a15 100644 --- a/stepmania/src/Makefile.am +++ b/stepmania/src/Makefile.am @@ -62,7 +62,7 @@ NoteData.cpp NoteData.h NoteDataUtil.cpp NoteDataUtil.h NoteDataWithScoring.cpp NoteFieldPositioning.cpp NoteFieldPositioning.h NoteTypes.cpp NoteTypes.h NotesLoader.cpp NotesLoader.h \ NotesLoaderBMS.cpp NotesLoaderBMS.h NotesLoaderDWI.cpp NotesLoaderDWI.h NotesLoaderKSF.cpp NotesLoaderKSF.h \ NotesLoaderSM.cpp NotesLoaderSM.h NotesWriterDWI.cpp NotesWriterDWI.h NotesWriterSM.cpp NotesWriterSM.h \ -PlayerAI.cpp PlayerAI.h PlayerNumber.cpp PlayerNumber.h PlayerOptions.cpp PlayerOptions.h Profile.cpp Profile.h RandomSample.cpp RandomSample.h \ +PlayerAI.cpp PlayerAI.h PlayerNumber.cpp PlayerNumber.h PlayerOptions.cpp PlayerOptions.h Profile.cpp Profile.h ProfileHtml.cpp ProfileHtml.h RandomSample.cpp RandomSample.h \ ScoreKeeper.h ScoreKeeperMAX2.cpp ScoreKeeperMAX2.h ScoreKeeperRave.cpp ScoreKeeperRave.h \ Song.cpp song.h SongCacheIndex.cpp SongCacheIndex.h SongOptions.cpp SongOptions.h StageStats.cpp StageStats.h Steps.cpp Steps.h \ Style.h StyleDef.cpp StyleDef.h StyleInput.h TimingData.cpp TimingData.h TitleSubstitution.cpp TitleSubstitution.h \ diff --git a/stepmania/src/Profile.cpp b/stepmania/src/Profile.cpp index e4891d170e..503aabe42c 100644 --- a/stepmania/src/Profile.cpp +++ b/stepmania/src/Profile.cpp @@ -24,9 +24,10 @@ #include "Course.h" #include #include "ThemeManager.h" -#include "Bookkeeper.h" #include "CryptManager.h" #include "PrefsManager.h" +#include "ProfileHtml.h" +#include "ProfileManager.h" // // Old file versions for backward compatibility @@ -47,14 +48,10 @@ const int SM_390A12_COURSE_SCORES_VERSION = 8; #define CATEGORY_SCORES_XML "CategoryScores.xml" #define SONG_SCORES_XML "SongScores.xml" #define COURSE_SCORES_XML "CourseScores.xml" -#define STATS_HTML "stats.html" -#define STYLE_CSS "style.css" #define DEFAULT_PROFILE_NAME "" -#define STATS_TITLE THEME->GetMetric("ProfileManager","StatsTitle") - void Profile::InitGeneralData() { @@ -989,510 +986,13 @@ void Profile::DeleteCategoryScoresFromDirSM390a12( CString sDir ) const void Profile::SaveStatsWebPageToDir( CString sDir ) const { - const Profile* pProfile = this; - - CString fn = sDir + STATS_HTML; - - LOG->Trace( "Writing %s ...", fn.c_str() ); - - // - // Open file - // - RageFile f; - if( !f.Open( fn, RageFile::WRITE ) ) + if( m_bIsMachineProfile ) { - LOG->Warn( "Couldn't open file '%s'", fn.c_str() ); - return; + SaveMachineHtmlToDir( sDir, this ); +// SavePlayerHtmlToDir( sDir, this, PROFILEMAN->GetMachineProfile() ); // remove this when done debugging } - - // - // Gather data - // - vector vpSongs = SONGMAN->GetAllSongs(); - vector vpAllSteps; - map mapStepsToSong; + else { - for( unsigned i=0; i vpSteps = pSong->GetAllSteps(); - for( unsigned j=0; jIsAutogen() ) - continue; // skip - vpAllSteps.push_back( pSteps ); - mapStepsToSong[pSteps] = pSong; - } - } + SavePlayerHtmlToDir( sDir, this, PROFILEMAN->GetMachineProfile() ); } - 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) ); - 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", (unsigned)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", (unsigned)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) ) -#define PRINT_LINE_RANK_LINK(iRank,sName,szLink,iVal) f.Write( ssprintf("

%d - %s (%d)

\n",iRank,szLink,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( "High Scores Table", "#High Scores Table" ); - PRINT_LINK( "Song/Steps List", "#Song/Steps List" ); - PRINT_LINK( "Bookkeeping", "#Bookkeeping" ); - PRINT_DIV_END; - } - - // - // Print Statistics - // - LOG->Trace( "Writing stats ..." ); - { - 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, this ); - PRINT_DIV_START( "Songs by Popularity" ); - for( unsigned i=0; iGetFullDisplayTitle(), ssprintf("#%u",(unsigned)pSong).c_str(), this->GetSongNumTimesPlayed(pSong) ); - } - PRINT_DIV_END; - } - - // Steps by popularity - { - unsigned uNumToShow = min( vpAllSteps.size(), (unsigned)100 ); - - SortStepsPointerArrayByMostPlayed( vpAllSteps, this ); - 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_LINK( i+1, s, ssprintf("#%u",(unsigned)pSteps).c_str(), pProfile->GetStepsNumTimesPlayed(pSteps) ); - } - PRINT_DIV_END; - } - - // Course by popularity - { - unsigned uNumToShow = min( vpCourses.size(), (unsigned)100 ); - - SortCoursePointerArrayByMostPlayed( vpCourses, this ); - PRINT_DIV_START( "Courses by Popularity" ); - for( unsigned i=0; im_sName, ssprintf("#%u",(unsigned)pCourse).c_str(), pProfile->GetCourseNumTimesPlayed(pCourse) ); - } - PRINT_DIV_END; - } - - PRINT_SECTION_END; - } - - // - // Print High score tables - // - { - SortSongPointerArrayByGroupAndTitle( vpSongs ); - - PRINT_SECTION_START( "High Scores 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("", - (unsigned)pSong, // use poister value as the hash - pSong->GetFullDisplayTitle().c_str()) ); - - for( Difficulty dc=(Difficulty)0; dcGetStepsByDifficulty( st, dc, false ); - if( pSteps ) - { - const HighScoreList &hsl = pProfile->GetStepsHighScoreList( pSteps ); - CString sHighScore; - if( !hsl.vHighScores.empty() ) - { - sHighScore += hsl.vHighScores[0].sName; - sHighScore += "
"; - sHighScore += GradeToString( hsl.vHighScores[0].grade ); - sHighScore += "
"; - sHighScore += ssprintf("%d",hsl.vHighScores[0].iScore); - } - f.PutLine( ssprintf("
", - (unsigned)pSteps, // use poister value as the hash - sHighScore.c_str()) ); - } - else - { - f.PutLine( "" ); - } - } - - f.Write( "" ); - } - - f.PutLine( "
 
%s

%s

 
\n" ); - 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("", - (unsigned)pSong, // use poister value as the hash - pSong->GetFullDisplayTitle().c_str()) ); - - for( Difficulty dc=(Difficulty)0; dcGetStepsByDifficulty( st, dc, false ); - if( pSteps ) - { - f.PutLine( ssprintf("", - (unsigned)pSteps, // use poister 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 - // - LOG->Trace( "Writing 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 estire - * 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 prist 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", this->GetSongNumTimesPlayed(pSong) ); - 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 - const HighScoreList &hsl = pProfile->GetStepsHighScoreList( pSteps ); - CString s = - GAMEMAN->NotesTypeToString(pSteps->m_StepsType) + - " - " + - DifficultyToString(pSteps->GetDifficulty()); - PRINT_DIV2_START_ANCHOR( /*Steps primary key*/pSteps, s.c_str() ); // use poister value as the hash - PRINT_LINE_I( "NumTimesPlayed", hsl.iNumTimesPlayed ); - - for( unsigned i=0; i\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; iGetCoinsByDayOfWeek( coins ); - PRINT_DIV_START( "Coins by Day of Week" ); - for( int i=0; iGetCoinsByHour( coins ); - PRINT_DIV_START( ssprintf("Coins for Last %d Hours",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"); - FileCopy( sStyleFile, sDir+STYLE_CSS ); - LOG->Trace( "Done." ); } diff --git a/stepmania/src/Profile.h b/stepmania/src/Profile.h index a3773a6d77..3b9983ea8e 100644 --- a/stepmania/src/Profile.h +++ b/stepmania/src/Profile.h @@ -43,6 +43,7 @@ public: // // General data // + bool m_bIsMachineProfile; CString m_sName; CString m_sLastUsedHighScoreName; bool m_bUsingProfileDefaultModifiers; diff --git a/stepmania/src/ProfileHtml.cpp b/stepmania/src/ProfileHtml.cpp new file mode 100644 index 0000000000..e25880c107 --- /dev/null +++ b/stepmania/src/ProfileHtml.cpp @@ -0,0 +1,749 @@ +#include "global.h" +/* +----------------------------------------------------------------------------- + Class: ProfileHtml + + Desc: See header. + + Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. + Chris Danford +----------------------------------------------------------------------------- +*/ + +#include "ProfileHtml.h" +#include "ThemeManager.h" +#include "RageFile.h" +#include "RageLog.h" +#include "SongManager.h" +#include "song.h" +#include "Steps.h" +#include +#include "GameManager.h" +#include "Course.h" +#include "Bookkeeper.h" + + +#define STATS_HTML "stats.html" +#define STYLE_CSS "style.css" + +#define STATS_TITLE THEME->GetMetric("ProfileManager","StatsTitle") + + +static int g_Level = 1; + +inline CString MakeUniqueId() { CString s="id"+ssprintf("%d%d%d",rand(),rand(),rand()); return s; } +inline void PRINT_OPEN(RageFile &f,CString sName,bool bExpanded,CString sID){ g_Level++; ASSERT(g_Level>0 && g_Level<6); f.Write( ssprintf("
\n" "%s\n" "
\n", g_Level, g_Level, sID.c_str(), sName.c_str(), g_Level, sID.c_str(), bExpanded?"visibletext":"hiddentext") ); } +inline void PRINT_OPEN(RageFile &f,CString sName,bool bExpanded=false) { PRINT_OPEN(f,sName,bExpanded,MakeUniqueId()); } +inline void PRINT_CLOSE(RageFile &f) { f.Write( "
\n" "
\n" ); g_Level--; ASSERT(g_Level>=0); } +//inline void PRINT_LINK(RageFile &f,CString sName,CString sLink) { f.Write( ssprintf("

%s

\n",sLink.c_str(),sName.c_str()) ); } +//inline void PRINT_LINE_S(RageFile &f,CString sName,CString sVal) { f.Write( ssprintf("

%s = %s

\n",sName.c_str(),sVal.c_str()) ); } +//inline void PRINT_LINE_B(RageFile &f,CString sName,bool bVal) { f.Write( ssprintf("

%s = %s

\n",sName.c_str(),(bVal)?"yes":"no") ); } +//inline void PRINT_LINE_I(RageFile &f,CString sName,int iVal) { f.Write( ssprintf("

%s = %d

\n",sName.c_str(),iVal) ); } +//inline void PRINT_LINE_RANK(RageFile &f,int iRank,CString sName,int iVal) { f.Write( ssprintf("

%d - %s (%d)

\n",iRank,sName.c_str(),iVal) ); } +//inline void PRINT_LINE_RANK_LINK(RageFile &f,int iRank,CString sName,CString sLink,int iVal) { f.Write( ssprintf("

%d - %s (%d)

\n",iRank,sLink.c_str(),sName.c_str(),iVal) ); } + + +struct Table +{ + Table() {} + + struct Line + { + Line() {} + Line(CString n,CString v) { sName = n; sValue = v; } + Line(CString n,bool v) { sName = n; sValue = ssprintf("%s",v?"yes":"no"); } + Line(CString n,int v) { sName = n; sValue = ssprintf("%d",v); } + Line(int r,CString n,int v) { sRank = ssprintf("%d",r); sName = n; sValue = ssprintf("%d",v); } + + CString sRank; + CString sName; + CString sValue; + }; + + int iNumCols; + vector vLines; +}; + +//inline CString MAKE_LINE_S(CString sName,CString sVal) { return ssprintf("

%s = %s

\n",sName.c_str(),sVal.c_str()); } +//inline CString MAKE_LINE_B(CString sName,bool bVal) { return ssprintf("

%s = %s

\n",sName.c_str(),(bVal)?"yes":"no"); } +//inline CString MAKE_LINE_I(CString sName,int iVal) { return ssprintf("

%s = %d

\n",sName.c_str(),iVal); } +//inline CString MAKE_LINE_RANK(int iRank,CString sName,int iVal) { return ssprintf("

%d - %s (%d)

\n",iRank,sName.c_str(),iVal); } + +#define BEGIN_TABLE(cols) { Table table; table.iNumCols=cols; +#define TABLE_LINE2(p1,p2) table.vLines.push_back( Table::Line(p1,p2) ); +#define TABLE_LINE3(p1,p2,p3) table.vLines.push_back( Table::Line(p1,p2,p3) ); +#define END_TABLE PrintTable( f, table ); } + +inline void PrintTable(RageFile &f,Table &table) +{ + const vector &vLines = table.vLines; + int &iNumCols = table.iNumCols; + + ASSERT( iNumCols > 0 ); + + if( vLines.empty() ) + return; + + bool bPrintRank = !vLines.empty() && !vLines[0].sRank.empty(); + + int iMaxItemsPerCol = (vLines.size()+iNumCols-1) / iNumCols; + iNumCols = (vLines.size()+iMaxItemsPerCol-1) / iMaxItemsPerCol; // round up + f.Write("\n"); + for( int col=0; col\n"); + + int iStartItem = col*iMaxItemsPerCol; + + f.Write("
\n"); + for( int i=iStartItem; i"); + + const Table::Line& line = (i"); + f.Write( line.sRank ); + f.Write(""); + f.Write(""); + } + f.Write(""); + f.Write(""); + f.Write(""); + f.Write( "\n" ); + + f.Write(""); + } + f.Write("
 "); + f.Write( line.sName ); + f.Write(" "); + f.Write( line.sValue ); + f.Write("
\n"); + + f.Write("\n"); + } + f.Write("\n"); +} + +void PrintStatistics( RageFile &f, const Profile *pProfile, CString sTitle, vector &vpSongs, vector &vpAllSteps, vector &vStepsTypesToShow, map mapStepsToSong, vector vpCourses ) +{ + PRINT_OPEN(f,sTitle); + { + PRINT_OPEN(f,"This Profile",true); + { + BEGIN_TABLE(2); + TABLE_LINE2( "Name", pProfile->m_sName ); + TABLE_LINE2( "LastUsedHighScoreName", pProfile->m_sLastUsedHighScoreName ); + TABLE_LINE2( "UsingProfileDefaultModifiers", pProfile->m_bUsingProfileDefaultModifiers ); + TABLE_LINE2( "DefaultModifiers", pProfile->m_sDefaultModifiers ); + TABLE_LINE2( "TotalPlays", pProfile->m_iTotalPlays ); + TABLE_LINE2( "TotalPlaySeconds", pProfile->m_iTotalPlaySeconds ); + TABLE_LINE2( "TotalGameplaySeconds", pProfile->m_iTotalGameplaySeconds ); + TABLE_LINE2( "CurrentCombo", pProfile->m_iCurrentCombo ); + END_TABLE; + } + PRINT_CLOSE(f); + + + PRINT_OPEN(f,"Num Songs Played by PlayMode",true); + { + BEGIN_TABLE(4); + FOREACH_PlayMode( pm ) + TABLE_LINE2( PlayModeToString(pm), pProfile->m_iNumSongsPlayedByPlayMode[pm] ); + END_TABLE; + } + PRINT_CLOSE(f); + + + PRINT_OPEN(f,"Num Songs Played by Style",true); + { + BEGIN_TABLE(4); + 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 + TABLE_LINE2( pStyleDef->m_szName, pProfile->m_iNumSongsPlayedByStyle[i] ); + } + END_TABLE; + } + PRINT_CLOSE(f); + + + PRINT_OPEN(f,"Num Songs Played by Difficulty",true); + { + BEGIN_TABLE(4); + FOREACH_Difficulty( dc ) + TABLE_LINE2( DifficultyToString(dc), pProfile->m_iNumSongsPlayedByDifficulty[dc] ); + END_TABLE; + } + PRINT_CLOSE(f); + + PRINT_OPEN(f,"Num Songs Played by Meter",true); + { + BEGIN_TABLE(4); + for( int i=MIN_METER; i<=MAX_METER; i++ ) + TABLE_LINE2( ssprintf("Meter %d",i), pProfile->m_iNumSongsPlayedByMeter[i] ); + END_TABLE; + } + PRINT_CLOSE(f); + } + PRINT_CLOSE(f); +} + +void PrintPopularity( RageFile &f, const Profile *pProfile, CString sTitle, vector &vpSongs, vector &vpAllSteps, vector &vStepsTypesToShow, map mapStepsToSong, vector vpCourses ) +{ + PRINT_OPEN(f, sTitle ); + { + // Songs by popularity + { + unsigned uNumToShow = min( vpSongs.size(), (unsigned)100 ); + + SortSongPointerArrayByMostPlayed( vpSongs, pProfile ); + PRINT_OPEN(f, "Songs by Popularity" ); + { + BEGIN_TABLE(1); + for( unsigned i=0; iGetFullDisplayTitle(), pProfile->GetSongNumTimesPlayed(pSong) ); + } + END_TABLE; + } + PRINT_CLOSE(f); + } + + // Steps by popularity + { + unsigned uNumToShow = min( vpAllSteps.size(), (unsigned)100 ); + + SortStepsPointerArrayByMostPlayed( vpAllSteps, pProfile ); + PRINT_OPEN(f, "Steps by Popularity" ); + { + BEGIN_TABLE(1); + for( unsigned i=0; iGetFullDisplayTitle(); + s += " - "; + s += GAMEMAN->NotesTypeToString(pSteps->m_StepsType); + s += " "; + s += DifficultyToString(pSteps->GetDifficulty()); + TABLE_LINE3(i+1, s, pProfile->GetStepsNumTimesPlayed(pSteps) ); + } + END_TABLE; + } + PRINT_CLOSE(f); + } + + // Course by popularity + { + unsigned uNumToShow = min( vpCourses.size(), (unsigned)100 ); + + SortCoursePointerArrayByMostPlayed( vpCourses, pProfile ); + PRINT_OPEN(f, "Courses by Popularity" ); + { + BEGIN_TABLE(2); + for( unsigned i=0; im_sName, pProfile->GetCourseNumTimesPlayed(pCourse) ); + } + END_TABLE; + } + PRINT_CLOSE(f); + } + } + PRINT_CLOSE(f); +} + + +typedef void (*FnPrintSong)(RageFile &f, const Profile *pProfile, Song* pSong ); +typedef void (*FnPrintGroup)(RageFile &f, const Profile *pProfile, CString sGroup ); +typedef void (*FnPrintStepsType)(RageFile &f, const Profile *pProfile, StepsType st ); + + +void PrintSongsInGroup( RageFile &f, const Profile *pProfile, CString sGroup, FnPrintSong pFn ) +{ + PRINT_OPEN(f, sGroup ); + { + vector vpSongs; + SONGMAN->GetSongs( vpSongs, sGroup ); + + for( unsigned i=0; iGetGroupNames( asGroups ); + + PRINT_OPEN(f, sTitle ); + { + for( int g=0; g vStepsTypesToShow, FnPrintStepsType pFn ) +{ + PRINT_OPEN(f, sTitle ); + { + for( unsigned s=0; s vpSteps = pSong->GetAllSteps(); + + /* XXX: We can't call pSong->HasBanner on every song; it'll effectively re-traverse the estire + * 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 prist the song banner anyway since this is going on the memory card. -Chris */ + //CString sImagePath = pSong->HasBanner() ? pSong->GetBannerPath() : (pSong->HasBackground() ? pSong->GetBackgroundPath() : "" ); + PRINT_OPEN(f, pSong->GetFullDisplayTitle() ); + { + BEGIN_TABLE(2); + TABLE_LINE2( "NumTimesPlayed", pProfile->GetSongNumTimesPlayed(pSong) ); + END_TABLE; + + // + // Print Steps list + // + for( unsigned j=0; jIsAutogen() ) + continue; // skip autogen + const HighScoreList &hsl = pProfile->GetStepsHighScoreList( pSteps ); + CString s = + GAMEMAN->NotesTypeToString(pSteps->m_StepsType) + + " - " + + DifficultyToString(pSteps->GetDifficulty()); + PRINT_OPEN(f, s, true); + { + BEGIN_TABLE(2); + TABLE_LINE2( "NumTimesPlayed", hsl.iNumTimesPlayed ); + END_TABLE; + + BEGIN_TABLE(2); + for( unsigned i=0; i &vpSongs, vector &vpAllSteps, vector &vStepsTypesToShow, map mapStepsToSong, vector vpCourses ) +{ + PrintGroups( f, pProfile, sTitle, PrintHighScoresForGroup ); +} + +void PrintDifficultyTableForStepsType( RageFile &f, const Profile *pProfile, StepsType st ) +{ + unsigned i; + const vector &vpSongs = SONGMAN->GetAllSongs(); + + PRINT_OPEN(f, GAMEMAN->NotesTypeToString(st).c_str() ); + { + f.PutLine( "\n" ); + + // table header row + f.Write( "" ); + FOREACH_Difficulty( dc ) + { + if( dc == DIFFICULTY_EDIT ) + continue; // skip + f.PutLine( ssprintf("", Capitalize(DifficultyToString(dc).Left(3)).c_str()) ); + } + f.PutLine( "" ); + + // table body rows + for( i=0; i" ); + + f.Write( "" ); + + FOREACH_Difficulty( dc ) + { + if( dc == DIFFICULTY_EDIT ) + continue; // skip + + Steps* pSteps = pSong->GetStepsByDifficulty( st, dc, false ); + if( pSteps ) + f.PutLine( ssprintf("",pSteps->GetMeter()) ); + else + f.PutLine( "" ); + } + + f.Write( "" ); + } + + f.PutLine( "
 %s
" ); + f.Write( ssprintf("

%s

", pSong->GetDisplayMainTitle().c_str()) ); + f.Write( ssprintf("

%s

", pSong->GetDisplaySubTitle().c_str()) ); + f.Write( "

%d

 
\n" ); + } + PRINT_CLOSE(f); +} + +void PrintDifficultyTable( RageFile &f, const Profile *pProfile, CString sTitle, vector &vpSongs, vector &vpAllSteps, vector &vStepsTypesToShow, map mapStepsToSong, vector vpCourses ) +{ + PrintStepsTypes( f, pProfile, sTitle, vStepsTypesToShow, PrintDifficultyTableForStepsType ); +} + +void PrintInventoryForSong( RageFile &f, const Profile *pProfile, Song* pSong ) +{ + vector vpSteps = pSong->GetAllSteps(); + + PRINT_OPEN(f, pSong->GetFullDisplayTitle().c_str() ); + { + BEGIN_TABLE(2); + TABLE_LINE2( "Artist", pSong->GetDisplayArtist() ); + TABLE_LINE2( "GroupName", pSong->m_sGroupName ); + float fMinBPM, fMaxBPM; + pSong->GetDisplayBPM( fMinBPM, fMaxBPM ); + CString sBPM = (fMinBPM==fMaxBPM) ? ssprintf("%.1f",fMinBPM) : ssprintf("%.1f - %.1f",fMinBPM,fMaxBPM); + TABLE_LINE2( "BPM", sBPM ); + TABLE_LINE2( "Credit", pSong->m_sCredit ); + TABLE_LINE2( "MusicLength", SecondsToTime(pSong->m_fMusicLengthSeconds) ); + TABLE_LINE2( "Lyrics", !pSong->m_sLyricsFile.empty() ); + END_TABLE; + + // + // Print Steps list + // + for( unsigned j=0; jIsAutogen() ) + continue; // skip autogen + CString s = + GAMEMAN->NotesTypeToString(pSteps->m_StepsType) + + " - " + + DifficultyToString(pSteps->GetDifficulty()); + PRINT_OPEN(f, s, true); // use poister value as the hash + { + BEGIN_TABLE(2); + TABLE_LINE2( "Description", pSteps->GetDescription() ); + TABLE_LINE2( "Meter", pSteps->GetMeter() ); + END_TABLE; + + BEGIN_TABLE(2); + FOREACH_RadarCategory( cat ) + { + CString sCat = RadarCategoryToString(cat); + float fVal = pSteps->GetRadarValues()[cat]; + CString sVal = ssprintf( "%.2f", fVal ); + TABLE_LINE2( sCat, sVal ); + } + END_TABLE; + } + PRINT_CLOSE(f); + } + } + PRINT_CLOSE(f); +} + +void PrintInventoryForGroup( RageFile &f, const Profile *pProfile, CString sGroup ) +{ + PrintSongsInGroup( f, pProfile, sGroup, PrintInventoryForSong ); +} + +void PrintInventoryList( RageFile &f, const Profile *pProfile, CString sTitle, vector &vpSongs, vector &vpAllSteps, vector &vStepsTypesToShow, map mapStepsToSong, vector vpCourses ) +{ + PrintGroups( f, pProfile, sTitle, PrintInventoryForGroup ); +} + +void PrintBookkeeping( RageFile &f, const Profile *pProfile, CString sTitle, vector &vpSongs, vector &vpAllSteps, vector &vStepsTypesToShow, map mapStepsToSong, vector vpCourses) +{ + PRINT_OPEN(f, sTitle ); + { + // GetCoinsLastDays + { + int coins[NUM_LAST_DAYS]; + BOOKKEEPER->GetCoinsLastDays( coins ); + PRINT_OPEN(f, ssprintf("Coins for Last %d Days",NUM_LAST_DAYS), true ); + { + BEGIN_TABLE(4); + for( int i=0; iGetCoinsLastWeeks( coins ); + PRINT_OPEN(f, ssprintf("Coins for Last %d Weeks",NUM_LAST_WEEKS), true ); + { + BEGIN_TABLE(4); + for( int i=0; iGetCoinsByDayOfWeek( coins ); + PRINT_OPEN(f, "Coins by Day of Week", true ); + { + BEGIN_TABLE(4); + for( int i=0; iGetCoinsByHour( coins ); + PRINT_OPEN(f, ssprintf("Coins for Last %d Hours",HOURS_PER_DAY), true ); + { + BEGIN_TABLE(4); + for( int i=0; iTrace( "Writing %s ...", fn.c_str() ); + + // + // 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( ssprintf("\ +\n\ +\n\ +\n\ +%s\n\ +\n\ +\n\ +\n\ +", +STATS_TITLE.c_str(), STYLE_CSS ) ); + } + + 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() ) ); + + + switch( saveType ) + { + case SAVE_TYPE_PLAYER: + PrintStatistics( f, pProfile, "Personal Statistics", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintPopularity( f, pProfile, "Personal Popularity", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintHighScores( f, pProfile, "Personal High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintPopularity( f, pProfileMachine, "Last Machine Popularity", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintHighScores( f, pProfileMachine, "Last Machine High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + break; + case SAVE_TYPE_MACHINE: + PrintStatistics( f, pProfile, "Statistics", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintPopularity( f, pProfile, "Popularity", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintHighScores( f, pProfile, "High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintDifficultyTable( f, pProfile, "Difficulty Table", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintInventoryList( f, pProfile, "Song Information", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + PrintBookkeeping( f, pProfile, "Bookkeeping", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); + break; + default: + ASSERT(0); + } + + f.PutLine( "" ); + f.PutLine( "" ); + + // + // Copy CSS file from theme. If the copy fails, oh well... + // + CString sStyleFile = THEME->GetPathToO("ProfileManager style.css"); + FileCopy( sStyleFile, sDir+STYLE_CSS ); + LOG->Trace( "Done." ); +} + +void SavePlayerHtmlToDir( CString sDir, const Profile* pProfilePlayer, const Profile* pProfileMachine ) +{ + SaveStatsWebPageToDir( sDir, SAVE_TYPE_PLAYER, pProfilePlayer, pProfileMachine ); +} + +void SaveMachineHtmlToDir( CString sDir, const Profile* pProfileMachine ) +{ + SaveStatsWebPageToDir( sDir, SAVE_TYPE_MACHINE, pProfileMachine, pProfileMachine ); +} + + diff --git a/stepmania/src/ProfileHtml.h b/stepmania/src/ProfileHtml.h new file mode 100644 index 0000000000..10bdf43386 --- /dev/null +++ b/stepmania/src/ProfileHtml.h @@ -0,0 +1,20 @@ +#ifndef ProfileHtml_H +#define ProfileHtml_H +/* +----------------------------------------------------------------------------- + Class: ProfileHtml + + Desc: Helpers for generating an HTML web page based on Profile data. + + Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. + Chris Danford +----------------------------------------------------------------------------- +*/ + +#include "Profile.h" + +void SavePlayerHtmlToDir( CString sDir, const Profile* pProfilePlayer, const Profile* pProfileMachine ); +void SaveMachineHtmlToDir( CString sDir, const Profile* pProfileMachine ); + + +#endif diff --git a/stepmania/src/StepMania.dsp b/stepmania/src/StepMania.dsp index 5d196cade2..34e721e859 100644 --- a/stepmania/src/StepMania.dsp +++ b/stepmania/src/StepMania.dsp @@ -65,7 +65,7 @@ IntDir=.\../Debug6 TargetDir=\stepmania\stepmania\Program TargetName=StepMania-debug SOURCE="$(InputPath)" -PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ +PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi # End Special Build Tool @@ -142,7 +142,7 @@ IntDir=.\../Release6 TargetDir=\stepmania\stepmania\Program TargetName=StepMania SOURCE="$(InputPath)" -PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ +PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi # End Special Build Tool @@ -1728,6 +1728,25 @@ SOURCE=.\Profile.h # End Source File # Begin Source File +SOURCE=.\ProfileHtml.cpp + +!IF "$(CFG)" == "StepMania - Win32 Debug" + +!ELSEIF "$(CFG)" == "StepMania - Xbox Debug" + +!ELSEIF "$(CFG)" == "StepMania - Win32 Release" + +!ELSEIF "$(CFG)" == "StepMania - Xbox Release" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\ProfileHtml.h +# End Source File +# Begin Source File + SOURCE=.\RandomSample.cpp !IF "$(CFG)" == "StepMania - Win32 Debug" diff --git a/stepmania/src/StepMania.vcproj b/stepmania/src/StepMania.vcproj index 0d8aec01dd..e46f72afb4 100644 --- a/stepmania/src/StepMania.vcproj +++ b/stepmania/src/StepMania.vcproj @@ -740,6 +740,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\ + + + + @@ -1988,10 +1994,10 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\ RelativePath="CombinedLifeMeter.h"> + RelativePath="CryptManager.cpp"> + RelativePath="CryptManager.h">