diff --git a/stepmania/src/Bookkeeper.cpp b/stepmania/src/Bookkeeper.cpp index 24f6bec6ff..c560088905 100644 --- a/stepmania/src/Bookkeeper.cpp +++ b/stepmania/src/Bookkeeper.cpp @@ -7,22 +7,19 @@ #include "GameConstantsAndTypes.h" #include "SongManager.h" #include "RageFile.h" +#include "XmlFile.h" #include Bookkeeper* BOOKKEEPER = NULL; // global and accessable from anywhere in our program -static const CString COINS_DAT = "Data/Coins.dat"; - -const int COINS_DAT_VERSION = 1; +static const CString COINS_DAT = "Data/Coins.xml"; Bookkeeper::Bookkeeper() { ClearAll(); ReadFromDisk(); - - UpdateLastSeenTime(); } Bookkeeper::~Bookkeeper() @@ -34,152 +31,173 @@ Bookkeeper::~Bookkeeper() void Bookkeeper::ClearAll() { - m_iLastSeenTime = time(NULL); - for( int i=0; im_sName != "Bookkeeping" ) + { + LOG->Warn( "Error loading bookkeeping: unexpected \"%s\"", pNode->m_sName.c_str() ); + return; + } + + const XNode *pData = pNode->GetChild( "Data" ); + if( pData == NULL ) + { + LOG->Warn( "Error loading bookkeeping: Data node missing", pNode->m_sName.c_str() ); + return; + } + + FOREACH_CONST_Child( pData, day ) + { + Date d; + if( !day->GetAttrValue( "Hour", d.m_iHour ) || + !day->GetAttrValue( "Day", d.m_iDayOfYear ) || + !day->GetAttrValue( "Year", d.m_iYear ) ) + { + LOG->Warn( "Incomplete date field" ); + continue; + } + + int iCoins; + day->GetValue( iCoins ); + + m_mapCoinsForHour[d] = iCoins; + } +} + +XNode* Bookkeeper::CreateNode() const +{ + XNode *xml = new XNode; + xml->m_sName = "Bookkeeping"; + + { + XNode* pData = xml->AppendChild("Data"); + + for( map::const_iterator it = m_mapCoinsForHour.begin(); it != m_mapCoinsForHour.end(); ++it ) + { + XNode *pDay = pData->AppendChild( "Coins" ); + + const Date &d = it->first; + pDay->AppendAttr( "Hour", d.m_iHour ); + pDay->AppendAttr( "Day", d.m_iDayOfYear ); + pDay->AppendAttr( "Year", d.m_iYear ); + + int iCoins = it->second; + pDay->SetValue( iCoins ); + } + } + + return xml; } void Bookkeeper::ReadFromDisk() { - RageFile f; - if( !f.Open(COINS_DAT, RageFile::READ) ) + XNode xml; + PARSEINFO pi; + if( !xml.LoadFromFile(COINS_DAT, &pi) ) { - LOG->Trace( "Couldn't open file \"%s\": %s", COINS_DAT.c_str(), f.GetError().c_str() ); + LOG->Warn( "Error parsing file \"%s\": %s", COINS_DAT.c_str(), pi.error_string.c_str() ); return; } - int iVer; - if( !FileRead(f, iVer) ) - WARN_AND_RETURN; - if( iVer != COINS_DAT_VERSION ) - WARN_AND_RETURN; - - if( !FileRead(f, m_iLastSeenTime) ) - WARN_AND_RETURN; - - for (int i=0; iWarn( "Couldn't open file \"%s\" for writing: %s", COINS_DAT.c_str(), f.GetError().c_str() ); return; } - - FileWrite(f, COINS_DAT_VERSION ); - - FileWrite(f, m_iLastSeenTime); - for (int i=0; iWarn( "The new time is older than the last seen time. Is someone fiddling with the system clock?" ); - m_iLastSeenTime = lNewTime; - return; - } - - tm tOld, tNew; - localtime_r( &lOldTime, &tOld ); - localtime_r( &lNewTime, &tNew ); - - CLAMP( tOld.tm_year, tNew.tm_year-1, tNew.tm_year ); - - int cnt = 0; - while( - tOld.tm_year != tNew.tm_year || - tOld.tm_yday != tNew.tm_yday || - tOld.tm_hour != tNew.tm_hour ) - { - /* Paranoia: break out in case our loop doesn't end for some reason. */ - ++cnt; - if( cnt > 1000 ) - break; - - tOld.tm_hour++; - if( tOld.tm_hour == HOURS_IN_DAY ) - { - tOld.tm_hour = 0; - tOld.tm_yday++; - } - if( tOld.tm_yday == DAYS_IN_YEAR ) - { - tOld.tm_yday = 0; - tOld.tm_year++; - } - - m_iCoinsByHourForYear[tOld.tm_yday][tOld.tm_hour] = 0; - } - - m_iLastSeenTime = lNewTime; + DISP_OPT opt; + XNode *xml = CreateNode(); + xml->SaveToFile( f, &opt ); + delete xml; } void Bookkeeper::CoinInserted() { - UpdateLastSeenTime(); + Date d; + d.Set( time(NULL) ); - time_t lTime = m_iLastSeenTime; - tm pTime; - localtime_r( &lTime, &pTime ); - - m_iCoinsByHourForYear[pTime.tm_yday][pTime.tm_hour]++; + ++m_mapCoinsForHour[d]; } -int Bookkeeper::GetCoinsForDay( int iDayOfYear ) +/* Return the number of coins between [beginning,ending). */ +int Bookkeeper::GetNumCoinsInRange( map::const_iterator begin, map::const_iterator end ) const { int iCoins = 0; - for( int i=0; isecond; + ++begin; + } + return iCoins; } - -void Bookkeeper::GetCoinsLastDays( int coins[NUM_LAST_DAYS] ) +int Bookkeeper::GetNumCoins( Date beginning, Date ending ) const { - UpdateLastSeenTime(); + return GetNumCoinsInRange( m_mapCoinsForHour.lower_bound( beginning ), + m_mapCoinsForHour.lower_bound( ending ) ); +} - time_t lOldTime = m_iLastSeenTime; +int Bookkeeper::GetCoinsTotal() const +{ + return GetNumCoinsInRange( m_mapCoinsForHour.begin(), m_mapCoinsForHour.end() ); +} + + +void Bookkeeper::GetCoinsLastDays( int coins[NUM_LAST_DAYS] ) const +{ + time_t lOldTime = time(NULL); tm time; localtime_r( &lOldTime, &time ); + time.tm_hour = 0; + for( int i=0; i::const_iterator it = m_mapCoinsForHour.begin(); it != m_mapCoinsForHour.end(); ++it ) { - coins[GetDayOfWeek(time)] += GetCoinsForDay( time.tm_yday ); - time = GetYesterday( time ); + const Date &d = it->first; + int iDayOfWeek = GetDayInYearAndYear( d.m_iDayOfYear, d.m_iYear ).tm_wday; + coins[iDayOfWeek] += it->second; } } -void Bookkeeper::GetCoinsByHour( int coins[HOURS_IN_DAY] ) +void Bookkeeper::GetCoinsByHour( int coins[HOURS_IN_DAY] ) const { - UpdateLastSeenTime(); - - for( int h=0; h::const_iterator it = m_mapCoinsForHour.begin(); it != m_mapCoinsForHour.end(); ++it ) { - coins[h] = 0; + const Date &d = it->first; - for( int d=0; d= HOURS_IN_DAY ) + { + LOG->Warn( "Hour %i >= %i", d.m_iHour, HOURS_IN_DAY ); + continue; + } + + coins[d.m_iHour] += it->second; } } /* - * (c) 2003-2004 Chris Danford + * (c) 2003-2005 Chris Danford, Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a diff --git a/stepmania/src/Bookkeeper.h b/stepmania/src/Bookkeeper.h index 5942ba6a2c..868bbe280b 100644 --- a/stepmania/src/Bookkeeper.h +++ b/stepmania/src/Bookkeeper.h @@ -4,7 +4,8 @@ #define Bookkeeper_H #include "DateTime.h" - +#include +struct XNode; class Bookkeeper { @@ -15,22 +16,36 @@ public: void ClearAll(); void CoinInserted(); - void UpdateLastSeenTime(); - void GetCoinsLastDays( int coins[NUM_LAST_DAYS] ); - void GetCoinsLastWeeks( int coins[NUM_LAST_WEEKS] ); - void GetCoinsByDayOfWeek( int coins[DAYS_IN_WEEK] ); - void GetCoinsByHour( int coins[HOURS_IN_DAY] ); + int GetCoinsTotal() const; + void GetCoinsLastDays( int coins[NUM_LAST_DAYS] ) const; + void GetCoinsLastWeeks( int coins[NUM_LAST_WEEKS] ) const; + void GetCoinsByDayOfWeek( int coins[DAYS_IN_WEEK] ) const; + void GetCoinsByHour( int coins[HOURS_IN_DAY] ) const; + + void LoadFromNode( const XNode *pNode ); + XNode* CreateNode() const; void ReadFromDisk(); void WriteToDisk(); private: - - int GetCoinsForDay( int iDayOfYear ); + struct Date + { + int m_iHour; // 0 = midnight + int m_iDayOfYear; // 0 = Jan 1 + int m_iYear; // eg. 2005 + Date() { m_iHour = m_iDayOfYear = m_iYear = 0; } + Date( tm time ) { Set(time); } + void Set( time_t t ); + void Set( tm pTime ); + bool operator<( const Date &rhs ) const; + }; + int GetNumCoins( Date beginning, Date ending ) const; + int GetNumCoinsInRange( map::const_iterator begin, map::const_iterator end ) const; int m_iLastSeenTime; - int m_iCoinsByHourForYear[DAYS_IN_YEAR][HOURS_IN_DAY]; + map m_mapCoinsForHour; }; diff --git a/stepmania/src/ScreenBookkeeping.cpp b/stepmania/src/ScreenBookkeeping.cpp index 88e48da6bb..d9bd9a19cc 100644 --- a/stepmania/src/ScreenBookkeeping.cpp +++ b/stepmania/src/ScreenBookkeeping.cpp @@ -145,6 +145,9 @@ void ScreenBookkeeping::ChangeView( View newView ) float fAverage = iTotalLast/(float)NUM_LAST_DAYS; sData += ssprintf("%.1f\n",fAverage); + sTitle += "Grand Total\n"; + sData += ssprintf( "%i\n", BOOKKEEPER->GetCoinsTotal() ); + m_textCols[0].SetHorizAlign( Actor::align_left ); m_textCols[0].SetText( sTitle ); m_textCols[1].SetText( "" );