diff --git a/src/Profile.cpp b/src/Profile.cpp index 6c66800f67..f00b592142 100644 --- a/src/Profile.cpp +++ b/src/Profile.cpp @@ -32,6 +32,7 @@ #include const RString STATS_XML = "Stats.xml"; +const RString ETT_XML = "ETT.xml"; const RString STATS_XML_GZ = "Stats.xml.gz"; /** @brief The filename for where one can edit their personal profile information. */ const RString EDITABLE_INI = "Editable.ini"; @@ -1325,7 +1326,8 @@ bool Profile::SaveAllToDir( const RString &sDir, bool bSignData ) const // Save editable.ini SaveEditableDataToDir( sDir ); - bool bSaved = SaveStatsXmlToDir( sDir, bSignData ); + //bool bSaved = SaveStatsXmlToDir( sDir, bSignData ); + bool bSaved = SaveEttXmlToDir(sDir, bSignData); SaveStatsWebPageToDir( sDir ); @@ -1376,6 +1378,16 @@ XNode *Profile::SaveStatsXmlCreateNode() const return xml; } +XNode *Profile::SaveEttXmlCreateNode() const +{ + XNode *xml = new XNode("Stats"); + + xml->AppendChild(SaveGeneralDataCreateNode()); + xml->AppendChild(SaveEttScoresCreateNode()); + + return xml; +} + bool Profile::SaveStatsXmlToDir( RString sDir, bool bSignData ) const { LOG->Trace( "SaveStatsXmlToDir: %s", sDir.c_str() ); @@ -1432,6 +1444,62 @@ bool Profile::SaveStatsXmlToDir( RString sDir, bool bSignData ) const return true; } +bool Profile::SaveEttXmlToDir(RString sDir, bool bSignData) const +{ + LOG->Trace("SaveStatsXmlToDir: %s", sDir.c_str()); + unique_ptr xml(SaveEttXmlCreateNode()); + + sDir += PROFILEMAN->GetStatsPrefix(); + // Save stats.xml + RString fn = sDir + (g_bProfileDataCompress ? STATS_XML_GZ : ETT_XML); + + { + RString sError; + RageFile f; + if (!f.Open(fn, RageFile::WRITE)) + { + LuaHelpers::ReportScriptErrorFmt("Couldn't open %s for writing: %s", fn.c_str(), f.GetError().c_str()); + return false; + } + + if (g_bProfileDataCompress) + { + RageFileObjGzip gzip(&f); + gzip.Start(); + if (!XmlFileUtil::SaveToFile(xml.get(), gzip, "", false)) + return false; + + if (gzip.Finish() == -1) + return false; + + /* After successfully saving STATS_XML_GZ, remove any stray STATS_XML. */ + if (FILEMAN->IsAFile(sDir + STATS_XML)) + FILEMAN->Remove(sDir + STATS_XML); + } + else + { + if (!XmlFileUtil::SaveToFile(xml.get(), f, "", false)) + return false; + + /* After successfully saving STATS_XML, remove any stray STATS_XML_GZ. */ + if (FILEMAN->IsAFile(sDir + STATS_XML_GZ)) + FILEMAN->Remove(sDir + STATS_XML_GZ); + } + } + + if (bSignData) + { + RString sStatsXmlSigFile = fn + SIGNATURE_APPEND; + CryptManager::SignFileToFile(fn, sStatsXmlSigFile); + + // Save the "don't share" file + RString sDontShareFile = sDir + DONT_SHARE_SIG; + CryptManager::SignFileToFile(sStatsXmlSigFile, sDontShareFile); + } + + return true; +} + void Profile::SaveTypeToDir(const RString &dir) const { IniFile ini; @@ -1948,7 +2016,51 @@ float Profile::CalculateCaloriesFromHeartRate(float HeartRate, float Duration) / 4.184f) * Duration; } -XNode* Profile::SaveSongScoresCreateNode() const +XNode* Profile::SaveSongScoresCreateNode() const +{ + CHECKPOINT_M("Getting the node to save song scores."); + + const Profile* pProfile = this; + ASSERT(pProfile != NULL); + + XNode* pNode = new XNode("SongScores"); + + FOREACHM_CONST(SongID, HighScoresForASong, m_SongHighScores, i) + { + const SongID &songID = i->first; + const HighScoresForASong &hsSong = i->second; + + // skip songs that have never been played + if (pProfile->GetSongNumTimesPlayed(songID) == 0) + continue; + + XNode* pSongNode = pNode->AppendChild(songID.CreateNode()); + + int jCheck2 = hsSong.m_StepsHighScores.size(); + int jCheck1 = 0; + FOREACHM_CONST(StepsID, HighScoresForASteps, hsSong.m_StepsHighScores, j) + { + jCheck1++; + ASSERT(jCheck1 <= jCheck2); + const StepsID &stepsID = j->first; + const HighScoresForASteps &hsSteps = j->second; + + const HighScoreList &hsl = hsSteps.hsl; + + // skip steps that have never been played + if (hsl.GetNumTimesPlayed() == 0) + continue; + + XNode* pStepsNode = pSongNode->AppendChild(stepsID.CreateNode()); + + pStepsNode->AppendChild(hsl.CreateNode()); + } + } + + return pNode; +} + +XNode* Profile::SaveEttScoresCreateNode() const { CHECKPOINT_M("Getting the node to save song scores."); @@ -1956,37 +2068,38 @@ XNode* Profile::SaveSongScoresCreateNode() const ASSERT( pProfile != NULL ); XNode* pNode = new XNode( "SongScores" ); - - FOREACHM_CONST( SongID, HighScoresForASong, m_SongHighScores, i ) + + FOREACHM_CONST( RString, HighScoreRateMap, HighScoresByChartKey, i ) { - const SongID &songID = i->first; - const HighScoresForASong &hsSong = i->second; + const RString &ck = i->first; + const HighScoreRateMap &hsrm = i->second; + XNode* pChartKey = new XNode("Chart"); - // skip songs that have never been played - if( pProfile->GetSongNumTimesPlayed(songID) == 0 ) + Song* psong = SONGMAN->GetSongByChartkey(ck); + + // sometimes scores on invalid songs get loaded even though there is literally an isvalid check before any scores are loaded..?? + if (!psong) continue; - XNode* pSongNode = pNode->AppendChild( songID.CreateNode() ); + pChartKey->AppendAttr("Key", ck); + pChartKey->AppendAttr("SongTitle", psong->GetDisplayMainTitle()); - int jCheck2 = hsSong.m_StepsHighScores.size(); - int jCheck1 = 0; - FOREACHM_CONST( StepsID, HighScoresForASteps, hsSong.m_StepsHighScores, j ) + XNode* pScores = new XNode("RateScores"); + FOREACHM_CONST( float, vector, hsrm, j ) { - jCheck1++; - ASSERT( jCheck1 <= jCheck2 ); - const StepsID &stepsID = j->first; - const HighScoresForASteps &hsSteps = j->second; - - const HighScoreList &hsl = hsSteps.hsl; - - // skip steps that have never been played - if( hsl.GetNumTimesPlayed() == 0 ) - continue; + const float &rate = j->first; + const vector &hsv = j->second; - XNode* pStepsNode = pSongNode->AppendChild( stepsID.CreateNode() ); + XNode* pRateScores = new XNode(ssprintf("%f", rate)); + FOREACH_CONST(HighScore, hsv, hs) { + const HighScore &chs = *hs; + pRateScores->AppendChild(chs.CreateNode()); + } - pStepsNode->AppendChild( hsl.CreateNode() ); + pScores->AppendChild(pRateScores); } + pChartKey->AppendChild(pScores); + pNode->AppendChild(pChartKey); } return pNode; @@ -2103,6 +2216,16 @@ void Profile::LoadSongScoresFromNode( const XNode* pSongScores ) } } +// more future goalman stuff +void Profile::CreateGoal(RString ck) { + Goal goal; + goal.assigned = DateTime::GetNowDateTime(); + goal.chartkey = ck; + //goal.rate = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; + goalmap[ck].emplace_back(goal); + LOG->Trace("New goal created %i goals", goalmap[ck].size()); +} + /* This is really lame because for whatever reason getting the highscore object and passing them to lua results in really weird and unpredictable errors that I can't figure out, so instead we pass lua the keys and let the lua function get the highscore objects */ @@ -3417,6 +3540,19 @@ class LunaProfile : public Luna return 1; } + static int GetGoalByKey(T* p, lua_State *L) { + RString ck = SArg(1); + auto it = p->goalmap.find(ck); + if (it == p->goalmap.end()) { + lua_pushnil(L); + } + else { + p->goalmap[SArg(1)].front().PushSelf(L); + } + + return 1; + } + LunaProfile() { ADD_METHOD( AddScreenshot ); @@ -3498,10 +3634,25 @@ class LunaProfile : public Luna ADD_METHOD( RecalcTopSSR ); ADD_METHOD( GetPBHighScoreByKey ); ADD_METHOD( ValidateAllScores ); + ADD_METHOD( GetGoalByKey); } }; LUA_REGISTER_CLASS( Profile ) +class LunaGoal : public Luna +{ +public: + static int GetRate(T* p, lua_State *L) { + lua_pushnumber(L, p->rate); + return 1; + } + + LunaGoal() + { + ADD_METHOD( GetRate ); + } +}; +LUA_REGISTER_CLASS(Goal) // lua end diff --git a/src/Profile.h b/src/Profile.h index 13b9ef75d2..3da11a9338 100644 --- a/src/Profile.h +++ b/src/Profile.h @@ -73,6 +73,21 @@ enum ProfileType ProfileType_Invalid }; +// future goalman stuff - Mina +class Goal +{ +public: + float rate = 1.f; + float percent = 93.f; + int priority = 1; + DateTime assigned; + DateTime achieved; + RString comment = ""; + RString chartkey = ""; + + void PushSelf(lua_State *L); +}; + /** * @brief Player data that persists between sessions. * @@ -257,6 +272,10 @@ class Profile // Vector for now, we can make this more efficient later vector FavoritedCharts; + // more future goalman stuff + void CreateGoal(RString ck); + map> goalmap; + /* store arbitrary data for the theme within a profile */ LuaTable m_UserTable; @@ -337,6 +356,7 @@ class Profile typedef map> HighScoreRateMap; map HighScoresByChartKey; + // Screenshot Data vector m_vScreenshots; void AddScreenshot( const Screenshot &screenshot ); @@ -433,9 +453,12 @@ class Profile void SaveTypeToDir(const RString &dir) const; void SaveEditableDataToDir( const RString &sDir ) const; bool SaveStatsXmlToDir( RString sDir, bool bSignData ) const; + bool SaveEttXmlToDir(RString sDir, bool bSignData) const; XNode* SaveStatsXmlCreateNode() const; + XNode* SaveEttXmlCreateNode() const; XNode* SaveGeneralDataCreateNode() const; XNode* SaveSongScoresCreateNode() const; + XNode* SaveEttScoresCreateNode() const; XNode* SaveCourseScoresCreateNode() const; XNode* SaveCategoryScoresCreateNode() const; XNode* SaveScreenshotDataCreateNode() const;