diff --git a/stepmania/Themes/default/metrics.ini b/stepmania/Themes/default/metrics.ini
index 795e95804e..48eb264d3d 100644
--- a/stepmania/Themes/default/metrics.ini
+++ b/stepmania/Themes/default/metrics.ini
@@ -4262,4 +4262,6 @@ LevelP1OnCommand=addy,-140
LevelP2OnCommand=addy,-140
[ProfileManager]
-StatsTitle=StepMania Statistics
+StatsTitle=StepMania Data
+StatsFooter=generated by StepMania
+StatsURL=
diff --git a/stepmania/src/Course.cpp b/stepmania/src/Course.cpp
index 1811bf8fe2..8f19975f9b 100644
--- a/stepmania/src/Course.cpp
+++ b/stepmania/src/Course.cpp
@@ -1056,20 +1056,20 @@ void SortCoursePointerArrayByAvgDifficulty( vector &apCourses )
}
-void SortCoursePointerArrayByMostPlayed( vector &arrayCoursePointers, ProfileSlot slot )
+void SortCoursePointerArrayByNumPlays( vector &arrayCoursePointers, ProfileSlot slot, bool bDescending )
{
Profile* pProfile = PROFILEMAN->GetProfile(slot);
if( pProfile == NULL )
return; // nothing to do since we don't have data
- SortCoursePointerArrayByMostPlayed( arrayCoursePointers, pProfile );
+ SortCoursePointerArrayByNumPlays( arrayCoursePointers, pProfile, bDescending );
}
-void SortCoursePointerArrayByMostPlayed( vector &arrayCoursePointers, const Profile* pProfile )
+void SortCoursePointerArrayByNumPlays( vector &arrayCoursePointers, const Profile* pProfile, bool bDescending )
{
ASSERT( pProfile );
for(unsigned i = 0; i < arrayCoursePointers.size(); ++i)
course_sort_val[arrayCoursePointers[i]] = (float) pProfile->GetCourseNumTimesPlayed(arrayCoursePointers[i]);
- stable_sort( arrayCoursePointers.begin(), arrayCoursePointers.end(), CompareCoursePointersBySortValueDescending );
+ stable_sort( arrayCoursePointers.begin(), arrayCoursePointers.end(), bDescending ? CompareCoursePointersBySortValueDescending : CompareCoursePointersBySortValueAscending );
course_sort_val.clear();
}
diff --git a/stepmania/src/Course.h b/stepmania/src/Course.h
index d746726baf..91b4e0b668 100644
--- a/stepmania/src/Course.h
+++ b/stepmania/src/Course.h
@@ -166,8 +166,8 @@ void SortCoursePointerArrayByType( vector &apCourses );
void SortCoursePointerArrayByAvgDifficulty( vector &apCourses );
void SortCoursePointerArrayByTotalDifficulty( vector &apCourses );
void SortCoursePointerArrayByRanking( vector &apCourses );
-void SortCoursePointerArrayByMostPlayed( vector &arrayCoursePointers, ProfileSlot slot );
-void SortCoursePointerArrayByMostPlayed( vector &arrayCoursePointers, const Profile* pProfile );
+void SortCoursePointerArrayByNumPlays( vector &arrayCoursePointers, ProfileSlot slot, bool bDescending );
+void SortCoursePointerArrayByNumPlays( vector &arrayCoursePointers, const Profile* pProfile, bool bDescending );
void MoveRandomToEnd( vector &apCourses );
diff --git a/stepmania/src/CryptManager.cpp b/stepmania/src/CryptManager.cpp
index 91db41e194..fb04569042 100644
--- a/stepmania/src/CryptManager.cpp
+++ b/stepmania/src/CryptManager.cpp
@@ -121,3 +121,8 @@ void CryptManager::DigestFile(const char *filename)
// cout << "\nMD5: ";
// md5Filter.TransferTo(encoder);
}
+
+CString CryptManager::GetPublicKeyFileName()
+{
+ return PUBLIC_KEY_PATH;
+}
diff --git a/stepmania/src/CryptManager.h b/stepmania/src/CryptManager.h
index 29995b4d88..bc6709dfc8 100644
--- a/stepmania/src/CryptManager.h
+++ b/stepmania/src/CryptManager.h
@@ -13,6 +13,8 @@ public:
static bool VerifyFile( CString sPath );
static void DigestFile(const char *filename);
+
+ static CString GetPublicKeyFileName();
};
extern CryptManager* CRYPTMAN; // global and accessable from anywhere in our program
diff --git a/stepmania/src/GameState.cpp b/stepmania/src/GameState.cpp
index 288fa365c2..9091970b9e 100644
--- a/stepmania/src/GameState.cpp
+++ b/stepmania/src/GameState.cpp
@@ -266,7 +266,7 @@ void GameState::EndGame()
CHECKPOINT;
BOOKKEEPER->WriteToDisk();
- PROFILEMAN->SaveMachineScoresToDisk();
+ PROFILEMAN->SaveMachineProfile();
for( p=0; p::const_iterator iter = m_StepsHighScores.find( pSteps );
if( iter == m_StepsHighScores.end() )
- {
return 0;
- }
else
- {
- int iTotalNumTimesPlayed = 0;
- for( unsigned st = 0; st < NUM_STEPS_TYPES; ++st )
- iTotalNumTimesPlayed += iter->second.hs.iNumTimesPlayed;
- return iTotalNumTimesPlayed;
- }
+ return iter->second.hs.iNumTimesPlayed;
}
void Profile::IncrementStepsPlayCount( const Steps* pSteps )
@@ -294,6 +288,8 @@ bool Profile::LoadAllFromDir( CString sDir )
bool Profile::SaveAllToDir( CString sDir ) const
{
+ m_sLastMachinePlayed = PREFSMAN->m_sMachineName;
+
// Delete old files after saving new ones so we don't try to load old
// and make duplicate records.
// If the save fails, the delete will fail too... probably :-)
@@ -305,6 +301,7 @@ bool Profile::SaveAllToDir( CString sDir ) const
SaveCategoryScoresToDir( sDir );
DeleteCategoryScoresFromDirSM390a12( sDir );
SaveStatsWebPageToDir( sDir );
+ SaveMachinePublicKeyToDir( sDir );
return bResult;
}
@@ -342,6 +339,9 @@ bool Profile::LoadGeneralDataFromDir( CString sDir )
ini.GetValue( "Profile", "TotalPlaySeconds", m_iTotalPlaySeconds );
ini.GetValue( "Profile", "TotalGameplaySeconds", m_iTotalGameplaySeconds );
ini.GetValue( "Profile", "CurrentCombo", m_iCurrentCombo );
+ ini.GetValue( "Profile", "WeightPounds", m_fWeightPounds );
+ ini.GetValue( "Profile", "CaloriesBurned", m_fCaloriesBurned );
+ ini.GetValue( "Profile", "LastMachinePlayed", m_sLastMachinePlayed );
unsigned i;
for( i=0; iGetMachineProfile();
+
+ if( bThisIsMachineProfile )
{
SaveMachineHtmlToDir( sDir, this );
-// SavePlayerHtmlToDir( sDir, this, PROFILEMAN->GetMachineProfile() ); // remove this when done debugging
+ SavePlayerHtmlToDir( sDir+"temp/", this, PROFILEMAN->GetMachineProfile() ); // remove this when done debugging
}
else
{
SavePlayerHtmlToDir( sDir, this, PROFILEMAN->GetMachineProfile() );
}
}
+
+void Profile::SaveMachinePublicKeyToDir( CString sDir ) const
+{
+ FileCopy( CRYPTMAN->GetPublicKeyFileName(), "public.key.rsa" );
+}
diff --git a/stepmania/src/Profile.h b/stepmania/src/Profile.h
index 3b9983ea8e..67900aba6c 100644
--- a/stepmania/src/Profile.h
+++ b/stepmania/src/Profile.h
@@ -34,7 +34,7 @@ public:
//
// smart accessors
//
- CString GetDisplayName();
+ CString GetDisplayName() const;
CString GetDisplayCaloriesBurned();
int GetTotalNumSongsPlayed();
static CString GetProfileDisplayNameFromDir( CString sDir );
@@ -43,7 +43,6 @@ public:
//
// General data
//
- bool m_bIsMachineProfile;
CString m_sName;
CString m_sLastUsedHighScoreName;
bool m_bUsingProfileDefaultModifiers;
@@ -54,6 +53,7 @@ public:
int m_iCurrentCombo;
float m_fWeightPounds;
int m_fCaloriesBurned;
+ mutable CString m_sLastMachinePlayed; // mutable because we overwrite this on save, and I don't want to remove const from the whole save chain. -Chris
int m_iNumSongsPlayedByPlayMode[NUM_PLAY_MODES];
int m_iNumSongsPlayedByStyle[NUM_STYLES];
int m_iNumSongsPlayedByDifficulty[NUM_DIFFICULTIES];
@@ -146,6 +146,8 @@ public:
void DeleteCategoryScoresFromDirSM390a12( CString sDir ) const;
void SaveStatsWebPageToDir( CString sDir ) const;
+
+ void SaveMachinePublicKeyToDir( CString sDir ) const;
};
diff --git a/stepmania/src/ProfileHtml.cpp b/stepmania/src/ProfileHtml.cpp
index e25880c107..c63b3bd21c 100644
--- a/stepmania/src/ProfileHtml.cpp
+++ b/stepmania/src/ProfileHtml.cpp
@@ -27,6 +27,7 @@
#define STYLE_CSS "style.css"
#define STATS_TITLE THEME->GetMetric("ProfileManager","StatsTitle")
+#define STATS_FOOTER THEME->GetMetric("ProfileManager","StatsFooter")
static int g_Level = 1;
@@ -141,6 +142,8 @@ void PrintStatistics( RageFile &f, const Profile *pProfile, CString sTitle, vect
TABLE_LINE2( "TotalPlaySeconds", pProfile->m_iTotalPlaySeconds );
TABLE_LINE2( "TotalGameplaySeconds", pProfile->m_iTotalGameplaySeconds );
TABLE_LINE2( "CurrentCombo", pProfile->m_iCurrentCombo );
+ TABLE_LINE2( "CaloriesBurned", pProfile->m_fCaloriesBurned );
+ TABLE_LINE2( "LastMachinePlayed", pProfile->m_sLastMachinePlayed );
END_TABLE;
}
PRINT_CLOSE(f);
@@ -201,35 +204,63 @@ void PrintPopularity( RageFile &f, const Profile *pProfile, CString sTitle, vect
{
PRINT_OPEN(f, sTitle );
{
- // Songs by popularity
- {
- unsigned uNumToShow = min( vpSongs.size(), (unsigned)100 );
+ SortSongPointerArrayByNumPlays( vpSongs, pProfile, true );
+ Song* pSongPopularThreshold = vpSongs[ vpSongs.size()*2/3 ];
+ int iPopularNumPlaysThreshold = pProfile->GetSongNumTimesPlayed(pSongPopularThreshold);
+
+ // unplayed songs are always considered unpopular
+ if( iPopularNumPlaysThreshold == 0 )
+ iPopularNumPlaysThreshold = 1;
- SortSongPointerArrayByMostPlayed( vpSongs, pProfile );
- PRINT_OPEN(f, "Songs by Popularity" );
+ unsigned uMaxToShow = min( vpSongs.size(), (unsigned)100 );
+
+ {
+ PRINT_OPEN(f, "Most Popular Songs" );
{
BEGIN_TABLE(1);
- for( unsigned i=0; iGetFullDisplayTitle(), pProfile->GetSongNumTimesPlayed(pSong) );
+ int iNumTimesPlayed = pProfile->GetSongNumTimesPlayed(pSong);
+ if( iNumTimesPlayed == 0 || iNumTimesPlayed < iPopularNumPlaysThreshold ) // not popular
+ break; // done searching
+ TABLE_LINE3(i+1, pSong->GetFullDisplayTitle(), iNumTimesPlayed );
+ }
+ END_TABLE;
+ }
+ PRINT_CLOSE(f);
+ }
+
+ {
+ SortSongPointerArrayByNumPlays( vpSongs, pProfile, false );
+ PRINT_OPEN(f, "Least Popular Songs" );
+ {
+ BEGIN_TABLE(1);
+ for( unsigned i=0; iGetSongNumTimesPlayed(pSong);
+ if( iNumTimesPlayed >= iPopularNumPlaysThreshold ) // not unpopular
+ break; // done searching
+ TABLE_LINE3(i+1, pSong->GetFullDisplayTitle(), iNumTimesPlayed );
}
END_TABLE;
}
PRINT_CLOSE(f);
}
- // Steps by popularity
{
unsigned uNumToShow = min( vpAllSteps.size(), (unsigned)100 );
- SortStepsPointerArrayByMostPlayed( vpAllSteps, pProfile );
- PRINT_OPEN(f, "Steps by Popularity" );
+ SortStepsPointerArrayByNumPlays( vpAllSteps, pProfile, true );
+ PRINT_OPEN(f, "Most Popular Steps" );
{
BEGIN_TABLE(1);
for( unsigned i=0; iGetStepsNumTimesPlayed(pSteps)==0 )
+ continue; // skip
Song* pSong = mapStepsToSong[pSteps];
CString s;
s += pSong->GetFullDisplayTitle();
@@ -244,12 +275,11 @@ void PrintPopularity( RageFile &f, const Profile *pProfile, CString sTitle, vect
PRINT_CLOSE(f);
}
- // Course by popularity
{
unsigned uNumToShow = min( vpCourses.size(), (unsigned)100 );
- SortCoursePointerArrayByMostPlayed( vpCourses, pProfile );
- PRINT_OPEN(f, "Courses by Popularity" );
+ SortCoursePointerArrayByNumPlays( vpCourses, pProfile, true );
+ PRINT_OPEN(f, "Most Popular Courses" );
{
BEGIN_TABLE(2);
for( unsigned i=0; iGetSongNumTimesPlayed(pSong);
+ if( iNumTimesPlayed == 0 )
+ return; // skip
+
vector 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) );
+ TABLE_LINE2( "NumTimesPlayed", iNumTimesPlayed );
END_TABLE;
//
@@ -344,6 +371,9 @@ void PrintHighScoresForSong( RageFile &f, const Profile *pProfile, Song* pSong )
Steps* pSteps = vpSteps[j];
if( pSteps->IsAutogen() )
continue; // skip autogen
+ if( pProfile->GetStepsNumTimesPlayed(pSteps)==0 )
+ continue; // skip
+
const HighScoreList &hsl = pProfile->GetStepsHighScoreList( pSteps );
CString s =
GAMEMAN->NotesTypeToString(pSteps->m_StepsType) +
@@ -692,6 +722,13 @@ function expandIt(whichEl)\n\
STATS_TITLE.c_str(), STYLE_CSS ) );
}
+ CString sType;
+ switch( saveType )
+ {
+ case SAVE_TYPE_PLAYER: sType = "Player: "; break;
+ case SAVE_TYPE_MACHINE: sType = "Machine: "; break;
+ }
+
CString sName =
pProfile->m_sLastUsedHighScoreName.empty() ?
pProfile->m_sName :
@@ -700,18 +737,20 @@ STATS_TITLE.c_str(), STYLE_CSS ) );
CString sTime = ctime( <ime );
f.Write( ssprintf(
- "\n",
- STATS_TITLE.c_str(), sName.c_str(), sTime.c_str() ) );
+ "\n",
+ STATS_TITLE.c_str(), sType.c_str(), sName.c_str(), sTime.c_str() ) );
+ CString sPlayerName = pProfile->GetDisplayName();
+ CString sMachineName = pProfileMachine->GetDisplayName();
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 );
+ PrintStatistics( f, pProfile, sPlayerName+"'s Statistics", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses );
+ PrintPopularity( f, pProfile, sPlayerName+"'s Popularity", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses );
+ PrintHighScores( f, pProfile, sPlayerName+"'s High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses );
+ PrintPopularity( f, pProfileMachine, sMachineName+"'s Popularity", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses );
+ PrintHighScores( f, pProfileMachine, sMachineName+"'s High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses );
break;
case SAVE_TYPE_MACHINE:
PrintStatistics( f, pProfile, "Statistics", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses );
@@ -725,6 +764,8 @@ STATS_TITLE.c_str(), STYLE_CSS ) );
ASSERT(0);
}
+ f.PutLine( ssprintf("\n", STATS_FOOTER.c_str()) );
+
f.PutLine( "