#include "global.h" #include "Bookkeeper.h" #include "RageUtil.h" #include "RageLog.h" #include "IniFile.h" #include "GameConstantsAndTypes.h" #include "SongManager.h" #include "RageFile.h" #include "XmlFile.h" #include "XmlFileUtil.h" #include Bookkeeper* BOOKKEEPER = NULL; // global and accessable from anywhere in our program static const RString COINS_DAT = "Save/Coins.xml"; Bookkeeper::Bookkeeper() { ClearAll(); ReadFromDisk(); } Bookkeeper::~Bookkeeper() { WriteToDisk(); } #define WARN_AND_RETURN { LOG->Warn("Error parsing at %s:%d",__FILE__,__LINE__); return; } void Bookkeeper::ClearAll() { m_mapCoinsForHour.clear(); } bool Bookkeeper::Date::operator<( const Date &rhs ) const { if( m_iYear != rhs.m_iYear ) return m_iYear < rhs.m_iYear; if( m_iDayOfYear != rhs.m_iDayOfYear ) return m_iDayOfYear < rhs.m_iDayOfYear; return m_iHour < rhs.m_iHour; } void Bookkeeper::Date::Set( time_t t ) { tm ltime; localtime_r( &t, <ime ); Set( ltime ); } void Bookkeeper::Date::Set( tm pTime ) { m_iHour = pTime.tm_hour; m_iDayOfYear = pTime.tm_yday; m_iYear = pTime.tm_year + 1900; } void Bookkeeper::LoadFromNode( const XNode *pNode ) { if( pNode->GetName() != "Bookkeeping" ) { LOG->Warn( "Error loading bookkeeping: unexpected \"%s\"", pNode->GetName().c_str() ); return; } const XNode *pData = pNode->GetChild( "Data" ); if( pData == NULL ) { LOG->Warn( "Error loading bookkeeping: Data node missing" ); 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->GetTextValue( iCoins ); m_mapCoinsForHour[d] = iCoins; } } XNode* Bookkeeper::CreateNode() const { XNode *xml = new XNode( "Bookkeeping" ); { XNode* pData = xml->AppendChild("Data"); for( map::const_iterator it = m_mapCoinsForHour.begin(); it != m_mapCoinsForHour.end(); ++it ) { int iCoins = it->second; XNode *pDay = pData->AppendChild( "Coins", iCoins ); const Date &d = it->first; pDay->AppendAttr( "Hour", d.m_iHour ); pDay->AppendAttr( "Day", d.m_iDayOfYear ); pDay->AppendAttr( "Year", d.m_iYear ); } } return xml; } void Bookkeeper::ReadFromDisk() { if( !IsAFile(COINS_DAT) ) return; XNode xml; if( !XmlFileUtil::LoadFromFileShowErrors(xml, COINS_DAT) ) return; LoadFromNode( &xml ); } void Bookkeeper::WriteToDisk() { // Write data. Use SLOW_FLUSH, to help ensure that we don't lose coin data. RageFile f; if( !f.Open(COINS_DAT, RageFile::WRITE|RageFile::SLOW_FLUSH) ) { LOG->Warn( "Couldn't open file \"%s\" for writing: %s", COINS_DAT.c_str(), f.GetError().c_str() ); return; } auto_ptr xml( CreateNode() ); XmlFileUtil::SaveToFile( xml.get(), f ); } void Bookkeeper::CoinInserted() { Date d; d.Set( time(NULL) ); ++m_mapCoinsForHour[d]; } // Return the number of coins between [beginning,ending). int Bookkeeper::GetNumCoinsInRange( map::const_iterator begin, map::const_iterator end ) const { int iCoins = 0; while( begin != end ) { iCoins += begin->second; ++begin; } return iCoins; } int Bookkeeper::GetNumCoins( Date beginning, Date ending ) const { return GetNumCoinsInRange( m_mapCoinsForHour.lower_bound( beginning ), m_mapCoinsForHour.lower_bound( ending ) ); } 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 ) { 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] ) const { memset( coins, 0, sizeof(int) * HOURS_IN_DAY ); for( map::const_iterator it = m_mapCoinsForHour.begin(); it != m_mapCoinsForHour.end(); ++it ) { const Date &d = it->first; if( d.m_iHour >= HOURS_IN_DAY ) { LOG->Warn( "Hour %i >= %i", d.m_iHour, HOURS_IN_DAY ); continue; } coins[d.m_iHour] += it->second; } } /* * (c) 2003-2005 Chris Danford, Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */