From 0f1a9bdef5b2fc3fe1f1f61dc42552b487ad1eb5 Mon Sep 17 00:00:00 2001 From: Nickito12 Date: Tue, 26 Sep 2017 16:39:37 -0300 Subject: [PATCH] First concept of Profile DB --- src/CMakeData-data.cmake | 4 + src/CMakeLists.txt | 1 + src/DBProfile.cpp | 988 +++++++++++++++++++++++++++++++++++++++ src/DBProfile.h | 49 ++ src/HighScore.cpp | 3 +- src/HighScore.h | 3 +- src/Profile.cpp | 28 +- src/Profile.h | 7 +- src/ScoreManager.h | 2 + src/SongUtil.cpp | 8 +- src/SongUtil.h | 1 + src/StepsUtil.h | 2 + src/XMLProfile.cpp | 219 +++++---- src/XMLProfile.h | 42 +- 14 files changed, 1212 insertions(+), 145 deletions(-) create mode 100644 src/DBProfile.cpp create mode 100644 src/DBProfile.h diff --git a/src/CMakeData-data.cmake b/src/CMakeData-data.cmake index 134b73ff34..ee0394b980 100644 --- a/src/CMakeData-data.cmake +++ b/src/CMakeData-data.cmake @@ -170,6 +170,8 @@ list(APPEND SM_DATA_REST_SRC "PlayerState.cpp" "Preference.cpp" "Profile.cpp" + "XMLProfile.cpp" + "DBProfile.cpp" "RadarValues.cpp" "RandomSample.cpp" "SampleHistory.cpp" @@ -223,6 +225,8 @@ list(APPEND SM_DATA_REST_HPP "PlayerState.h" "Preference.h" "Profile.h" + "XMLProfile.h" + "DBProfile.h" "RadarValues.h" "RandomSample.h" "SampleHistory.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bce917c4ca..d5c9424f35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -329,6 +329,7 @@ list(APPEND SMDATA_LINK_LIB "png" "glew" "jpeg" + "sqlite3" "SQLiteCpp" ) include(../extern/CMakeProject-mmmagic.cmake) diff --git a/src/DBProfile.cpp b/src/DBProfile.cpp new file mode 100644 index 0000000000..1c8f4283dd --- /dev/null +++ b/src/DBProfile.cpp @@ -0,0 +1,988 @@ + +#include "global.h" +#include "Profile.h" +#include "RageLog.h" +#include "RageUtil.h" +#include "ProfileManager.h" +#include "NoteData.h" +#include "DBProfile.h" +#include "RageFile.h" +#include "RageFileDriverDeflate.h" +#include "GameState.h" +#include "GameManager.h" +#include "LuaManager.h" +#include "NoteData.h" +#include "RageFileManager.h" + +#include "ScoreManager.h" +#include "CryptManager.h" +#include "Song.h" +#include "SongManager.h" +#include "Steps.h" +#include "sqlite3.h" +#include "RageFileManager.h" +#include +#include + +const RString PROFILE_DB= "profile.db"; + +ProfileLoadResult DBProfile::LoadDBFromDir(RString dir, Profile* profile) +{ + loadingProfile = profile; + return LoadDBFromDir(dir); +} + +ProfileLoadResult DBProfile::LoadDBFromDir(RString dir) +{ + + SQLite::Database *db; + try { + // Open a database file + db = new SQLite::Database(FILEMAN->ResolvePath(dir) + PROFILE_DB, SQLite::OPEN_READWRITE); + }catch (std::exception& e) + { + return ProfileLoadResult_FailedNoProfile; + } + try { + // Use the db + if(!LoadGeneralData(db)) + return ProfileLoadResult_FailedTampered; + LoadFavourites(db); + LoadPermaMirrors(db); + LoadScoreGoals(db); + LoadPlayLists(db); + LoadPlayerScores(db); + } + catch (std::exception& e) + { + return ProfileLoadResult_FailedTampered; + } + delete db; + return ProfileLoadResult_Success; +} + +bool DBProfile::LoadGeneralData(SQLite::Database* db) +{ + SQLite::Statement gDataQuery(*db, "SELECT * FROM generaldata"); + + //Should only have one row so no executeStep loop + if(!gDataQuery.executeStep()) + return false; + loadingProfile->m_sDisplayName = static_cast(gDataQuery.getColumn(1)); + loadingProfile->m_sCharacterID = static_cast(gDataQuery.getColumn(2)); + loadingProfile->m_sLastUsedHighScoreName = static_cast(gDataQuery.getColumn(3)); + loadingProfile->m_sGuid = static_cast(gDataQuery.getColumn(4)); + loadingProfile->m_SortOrder = StringToSortOrder(static_cast(gDataQuery.getColumn(5))); + loadingProfile->m_LastDifficulty = static_cast(static_cast(gDataQuery.getColumn(6))); + loadingProfile->m_LastStepsType = GAMEMAN->StringToStepsType(static_cast(gDataQuery.getColumn(7))); + + const char* song = gDataQuery.getColumn(8); + if(song != nullptr && song!="") + loadingProfile->m_lastSong.LoadFromString(song); + loadingProfile->m_iCurrentCombo = gDataQuery.getColumn(9); + loadingProfile->m_iTotalSessions = gDataQuery.getColumn(10); + loadingProfile->m_iTotalSessionSeconds = gDataQuery.getColumn(11); + loadingProfile->m_iTotalGameplaySeconds = gDataQuery.getColumn(12); + loadingProfile->m_LastPlayedDate.FromString(static_cast(gDataQuery.getColumn(13))); + loadingProfile->m_iTotalDancePoints = gDataQuery.getColumn(14); + loadingProfile->m_iNumToasties = gDataQuery.getColumn(15); + loadingProfile->m_iTotalTapsAndHolds = gDataQuery.getColumn(16); + loadingProfile->m_iTotalJumps = gDataQuery.getColumn(17); + loadingProfile->m_iTotalHolds = gDataQuery.getColumn(18); + loadingProfile->m_iTotalRolls = gDataQuery.getColumn(19); + loadingProfile->m_iTotalMines = gDataQuery.getColumn(20); + loadingProfile->m_iTotalHands = gDataQuery.getColumn(21); + loadingProfile->m_iTotalLifts = gDataQuery.getColumn(22); + loadingProfile->m_fPlayerRating = static_cast(gDataQuery.getColumn(23)); + + SQLite::Statement modifierQuery(*db, "SELECT * FROM defaultmodifiers"); + while(modifierQuery.executeStep()) + { + const char* modifierName = modifierQuery.getColumn(1); + const char* modifierValue = modifierQuery.getColumn(2); + loadingProfile->m_sDefaultModifiers[modifierName] = modifierValue; + } + + SQLite::Statement skillsetsQuery(*db, "SELECT * FROM playerskillsets"); + while(skillsetsQuery.executeStep()) + { + int skillsetNum = skillsetsQuery.getColumn(1); + float skillsetValue = static_cast(skillsetsQuery.getColumn(2)); + loadingProfile->m_fPlayerSkillsets[skillsetNum] = skillsetValue; + } + + SQLite::Statement userTableQuery(*db, "SELECT * FROM usertable"); + + Lua *L = LUA->Get(); + + lua_newtable(L); + + //Only string values and no nested table support (TODO) + while (userTableQuery.executeStep()) + { + const char *key = userTableQuery.getColumn(0); + const char *value = userTableQuery.getColumn(1); + lua_pushstring(L, key); // push key + lua_pushstring(L, value); // push value + lua_settable(L, -3); + } + + loadingProfile->m_UserTable.SetFromStack(L); + LUA->Release(L); + + return true; +} +void DBProfile::LoadFavourites(SQLite::Database* db) +{ + SQLite::Statement query(*db, "SELECT chartkeys.chartkey FROM favourites INNER JOIN chartkeys ON favourites.chartkeyid = chartkeys.id"); + while (query.executeStep()) + { + const char* key = query.getColumn(0); + //loadingProfile->FavoritedCharts.emplace(SONGMAN->ReconcileBustedKeys(key)); + loadingProfile->FavoritedCharts.emplace(key); + } + SONGMAN->SetFavoritedStatus(loadingProfile->FavoritedCharts); + SONGMAN->MakePlaylistFromFavorites(loadingProfile->FavoritedCharts); +} + + +void DBProfile::LoadPlayLists(SQLite::Database* db) +{ + //First load playlists + SQLite::Statement query(*db, "SELECT playlists.name, chartkeys.chartkey, " + "charts.difficulty, songs.song, songs.pack, chartplaylists.rate " + "FROM chartplaylists INNER JOIN playlists ON chartplaylists.playlistid = playlists.id " + "INNER JOIN charts ON charts.id = chartplaylists.chartid " + "INNER JOIN songs ON songs.id = charts.songid " + "INNER JOIN chartkeys ON charts.chartkeyid = chartkeys.id " + "ORDER BY playlists.name, chartkeys.chartkey, chartplaylists.rate"); + auto& pls = SONGMAN->allplaylists; + + Playlist *tmp; + + + //Read one row + if (query.executeStep()) { + tmp = new Playlist; + tmp->name = static_cast(query.getColumn(0)); + + //Load chart + Chart ch; + ch.key = static_cast(query.getColumn(1)); + ch.lastdiff = static_cast(static_cast(query.getColumn(2))); + ch.lastsong = static_cast(query.getColumn(3)); + ch.lastpack = static_cast(query.getColumn(4)); + ch.rate = static_cast(query.getColumn(5)); + + // check if this chart is loaded and overwrite any last-seen values with updated ones + //ch.key = SONGMAN->ReconcileBustedKeys(ch.key); + ch.FromKey(ch.key); + + //Add chart to playlist + tmp->chartlist.emplace_back(ch); + } + else //Return if there are no palylists(There wont be course runs either) + return; + //Read the rest + while (query.executeStep()) + { + const char* curName = query.getColumn(0); + const char* key = query.getColumn(1); + + if (curName != tmp->name) { + //If the playlist changed add it and start a new one + pls.emplace(tmp->name, *tmp); + delete tmp; + tmp = new Playlist; + tmp->name = curName; + } + + //Load the chart + Chart ch; + + ch.lastdiff = static_cast(static_cast(query.getColumn(2))); + ch.lastsong = static_cast(query.getColumn(3)); + ch.lastpack = static_cast(query.getColumn(4)); + ch.rate = static_cast(query.getColumn(5)); + ch.key = key; + + // check if this chart is loaded and overwrite any last-seen values with updated ones + //ch.key = SONGMAN->ReconcileBustedKeys(ch.key); + ch.FromKey(ch.key); + + //Add chart to playlist + tmp->chartlist.emplace_back(ch); + + } + + pls.emplace(tmp->name, *tmp); + delete tmp; + SONGMAN->activeplaylist = tmp->name; + //Now read courseruns + + SQLite::Statement courseRunsQuery(*db, "SELECT runs.scorekey, courseruns.id, playlists.name " + "FROM runs INNER JOIN courseruns ON courseruns.id = runs.courserunid " + "INNER JOIN playlists ON playlists.id = courseruns.playlistid " + "ORDER BY runs.scorekey, courseruns.id, playlists.name"); + + string lastPlayListName=""; + int lastCourseRunID; + vector tmpCourseRun; + + //Read one row + if (courseRunsQuery.executeStep()) { + + const char* curScoreKey = query.getColumn(0); + lastCourseRunID = query.getColumn(1); + lastPlayListName = static_cast(query.getColumn(2)); + + tmpCourseRun.emplace_back(curScoreKey); + + } + else + return; + + //Read the rest + while (query.executeStep()) + { + const char* curScoreKey = query.getColumn(0); + int curCourseRunID = query.getColumn(1); + + if (lastCourseRunID != curCourseRunID) { + //If the courserun changed add it and start a new one + pls[lastPlayListName].courseruns.emplace_back(tmpCourseRun); + tmpCourseRun.clear(); + lastPlayListName = static_cast(query.getColumn(2)); + lastCourseRunID = curCourseRunID; + } + + tmpCourseRun.emplace_back(curScoreKey); + + } + pls[lastPlayListName].courseruns.emplace_back(tmpCourseRun); + tmpCourseRun.clear(); + return; +} + +void DBProfile::LoadPlayerScores(SQLite::Database* db) +{ + SQLite::Statement query(*db, "SELECT chartkeys.chartkey, " + "songs.song, songs.pack, charts.difficulty, " + "scoresatrates.rate, " // "scoresatrates.bestgrade, scoresatrates.pbkey, " + "scores.scorekey, scores.calcversion, " + "scores.grade, scores.wifescore, scores.ssrnormpercent, " + "scores.judgescale, scores.nochordcohesion, scores.etternavalid, " + "scores.surviveseconds, scores.maxcombo, scores.modifiers, scores.datetime, " + "scores.hitmine, scores.avoidmine, scores.miss, scores.w5, scores.w4, scores.w3, " + "scores.w2, scores.w1, scores.letgoholds, scores.heldholds, scores.missedholds, " + "scores.overall, scores.stream, scores.jumpstream, " + "scores.handstream, stamina, scores.jackspeed, " + "scores.jackstamina, scores.technical, scores.brittle, scores.weak FROM scores " + "INNER JOIN scoresatrates ON scoresatrates.id = scores.scoresatrateid " + "INNER JOIN charts ON charts.id=scoresatrates.chartid " + "INNER JOIN songs ON songs.id = charts.songid " + "INNER JOIN chartkeys ON chartkeys.id=charts.chartkeyid " + "ORDER BY chartkeys.id, charts.id, scoresatrates.id"); + + unordered_map& scores = *(SCOREMAN->GetProfileScores()); + + string curCK = ""; + + while (query.executeStep()) + { + const string key = static_cast(query.getColumn(0)); + if (key != curCK) + { + //Per Chart + curCK = key; + scores[key].ch.key = key; + scores[key].ch.lastsong = static_cast(query.getColumn(1)); + scores[key].ch.lastpack = static_cast(query.getColumn(2)); + scores[key].ch.lastdiff = static_cast(static_cast(query.getColumn(3))); + } + + int rate = query.getColumn(4); + + //Per Score + string ScoreKey = query.getColumn(5); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetSSRCalcVersion(query.getColumn(6)); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetGrade(static_cast(static_cast(query.getColumn(7)))); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetWifeScore(static_cast(query.getColumn(8))); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetSSRNormPercent(static_cast(query.getColumn(9))); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetMusicRate(scores[key].KeyToRate(rate)); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetJudgeScale(static_cast(query.getColumn(10))); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetChordCohesion(static_cast(query.getColumn(11))!=0); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetEtternaValid(static_cast(query.getColumn(12))!=0); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetChartKey(key); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetScoreKey(ScoreKey); + //TODO:surviveseconds + //scores[key].ScoresByRate[rate].scores[ScoreKey].SetSurviveSeconds(query.getColumn(13)); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetMaxCombo(query.getColumn(14)); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetModifiers(query.getColumn(15)); + DateTime d; + d.FromString(static_cast(query.getColumn(16))); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetDateTime(d); + + + int index = 17; + + FOREACH_ENUM(TapNoteScore, tns) + if (tns != TNS_None && tns != TNS_CheckpointMiss && tns != TNS_CheckpointHit) + scores[key].ScoresByRate[rate].scores[ScoreKey].SetTapNoteScore(tns, query.getColumn(index++)); + + FOREACH_ENUM(HoldNoteScore, hns) + if (hns != HNS_None) + scores[key].ScoresByRate[rate].scores[ScoreKey].SetHoldNoteScore(hns, query.getColumn(index++)); + + //int index = 28; + if (scores[key].ScoresByRate[rate].scores[ScoreKey].GetWifeScore() > 0.f) { + FOREACH_ENUM(Skillset, ss) + scores[key].ScoresByRate[rate].scores[ScoreKey].SetSkillsetSSR(ss, static_cast(query.getColumn(index++))); + } + scores[key].ScoresByRate[rate].scores[ScoreKey].SetValidationKey(ValidationKey_Brittle, static_cast(query.getColumn(index++))); + scores[key].ScoresByRate[rate].scores[ScoreKey].SetValidationKey(ValidationKey_Weak, static_cast(query.getColumn(index++))); + + //TODO: validation keys + + + if (scores[key].ScoresByRate[rate].scores[ScoreKey].GetScoreKey() == "") + scores[key].ScoresByRate[rate].scores[ScoreKey].SetScoreKey("S" + BinaryToHex(CryptManager::GetSHA1ForString(scores[key].ScoresByRate[rate].scores[ScoreKey].GetDateTime().GetString()))); + + // Validate input. + scores[key].ScoresByRate[rate].scores[ScoreKey].SetGrade(clamp(scores[key].ScoresByRate[rate].scores[ScoreKey].GetGrade(), Grade_Tier01, Grade_Failed)); + + + // Set any pb + if (scores[key].ScoresByRate[rate].PBptr == nullptr) + scores[key].ScoresByRate[rate].PBptr = &scores[key].ScoresByRate[rate].scores.find(ScoreKey)->second; + else { + // update pb if a better score is found + if (scores[key].ScoresByRate[rate].PBptr->GetWifeScore() < scores[key].ScoresByRate[rate].scores[ScoreKey].GetWifeScore()) + scores[key].ScoresByRate[rate].PBptr = &scores[key].ScoresByRate[rate].scores.find(ScoreKey)->second; + }; + + scores[key].ScoresByRate[rate].bestGrade = min(scores[key].ScoresByRate[rate].scores[ScoreKey].GetWifeGrade(), scores[key].ScoresByRate[rate].bestGrade); + + // Very awkward, need to figure this out better so there isn't unnecessary redundancy between loading and adding + SCOREMAN->RegisterScore(&scores[key].ScoresByRate[rate].scores.find(ScoreKey)->second); + SCOREMAN->AddToKeyedIndex(&scores[key].ScoresByRate[rate].scores.find(ScoreKey)->second); + + scores[key].bestGrade = min(scores[key].ScoresByRate[rate].bestGrade, scores[key].bestGrade); + } + /* +// Read scores from xml +void ScoresForChart::LoadFromNode(const XNode* node, const string& ck) { + RString rs = ""; + int rate; + + if(node->GetName() == "Chart") + ch.LoadFromNode(node); + + if (node->GetName() == "ChartScores") { + node->GetAttrValue("Key", rs); + ch.key = rs; + node->GetAttrValue("Song", ch.lastsong); + node->GetAttrValue("Pack", ch.lastpack); + } + + FOREACH_CONST_Child(node, p) { + ASSERT(p->GetName() == "ScoresAt"); + p->GetAttrValue("Rate", rs); + rate = 10 * StringToInt(rs.substr(0, 1) + rs.substr(2, 4)); + ScoresByRate[rate].LoadFromNode(p, ck, KeyToRate(rate)); + bestGrade = min(ScoresByRate[rate].bestGrade, bestGrade); + } +} +void ScoreManager::LoadFromNode(const XNode * node) { + FOREACH_CONST_Child(node, p) { + //ASSERT(p->GetName() == "Chart"); + RString tmp; + p->GetAttrValue("Key", tmp); + string doot = SONGMAN->ReconcileBustedKeys(tmp); + const string ck = doot; + pscores[ck].LoadFromNode(p, ck); + } + +void ScoresAtRate::LoadFromNode(const XNode* node, const string& ck, const float& rate) { + RString sk; + FOREACH_CONST_Child(node, p) { + p->GetAttrValue("Key", sk); + scores[sk].LoadFromEttNode(p); + + // Set any pb + if(PBptr == nullptr) + PBptr = &scores.find(sk)->second; + else { + // update pb if a better score is found + if (PBptr->GetWifeScore() < scores[sk].GetWifeScore()) + PBptr = &scores.find(sk)->second; + } + + // Fill in stuff for the highscores + scores[sk].SetChartKey(ck); + scores[sk].SetScoreKey(sk); + scores[sk].SetMusicRate(rate); + + bestGrade = min(scores[sk].GetWifeGrade(), bestGrade); + + // Very awkward, need to figure this out better so there isn't unnecessary redundancy between loading and adding + SCOREMAN->RegisterScore(&scores.find(sk)->second); + SCOREMAN->AddToKeyedIndex(&scores.find(sk)->second); + } +} +void HighScoreImpl::LoadFromEttNode(const XNode *pNode) { + //ASSERT(pNode->GetName() == "Score"); + + RString s; + pNode->GetChildValue("SSRCalcVersion", SSRCalcVersion); + pNode->GetChildValue("Grade", s); + grade = StringToGrade(s); + pNode->GetChildValue("WifeScore", fWifeScore); + pNode->GetChildValue("SSRNormPercent", fSSRNormPercent); + pNode->GetChildValue("Rate", fMusicRate); + pNode->GetChildValue("JudgeScale", fJudgeScale); + pNode->GetChildValue("NoChordCohesion", bNoChordCohesion); + pNode->GetChildValue("EtternaValid", bEtternaValid); + pNode->GetChildValue("SurviveSeconds", fSurviveSeconds); + pNode->GetChildValue("MaxCombo", iMaxCombo); + pNode->GetChildValue("Modifiers", s); sModifiers = s; + pNode->GetChildValue("DateTime", s); dateTime.FromString(s); + pNode->GetChildValue("ScoreKey", s); ScoreKey = s; + + const XNode* pTapNoteScores = pNode->GetChild("TapNoteScores"); + if (pTapNoteScores) + FOREACH_ENUM(TapNoteScore, tns) + pTapNoteScores->GetChildValue(TapNoteScoreToString(tns), iTapNoteScores[tns]); + + const XNode* pHoldNoteScores = pNode->GetChild("HoldNoteScores"); + if (pHoldNoteScores) + FOREACH_ENUM(HoldNoteScore, hns) + pHoldNoteScores->GetChildValue(HoldNoteScoreToString(hns), iHoldNoteScores[hns]); + + if (fWifeScore > 0.f) { + const XNode* pSkillsetSSRs = pNode->GetChild("SkillsetSSRs"); + if (pSkillsetSSRs) + FOREACH_ENUM(Skillset, ss) + pSkillsetSSRs->GetChildValue(SkillsetToString(ss), fSkillsetSSRs[ss]); + } + + if (fWifeScore > 0.f) { + const XNode* pValidationKeys = pNode->GetChild("ValidationKeys"); + if (pValidationKeys) { + pValidationKeys->GetChildValue(ValidationKeyToString(ValidationKey_Brittle), s); ValidationKeys[ValidationKey_Brittle] = s; + pValidationKeys->GetChildValue(ValidationKeyToString(ValidationKey_Weak), s); ValidationKeys[ValidationKey_Weak] = s; + } + } + + if (ScoreKey == "") + ScoreKey = "S" + BinaryToHex(CryptManager::GetSHA1ForString(dateTime.GetString())); + + // Validate input. + grade = clamp(grade, Grade_Tier01, Grade_Failed); +} + +*/ +} +void DBProfile::LoadPermaMirrors(SQLite::Database* db) +{ + SQLite::Statement query(*db, "SELECT chartkeys.chartkey FROM permamirrors INNER " + "JOIN chartkeys ON permamirrors.chartkeyid = chartkeys.id"); + while (query.executeStep()) + { + const char* key = query.getColumn(0); + //loadingProfile->PermaMirrorCharts.emplace(SONGMAN->ReconcileBustedKeys(key)); + loadingProfile->PermaMirrorCharts.emplace(key); + } + SONGMAN->SetPermaMirroredStatus(loadingProfile->PermaMirrorCharts); +} +void DBProfile::LoadScoreGoals(SQLite::Database* db) +{ + SQLite::Statement query(*db, "SELECT chartkeys.chartkey, scoregoals.rate, scoregoals.priority, scoregoals.percent, " + "scoregoals.timeassigned, scoregoals.timeachieved, scoregoals.scorekey, " + "scoregoals.comment FROM scoregoals INNER JOIN chartkeys ON " + "scoregoals.chartkeyid = chartkeys.id ORDER BY chartkeys.chartkey ASC"); + //string lastKey=""; + RString ck; + while (query.executeStep()) + { + ck = static_cast(query.getColumn(0)); + /* comment this for now (Also uncomment lastkey if uncommenting this) + //Check if its a new key or the one we were already working on + if (curKey != lastKey && curKey != ck) { + lastKey = curKey; + ck = SONGMAN->ReconcileBustedKeys(lastKey); + } + */ + + //Load the scoregoal + ScoreGoal sg; + sg.rate = static_cast(query.getColumn(1)); + sg.priority = query.getColumn(2); + sg.percent = static_cast(query.getColumn(3)); + sg.timeassigned.FromString(static_cast(query.getColumn(4))); + sg.timeachieved.FromString(static_cast(query.getColumn(5))); + sg.scorekey = static_cast(query.getColumn(6)); + sg.comment = static_cast(query.getColumn(7)); + //Add it to the GoalsForAChart goalmap[chart] + loadingProfile->goalmap[ck].Add(sg); + } + + SONGMAN->SetHasGoal(loadingProfile->goalmap); +} +ProfileLoadResult DBProfile::SaveDBToDir(RString dir, const Profile* profile) const +{ + SQLite::Database *db; + try { + // Open a database file + db = new SQLite::Database(FILEMAN->ResolvePath(dir) + PROFILE_DB, SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); + } + catch (std::exception& e) + { + return ProfileLoadResult_FailedNoProfile; + } + try { + // Use the db + // Begin transaction + SQLite::Transaction transaction(*db); + //We need to initialize these tables here + db->exec("CREATE TABLE IF NOT EXISTS chartkeys (id INTEGER PRIMARY KEY, chartkey TEXT)"); + db->exec("DROP TABLE IF EXISTS skillsets"); + db->exec("CREATE TABLE skillsets (skillsetnum INTEGER PRIMARY KEY, skillset TEXT)"); + FOREACH_ENUM(Skillset, ss) + db->exec("INSERT INTO skillsets VALUES (" + to_string(static_cast(ss)) + ", \"" + SkillsetToString(ss) + "\")"); + SaveGeneralData(db, profile); + SaveFavourites(db, profile); + SavePermaMirrors(db, profile); + SaveScoreGoals(db, profile); + //Make sure playlists are loaded after playerscores + SavePlayerScores(db, profile); + SavePlayLists(db, profile); + transaction.commit(); + } + catch (std::exception& e) + { + return ProfileLoadResult_FailedTampered; + } + delete db; + return ProfileLoadResult_Success; + +} + +void DBProfile::SaveFavourites(SQLite::Database* db, const Profile* profile) const +{; + + db->exec("DROP TABLE IF EXISTS favourites"); + db->exec("CREATE TABLE favourites (id INTEGER PRIMARY KEY, chartkeyid INTEGER, CONSTRAINT fk_chartkeyid FOREIGN KEY (chartkeyid) REFERENCES chartkeys(id))"); + if (!profile->FavoritedCharts.empty()) { + FOREACHS_CONST(string, profile->FavoritedCharts, ck) { + SQLite::Statement insertFav(*db, "INSERT INTO favourites VALUES (NULL, ?)"); + insertFav.bind(1, FindOrCreateChartKey(db, *ck)); + insertFav.exec(); + } + } + +} + + +void DBProfile::SaveGeneralData(SQLite::Database* db, const Profile* profile) const +{ + + db->exec("DROP TABLE IF EXISTS generaldata"); + db->exec("CREATE TABLE generaldata (id INTEGER PRIMARY KEY, " + "displayname TEXT, characterid TEXT, guid TEXT, sortorder TEXT, " + "lastdiff INTEGER, laststeps TEXT, lastsong TEXT, totalsessions INTEGER, totalsessionseconds INTEGER, " + "totalgameplayseconds INTEGER, lastplayedmachineguid TEXT, lastplayeddate DATE, " + "totaldancepoints INTEGER, numtoasties INTEGER, totaltapsandholds INTEGER, " + "totaljumps INTEGER, totalholds INTEGER, totalrolls INTEGER, totalmines INTEGER, " + "totalhands INTEGER, totallifts INTEGER, rating DOUBLE, totalsongsplayed INTEGER)"); + + SQLite::Statement insertGData(*db, "INSERT INTO generaldata VALUES (NULL, ?," + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + insertGData.bind(1, profile->GetDisplayNameOrHighScoreName()); + insertGData.bind(2, profile->m_sCharacterID); + insertGData.bind(3, profile->m_sGuid); + insertGData.bind(4, SortOrderToString(profile->m_SortOrder)); + insertGData.bind(5, profile->m_LastDifficulty); + insertGData.bind(6, + ((profile->m_LastStepsType != StepsType_Invalid && profile->m_LastStepsType < NUM_StepsType) ? + GAMEMAN->GetStepsTypeInfo(profile->m_LastStepsType).szName : "")); + insertGData.bind(7, profile->m_lastSong.ToString()); + insertGData.bind(8, profile->m_iTotalSessions); + insertGData.bind(9, profile->m_iTotalSessionSeconds); + insertGData.bind(10, profile->m_iTotalGameplaySeconds); + insertGData.bind(11, profile->m_sLastPlayedMachineGuid); + insertGData.bind(12, profile->m_LastPlayedDate.GetString()); + insertGData.bind(13, profile->m_iTotalDancePoints); + insertGData.bind(14, profile->m_iNumToasties); + insertGData.bind(15, profile->m_iTotalTapsAndHolds); + insertGData.bind(16, profile->m_iTotalJumps); + insertGData.bind(17, profile->m_iTotalHolds); + insertGData.bind(18, profile->m_iTotalRolls); + insertGData.bind(19, profile->m_iTotalMines); + insertGData.bind(20, profile->m_iTotalHands); + insertGData.bind(21, profile->m_iTotalLifts); + insertGData.bind(22, profile->m_fPlayerRating); + insertGData.bind(23, profile->m_iNumTotalSongsPlayed); + insertGData.exec(); + db->exec("DROP TABLE IF EXISTS defaultmodifiers"); + db->exec("CREATE TABLE defaultmodifiers (id INTEGER PRIMARY KEY, " + "name TEXT, value TEXT)"); + FOREACHM_CONST(RString, RString, profile->m_sDefaultModifiers, it) + db->exec("INSERT INTO defaultmodifiers VALUES (NULL, \"" + it->first + "\", \"" + it->second + "\")"); + + db->exec("DROP TABLE IF EXISTS playerskillsets"); + db->exec("CREATE TABLE playerskillsets (id INTEGER PRIMARY KEY, " + "skillsetnum INTEGER, value DOUBLE, " + "CONSTRAINT fk_skillsetnum FOREIGN KEY (skillsetnum) REFERENCES skillsets(skillsetnum))"); + + FOREACH_ENUM(Skillset, ss) + db->exec("INSERT INTO playerskillsets VALUES (NULL, " + to_string(ss) + + ", " + to_string(profile->m_fPlayerSkillsets[ss]) + ")"); + + db->exec("DROP TABLE IF EXISTS usertable"); + db->exec("CREATE TABLE usertable (id INTEGER PRIMARY KEY, " + "key TEXT, value TEXT)"); + /* TODO:UserTables + if (profile->m_UserTable.IsSet()) + { + Lua *L = LUA->Get(); + profile->m_UserTable.PushSelf(L); + //XNode* pUserTable = XmlFileUtil::XNodeFromTable(L); + //TODO + LUA->Release(L); + } + */ + + +} + + +void DBProfile::MoveBackupToDir(const RString &sFromDir, const RString &sToDir) +{ + if (FILEMAN->IsAFile(sFromDir + PROFILE_DB)) + FILEMAN->Move(sFromDir + PROFILE_DB, sToDir + PROFILE_DB); +} + +void DBProfile::SavePermaMirrors(SQLite::Database* db, const Profile* profile) const +{ + + db->exec("DROP TABLE IF EXISTS permamirrors"); + db->exec("CREATE TABLE permamirrors (id INTEGER PRIMARY KEY, chartkeyid INTEGER, CONSTRAINT fk_chartkeyid FOREIGN KEY (chartkeyid) REFERENCES chartkeys(id))"); + + if (!profile->PermaMirrorCharts.empty()) { + FOREACHS_CONST(string, profile->PermaMirrorCharts, it) { + int chID = FindOrCreateChartKey(db, *it); + db->exec("INSERT INTO permamirrors VALUES (NULL, " + to_string(chID) + ")"); + } + } +} + + +void DBProfile::SaveScoreGoals(SQLite::Database* db, const Profile* profile) const +{ + + db->exec("DROP TABLE IF EXISTS scoregoals"); + db->exec("CREATE TABLE scoregoals (id INTEGER PRIMARY KEY, chartkeyid INTEGER, " + "rate FLOAT, priority INTEGER, percent FLOAT, timeassigned DATE, timeachieved DATE, " + "scorekey TEXT, comment TEXT, CONSTRAINT fk_chartkeyid FOREIGN KEY (chartkeyid) REFERENCES chartkeys(id))"); + + if (!profile->goalmap.empty()) { + FOREACHUM_CONST(string, GoalsForChart, profile->goalmap, i) { + const GoalsForChart& cg = i->second; + if (cg.goals.empty()) + continue; + int chID = FindOrCreateChartKey(db, cg.goals[0].chartkey); + FOREACH_CONST(ScoreGoal, cg.goals, sg) { + SQLite::Statement insertScoreGoal(*db, "INSERT INTO scoregoals VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); + insertScoreGoal.bind(1, chID); + insertScoreGoal.bind(2, sg->rate); + insertScoreGoal.bind(3, sg->priority); + insertScoreGoal.bind(4, sg->percent); + insertScoreGoal.bind(5, sg->timeassigned.GetString()); + if (sg->achieved) { + insertScoreGoal.bind(6, sg->timeachieved.GetString()); + insertScoreGoal.bind(7, sg->scorekey); + } + else { + //bind null values + insertScoreGoal.bind(6); + insertScoreGoal.bind(7); + } + insertScoreGoal.bind(8, sg->comment); + insertScoreGoal.exec(); + } + } + } + +} + +void DBProfile::SavePlayLists(SQLite::Database* db, const Profile* profile) const +{ + + db->exec("DROP TABLE IF EXISTS playlists"); + db->exec("CREATE TABLE playlists (id INTEGER PRIMARY KEY, name TEXT)"); + + db->exec("DROP TABLE IF EXISTS chartplaylists"); + db->exec("CREATE TABLE chartplaylists (id INTEGER PRIMARY KEY, chartid INTEGER, " + "playlistid INTEGER, rate FLOAT, " + "CONSTRAINT fk_chartid FOREIGN KEY (chartid) REFERENCES charts(id), " + "CONSTRAINT fk_playlistid FOREIGN KEY (playlistid) REFERENCES playlists(id))"); + + db->exec("DROP TABLE IF EXISTS courseruns"); + db->exec("CREATE TABLE courseruns (id INTEGER PRIMARY KEY, playlistid INTEGER, " + "CONSTRAINT fk_playlistid FOREIGN KEY (playlistid) REFERENCES playlists(id))"); + + db->exec("DROP TABLE IF EXISTS runs"); + db->exec("CREATE TABLE runs (id INTEGER PRIMARY KEY, scorekey TEXT, " + "courserunid INTEGER, " + "CONSTRAINT fk_courserunid FOREIGN KEY (courserunid) REFERENCES courseruns(id))"); + + + auto& pls = SONGMAN->allplaylists; + if (!pls.empty()) { + FOREACHM(string, Playlist, pls, pl) + { + if (pl->first != "" && pl->first != "Favorites") + { + SQLite::Statement insertPlaylist(*db, "INSERT INTO playlists VALUES (NULL, ?)"); + insertPlaylist.bind(1, (pl->second).name); + insertPlaylist.exec(); + //db->exec("INSERT INTO playlists VALUES (NULL, \"" + (pl->second).name + "\")"); + int plID = sqlite3_last_insert_rowid(db->getHandle()); + FOREACH_CONST(Chart, (pl->second).chartlist, ch) + { + + int chartKeyID = FindOrCreateChartKey(db, ch->key); + int chartID; + + int songID = FindOrCreateSong(db, ch->lastpack, ch->lastsong); + + //Find or create chart now + //Check if chart already exists + SQLite::Statement query(*db, "SELECT * FROM charts WHERE chartkeyid=? AND songid=? AND difficulty=?"); + query.bind(1, chartKeyID); + query.bind(2, songID); + query.bind(3, ch->lastdiff); + //if not + if (!query.executeStep()) + { + //insert the chart + + + SQLite::Statement insertChart(*db, "INSERT INTO charts VALUES (NULL, ?, ?, ?)"); + + insertChart.bind(1, chartKeyID); + insertChart.bind(2, songID); + insertChart.bind(3, ch->lastdiff); + insertChart.exec(); + chartID = sqlite3_last_insert_rowid(db->getHandle()); + } + else { + chartID = query.getColumn(0); + } + SQLite::Statement insertChartPlaylist(*db, "INSERT INTO chartplaylists VALUES (NULL, ?, ?, ?)"); + insertChartPlaylist.bind(1, chartID); + insertChartPlaylist.bind(2, plID); + insertChartPlaylist.bind(3, ch->rate); + insertChartPlaylist.exec(); + } + + FOREACH_CONST(vector, (pl->second).courseruns, run) { + + SQLite::Statement insertCourseRun(*db, "INSERT INTO courseruns VALUES (NULL, ?)"); + insertCourseRun.bind(1, plID); + insertCourseRun.exec(); + int courseRunID = sqlite3_last_insert_rowid(db->getHandle()); + FOREACH_CONST(string, *run, scorekey) { + SQLite::Statement insertRun(*db, "INSERT INTO runs VALUES (NULL, ?, ?)"); + insertRun.bind(1, *scorekey); + insertRun.bind(2, courseRunID); + insertRun.exec(); + + } + } + } + } + } + +} + + +void DBProfile::SavePlayerScores(SQLite::Database* db, const Profile* profile) const +{ + + + db->exec("DROP TABLE IF EXISTS scores"); + db->exec("CREATE TABLE scores (id INTEGER PRIMARY KEY, scoresatrateid INTEGER, " + "scorekey TEXT, calcversion INT, grade INTEGER, wifescore FLOAT, " + "ssrnormpercent FLOAT, judgescale FLOAT, nochordcohesion INTEGER, " + "etternavalid INTEGER, surviveseconds FLOAT, maxcombo INTEGER, modifiers TEXT, datetime DATE, " + "hitmine INTEGER, avoidmine INTEGER, miss INTEGER, w5 INTEGER, w4 INTEGER, " + "w3 INTEGER, w2 INTEGER, w1 INTEGER, letgoholds INTEGER, heldholds INTEGER, " + "missedholds INTEGER, overall FLOAT, stream FLOAT, jumpstream FLOAT, " + "handstream FLOAT, stamina FLOAT, jackspeed FLOAT, " + "jackstamina FLOAT, technical FLOAT, brittle TEXT, weak TEXT, " + "CONSTRAINT fk_scoresatrateid FOREIGN KEY (scoresatrateid) REFERENCES scoresatrates(id))"); + + + //TODO: validation keys + + + db->exec("DROP TABLE IF EXISTS scoresatrates"); + db->exec("CREATE TABLE scoresatrates (id INTEGER PRIMARY KEY, chartid INTEGER, " + "rate INTEGER, bestgrade TEXT, pbkey TEXT, " + "CONSTRAINT fk_chartid FOREIGN KEY (chartid) REFERENCES charts(id))"); + + /* + db->exec("DROP TABLE IF EXISTS charts"); + db->exec("CREATE TABLE charts (id INTEGER PRIMARY KEY, chartkeyid INTEGER, " + "pack TEXT, song TEXT, difficulty INTEGER, " + "CONSTRAINT fk_chartkeyid FOREIGN KEY (chartkeyid) REFERENCES chartkeys(id))");*/ + db->exec("DROP TABLE IF EXISTS charts"); + db->exec("CREATE TABLE charts (id INTEGER PRIMARY KEY, chartkeyid INTEGER, " + "songid INTEGER, difficulty INTEGER, " + "CONSTRAINT fk_chartkeyid FOREIGN KEY (chartkeyid) REFERENCES chartkeys(id), " + "CONSTRAINT fk_songid FOREIGN KEY (songid) REFERENCES songs(id))"); + + db->exec("DROP TABLE IF EXISTS songs"); + db->exec("CREATE TABLE songs (id INTEGER PRIMARY KEY, " + "pack TEXT, song TEXT)"); + + unordered_map & pScores = *SCOREMAN->GetProfileScores(); + FOREACHUM_CONST(string, ScoresForChart, pScores, chartPair) { + // First is ckey and two is ScoresForChart + + Chart ch = ((chartPair->second).ch); + ch.FromKey(chartPair->first); + + //add chart ch + int chartKeyID = FindOrCreateChartKey(db, ch.key); + + int songID = FindOrCreateSong(db, ch.lastpack, ch.lastsong); + + SQLite::Statement insertChart(*db, "INSERT INTO charts VALUES (NULL, ?, ?, ?)"); + + insertChart.bind(1, chartKeyID); + insertChart.bind(2, songID); + insertChart.bind(3, ch.lastdiff); + + insertChart.exec(); + int chartID = sqlite3_last_insert_rowid(db->getHandle()); + + //Add scores per rate + FOREACHM_CONST(int, ScoresAtRate, chartPair->second.ScoresByRate, ratePair) { + //first is rate int and second is ScoresAtRate + int rate = ratePair->first; + SQLite::Statement insertScoresAtRate(*db, "INSERT INTO scoresatrates VALUES (NULL, ?, ?, ?, ?)"); + insertScoresAtRate.bind(1, chartID); + insertScoresAtRate.bind(2, rate ); + insertScoresAtRate.bind(3, GradeToString(ratePair->second.bestGrade)); + insertScoresAtRate.bind(4, ratePair->second.PBptr->GetScoreKey()); + insertScoresAtRate.exec(); + int scoresAtRateID = sqlite3_last_insert_rowid(db->getHandle()); + FOREACHUM_CONST(string, HighScore, ratePair->second.scores, i) { + // prune out sufficiently low scores + if (i->second.GetWifeScore() > SCOREMAN->minpercent) { + const HighScore* hs = &(i->second); + //Add scores + SQLite::Statement insertScore(*db, "INSERT INTO scores VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + insertScore.bind(1, scoresAtRateID); + insertScore.bind(2, (hs->GetScoreKey() == "" ? "S" + + BinaryToHex(CryptManager::GetSHA1ForString(hs->GetDateTime().GetString())) : + hs->GetScoreKey())); + insertScore.bind(3, hs->GetSSRCalcVersion()); + insertScore.bind(4, hs->GetWifeGrade()); + insertScore.bind(5, hs->GetWifeScore()); + insertScore.bind(6, hs->GetSSRNormPercent()); + insertScore.bind(7, hs->GetJudgeScale()); + insertScore.bind(8, hs->GetChordCohesion()); + insertScore.bind(9, hs->GetEtternaValid()); + insertScore.bind(10, hs->GetSurviveSeconds()); + insertScore.bind(11, hs->GetMaxCombo()); + insertScore.bind(12, hs->GetModifiers()); + insertScore.bind(13, hs->GetDateTime().GetString()); + + int index = 14; + FOREACH_ENUM(TapNoteScore, tns) + if (tns != TNS_None && tns != TNS_CheckpointMiss && tns != TNS_CheckpointHit) + insertScore.bind(index++, hs->GetTapNoteScore(tns)); + + FOREACH_ENUM(HoldNoteScore, hns) + if (hns != HNS_None) + insertScore.bind(index++, hs->GetHoldNoteScore(hns)); + + if (hs->GetWifeScore() > 0.f && hs->GetWifeGrade() != Grade_Failed && hs->GetSkillsetSSR(Skill_Overall) > 0.f) + FOREACH_ENUM(Skillset, ss) + insertScore.bind(index++, hs->GetSkillsetSSR(ss)); + else + FOREACH_ENUM(Skillset, ss) + insertScore.bind(index++, 0); + + insertScore.bind(index++, hs->GetValidationKey(ValidationKey_Brittle)); + insertScore.bind(index++, hs->GetValidationKey(ValidationKey_Weak)); + insertScore.exec(); + + /* + int scoreID = sqlite3_last_insert_rowid(db->getHandle()); + // dont bother writing skillset ssrs for non-applicable scores + if (hs->GetWifeScore() > 0.f && hs->GetWifeGrade() != Grade_Failed && hs->GetSkillsetSSR(Skill_Overall) > 0.f) {; + FOREACH_ENUM(Skillset, ss) { + SQLite::Statement insertSkillsetSSR(*db, "INSERT INTO skillsetssrs VALUES (NULL, " + to_string(scoreID) + ", ?, ?)"); + insertSkillsetSSR.bind(1, static_cast(ss)); + insertSkillsetSSR.bind(2, hs->GetSkillsetSSR(ss)); + insertSkillsetSSR.exec(); + } + } + */ + } + } + } + } +} + + +int DBProfile::GetChartKeyID(SQLite::Database* db, RString key) const +{ + SQLite::Statement query(*db, "SELECT * FROM chartkeys WHERE chartkey=?"); + query.bind(1, key); + if (!query.executeStep()) + return 0; + return query.getColumn(0); +} + +RString DBProfile::GetChartKeyByID(SQLite::Database* db, int id) const +{ + SQLite::Statement query(*db, "SELECT * FROM chartkeys WHERE id=?"); + query.bind(1, id); + if (!query.executeStep()) + return ""; + return static_cast(query.getColumn(1)); +} + + +int DBProfile::FindOrCreateChartKey(SQLite::Database* db, RString key) const +{ + int exists = GetChartKeyID(db, key); + if (exists) + return exists; + db->exec("INSERT INTO chartkeys VALUES (NULL, \"" + key + "\")"); + return sqlite3_last_insert_rowid(db->getHandle()); +} + + +int DBProfile::FindOrCreateSong(SQLite::Database* db, string pack, string song) const +{ + SQLite::Statement query(*db, "SELECT songs.id FROM songs WHERE song=? AND pack =?"); + query.bind(1, song); + query.bind(2, pack); + if (!query.executeStep()) { + SQLite::Statement insertSong(*db, "INSERT INTO songs VALUES (NULL, ?, ?)"); + + insertSong.bind(1, pack); + insertSong.bind(2, song); + insertSong.exec(); + return sqlite3_last_insert_rowid(db->getHandle()); + } + return query.getColumn(0); +} diff --git a/src/DBProfile.h b/src/DBProfile.h new file mode 100644 index 0000000000..6faee84e3c --- /dev/null +++ b/src/DBProfile.h @@ -0,0 +1,49 @@ +#ifndef Profile_DB +#define Profile_DB + +#include "global.h" +#include "GameConstantsAndTypes.h" +#include "HighScore.h" +#include "Profile.h" +#include "sqlite3.h" +#include +#include + + + +class DBProfile { +public: + + ProfileLoadResult LoadDBFromDir(RString dir); + ProfileLoadResult LoadDBFromDir(RString dir, Profile* profile); + + ProfileLoadResult SaveDBToDir(RString sDir, const Profile* profile) const; + + static void MoveBackupToDir(const RString &sFromDir, const RString &sToDir); + + void SetLoadingProfile(Profile* p) { loadingProfile = p; } +private: + Profile* loadingProfile; + int GetChartKeyID(SQLite::Database* db, RString key) const; + RString GetChartKeyByID(SQLite::Database* db, int id) const; + int FindOrCreateChartKey(SQLite::Database* db, RString key) const; + int FindOrCreateSong(SQLite::Database* db, string pack, string song) const; + + void LoadFavourites(SQLite::Database* db); + void LoadPlayLists(SQLite::Database* db); + void LoadPlayerScores(SQLite::Database* db); + bool LoadGeneralData(SQLite::Database* db); + void LoadPermaMirrors(SQLite::Database* db); + void LoadScoreGoals(SQLite::Database* db); + + void SaveFavourites(SQLite::Database* db, const Profile* profile) const; + void SavePlayLists(SQLite::Database* db, const Profile* profile) const; + void SavePlayerScores(SQLite::Database* db, const Profile* profile) const; + void SaveGeneralData(SQLite::Database* db, const Profile* profile) const; + void SavePermaMirrors(SQLite::Database* db, const Profile* profile) const; + void SaveScoreGoals(SQLite::Database* db, const Profile* profile) const; + +}; + + +#endif \ No newline at end of file diff --git a/src/HighScore.cpp b/src/HighScore.cpp index 23427e8c85..cfc4ab8505 100644 --- a/src/HighScore.cpp +++ b/src/HighScore.cpp @@ -793,6 +793,7 @@ void HighScore::SetHoldNoteScore( HoldNoteScore hns, int i ) { m_Impl->iHoldNote void HighScore::SetSkillsetSSR(Skillset ss, float ssr) { m_Impl->fSkillsetSSRs[ss] = ssr; } void HighScore::SetValidationKey(ValidationKey vk, string k) { m_Impl->ValidationKeys[vk] = k; } void HighScore::SetTopScore(int i) { m_Impl->TopScore = i; } +string HighScore::GetValidationKey(ValidationKey vk) const { return m_Impl->ValidationKeys[vk]; } void HighScore::SetRadarValues( const RadarValues &rv ) { m_Impl->radarValues = rv; } void HighScore::SetLifeRemainingSeconds( float f ) { m_Impl->fLifeRemainingSeconds = f; } void HighScore::SetDisqualified( bool b ) { m_Impl->bDisqualified = b; } @@ -1157,7 +1158,7 @@ vector HighScore::GetRescoreJudgeVector(int x) { return m_Impl->vRescoreJudgeVector; } -Grade HighScore::GetWifeGrade() { +Grade HighScore::GetWifeGrade() const { return m_Impl->GetWifeGrade(); } diff --git a/src/HighScore.h b/src/HighScore.h index 766bc80e56..2169d03936 100644 --- a/src/HighScore.h +++ b/src/HighScore.h @@ -39,7 +39,7 @@ struct HighScore * @brief Determine if any judgments were tallied during this run. * @return true if no judgments were recorded, false otherwise. */ bool IsEmpty() const; - Grade GetWifeGrade(); + Grade GetWifeGrade() const; float ConvertDpToWife(); float GetPercentDP() const; float GetWifeScore() const; @@ -152,6 +152,7 @@ struct HighScore void SetSkillsetSSR(Skillset ss, float ssr); void SetValidationKey(ValidationKey vk, string k); void SetTopScore(int i); + string GetValidationKey(ValidationKey vk) const; vector GetRescoreJudgeVector(int x); // Lua void PushSelf( lua_State *L ); diff --git a/src/Profile.cpp b/src/Profile.cpp index 36e513f9da..efe759066f 100644 --- a/src/Profile.cpp +++ b/src/Profile.cpp @@ -92,7 +92,7 @@ void Profile::ClearStats() RString Profile::MakeGuid() { - RString s; + string s; s.reserve( GUID_SIZE_BYTES*2 ); unsigned char buf[GUID_SIZE_BYTES]; CryptManager::GetRandomBytes( buf, GUID_SIZE_BYTES ); @@ -791,16 +791,21 @@ ProfileLoadResult Profile::LoadAllFromDir( const RString &sDir, bool bRequireSig LoadTypeFromDir(sDir); // Not critical if this fails LoadEditableDataFromDir( sDir ); - - ProfileLoadResult ret= XMLProf.LoadEttFromDir(sDir); + DBProf.SetLoadingProfile(this); + XMLProf.SetLoadingProfile(this); + ProfileLoadResult ret = DBProf.LoadDBFromDir(sDir); if (ret != ProfileLoadResult_Success) { - ret = LoadStatsFromDir(sDir, bRequireSignature); + ret = XMLProf.LoadEttFromDir(sDir); + ProfileLoadResult ret = XMLProf.LoadEttFromDir(sDir); + if (ret != ProfileLoadResult_Success) { + ret = XMLProf.LoadStatsFromDir(sDir, bRequireSignature); - if (ret != ProfileLoadResult_Success) - return ret; + if (ret != ProfileLoadResult_Success) + return ret; - IsEtternaProfile = true; - ImportScoresToEtterna(); + IsEtternaProfile = true; + ImportScoresToEtterna(); + } } // move old profile specific replays to the new aggregate folder @@ -932,9 +937,12 @@ bool Profile::SaveAllToDir( const RString &sDir, bool bSignData ) const SaveTypeToDir(sDir); // Save editable.ini SaveEditableDataToDir( sDir ); - - bool bSaved = XMLProf.SaveEttXmlToDir(sDir); + + GAMESTATE->SaveCurrentSettingsToProfile(PLAYER_1); + + bool bSaved = XMLProf.SaveEttXmlToDir(sDir, this); SaveStatsWebPageToDir( sDir ); + bool dbSabed = DBProf.SaveDBToDir(sDir, this); // Empty directories if none exist. FILEMAN->CreateDir( sDir + SCREENSHOTS_SUBDIR ); diff --git a/src/Profile.h b/src/Profile.h index 8f08d8ee6b..d460e82ed1 100644 --- a/src/Profile.h +++ b/src/Profile.h @@ -13,6 +13,7 @@ #include "StyleUtil.h" // for StyleID #include "LuaReference.h" #include "XMLProfile.h" +#include "DBProfile.h" #include "arch/LoadingWindow/LoadingWindow.h" #include @@ -163,7 +164,6 @@ class Profile FOREACH_ENUM( StepsType,st ) FOREACH_ENUM( RankingCategory,rc ) m_CategoryHighScores[st][rc].Init(); - XMLProf.profile = this; } // smart accessors @@ -199,7 +199,7 @@ class Profile // General data static RString MakeGuid(); - + RString* GetGuid() { return &m_sGuid; } RString m_sGuid; map m_sDefaultModifiers; SortOrder m_SortOrder{SortOrder_Invalid}; @@ -364,13 +364,14 @@ class Profile static RString MakeUniqueFileNameNoExtension( const RString &sDir, const RString &sFileNameBeginning ); static RString MakeFileNameNoExtension( const RString &sFileNameBeginning, int iIndex ); - XMLProfile XMLProf; // Lua void PushSelf( lua_State *L ); private: const HighScoresForASong *GetHighScoresForASong( const SongID& songID ) const; + XMLProfile XMLProf; + DBProfile DBProf; }; diff --git a/src/ScoreManager.h b/src/ScoreManager.h index 764144ece7..1e740f62f0 100644 --- a/src/ScoreManager.h +++ b/src/ScoreManager.h @@ -136,6 +136,8 @@ class ScoreManager void SetAllTopScores(); void PurgeScores(); + unordered_map* GetProfileScores() { return &pscores; }; + private: unordered_map pscores; // Profile scores diff --git a/src/SongUtil.cpp b/src/SongUtil.cpp index 2756bfeebd..cfb416383f 100644 --- a/src/SongUtil.cpp +++ b/src/SongUtil.cpp @@ -1077,9 +1077,15 @@ void SongID::LoadFromNode( const XNode* pNode ) m_Cache.Unset(); } +void SongID::LoadFromString(const char * dir) +{ + sDir = dir; + m_Cache.Unset(); +} + RString SongID::ToString() const { - return sDir; + return (sDir.empty() ? RString() : sDir); } bool SongID::IsValid() const diff --git a/src/SongUtil.h b/src/SongUtil.h index e0a41143a3..efbf1012c2 100644 --- a/src/SongUtil.h +++ b/src/SongUtil.h @@ -215,6 +215,7 @@ class SongID XNode* CreateNode() const; void LoadFromNode( const XNode* pNode ); + void LoadFromString(const char * dir); void FromString( RString _sDir ) { sDir = _sDir; } RString ToString() const; bool IsValid() const; diff --git a/src/StepsUtil.h b/src/StepsUtil.h index 5f9ad891a5..d996f062da 100644 --- a/src/StepsUtil.h +++ b/src/StepsUtil.h @@ -201,6 +201,8 @@ class StepsID StepsType GetStepsType() const { return st; } Difficulty GetDifficulty() const { return dc; } RString GetKey() const { return ck; } + RString GetDescription() const { return (dc == Difficulty_Edit ? sDescription : RString()); } + unsigned GetHash() const { return uHash; } }; #endif diff --git a/src/XMLProfile.cpp b/src/XMLProfile.cpp index 5f10d7a9e2..cd97b683ab 100644 --- a/src/XMLProfile.cpp +++ b/src/XMLProfile.cpp @@ -123,7 +123,7 @@ ProfileLoadResult XMLProfile::LoadStatsFromDir(RString dir, bool require_signatu ProfileLoadResult XMLProfile::LoadEttFromDir(RString dir) { dir += PROFILEMAN->GetStatsPrefix(); profiledir = dir; - profile->IsEtternaProfile = true; + loadingProfile->IsEtternaProfile = true; RString fn = dir + ETT_XML; bool compressed = false; if (!IsAFile(fn)) { @@ -160,10 +160,10 @@ ProfileLoadResult XMLProfile::LoadEttFromDir(RString dir) { return LoadEttXmlFromNode(&xml); } -bool XMLProfile::SaveStatsXmlToDir(RString sDir, bool bSignData) +bool XMLProfile::SaveStatsXmlToDir(RString sDir, bool bSignData, const Profile* profile) { LOG->Trace("SaveStatsXmlToDir: %s", sDir.c_str()); - unique_ptr xml(SaveStatsXmlCreateNode()); + unique_ptr xml(SaveStatsXmlCreateNode(profile)); sDir += PROFILEMAN->GetStatsPrefix(); // Save stats.xml @@ -216,9 +216,9 @@ bool XMLProfile::SaveStatsXmlToDir(RString sDir, bool bSignData) return true; } -bool XMLProfile::SaveEttXmlToDir(RString sDir) const { +bool XMLProfile::SaveEttXmlToDir(RString sDir, const Profile* profile) const { LOG->Trace("Saving Etterna Profile to: %s", sDir.c_str()); - unique_ptr xml(SaveEttXmlCreateNode()); + unique_ptr xml(SaveEttXmlCreateNode(profile)); sDir += PROFILEMAN->GetStatsPrefix(); // Save Etterna.xml RString fn = sDir + ETT_XML; @@ -259,7 +259,7 @@ bool XMLProfile::SaveEttXmlToDir(RString sDir) const { return true; } -XNode* XMLProfile::SaveGeneralDataCreateNode() const +XNode* XMLProfile::SaveGeneralDataCreateNode(const Profile* profile) const { XNode* pGeneralDataNode = new XNode("GeneralData"); @@ -400,7 +400,7 @@ XNode* XMLProfile::SaveGeneralDataCreateNode() const return pGeneralDataNode; } -XNode* XMLProfile::SaveFavoritesCreateNode() const { +XNode* XMLProfile::SaveFavoritesCreateNode(const Profile* profile) const { CHECKPOINT_M("Saving the favorites node."); XNode* favs = new XNode("Favorites"); @@ -409,7 +409,7 @@ XNode* XMLProfile::SaveFavoritesCreateNode() const { return favs; } -XNode* XMLProfile::SavePermaMirrorCreateNode() const { +XNode* XMLProfile::SavePermaMirrorCreateNode(const Profile* profile) const { CHECKPOINT_M("Saving the permamirror node."); XNode* pmir = new XNode("PermaMirror"); @@ -429,7 +429,7 @@ XNode* GoalsForChart::CreateNode() const { return cg; } -XNode* XMLProfile::SaveScoreGoalsCreateNode() const { +XNode* XMLProfile::SaveScoreGoalsCreateNode(const Profile* profile) const { CHECKPOINT_M("Saving the scoregoals node."); XNode* goals = new XNode("ScoreGoals"); @@ -440,7 +440,7 @@ XNode* XMLProfile::SaveScoreGoalsCreateNode() const { return goals; } -XNode* XMLProfile::SavePlaylistsCreateNode() const { +XNode* XMLProfile::SavePlaylistsCreateNode(const Profile* profile) const { CHECKPOINT_M("Saving the playlists node."); XNode* playlists = new XNode("Playlists"); @@ -455,19 +455,19 @@ void XMLProfile::LoadFavoritesFromNode(const XNode *pNode) { CHECKPOINT_M("Loading the favorites node."); FOREACH_CONST_Child(pNode, ck) - profile->FavoritedCharts.emplace(SONGMAN->ReconcileBustedKeys(ck->GetName())); + loadingProfile->FavoritedCharts.emplace(SONGMAN->ReconcileBustedKeys(ck->GetName())); - SONGMAN->SetFavoritedStatus(profile->FavoritedCharts); - SONGMAN->MakePlaylistFromFavorites(profile->FavoritedCharts); + SONGMAN->SetFavoritedStatus(loadingProfile->FavoritedCharts); + SONGMAN->MakePlaylistFromFavorites(loadingProfile->FavoritedCharts); } void XMLProfile::LoadPermaMirrorFromNode(const XNode *pNode) { CHECKPOINT_M("Loading the permamirror node."); FOREACH_CONST_Child(pNode, ck) - profile->PermaMirrorCharts.emplace(SONGMAN->ReconcileBustedKeys(ck->GetName())); + loadingProfile->PermaMirrorCharts.emplace(SONGMAN->ReconcileBustedKeys(ck->GetName())); - SONGMAN->SetPermaMirroredStatus(profile->PermaMirrorCharts); + SONGMAN->SetPermaMirroredStatus(loadingProfile->PermaMirrorCharts); } void GoalsForChart::LoadFromNode(const XNode *pNode) { @@ -485,9 +485,9 @@ void XMLProfile::LoadScoreGoalsFromNode(const XNode *pNode) { FOREACH_CONST_Child(pNode, chgoals) { chgoals->GetAttrValue("Key", ck); ck = SONGMAN->ReconcileBustedKeys(ck); - profile->goalmap[ck].LoadFromNode(chgoals); + loadingProfile->goalmap[ck].LoadFromNode(chgoals); } - SONGMAN->SetHasGoal(profile->goalmap); + SONGMAN->SetHasGoal(loadingProfile->goalmap); } void XMLProfile::LoadPlaylistsFromNode(const XNode *pNode) { @@ -503,7 +503,7 @@ void XMLProfile::LoadPlaylistsFromNode(const XNode *pNode) { } -XNode* XMLProfile::SaveEttGeneralDataCreateNode() const { +XNode* XMLProfile::SaveEttGeneralDataCreateNode(const Profile* profile) const { CHECKPOINT_M("Saving the general node."); XNode* pGeneralDataNode = new XNode("GeneralData"); @@ -535,8 +535,6 @@ XNode* XMLProfile::SaveEttGeneralDataCreateNode() const { pGeneralDataNode->AppendChild("TotalLifts", profile->m_iTotalLifts); pGeneralDataNode->AppendChild("PlayerRating", profile->m_fPlayerRating); - // apparently this got ripped out in the course of streamlining things -mina - GAMESTATE->SaveCurrentSettingsToProfile(PLAYER_1); // Keep declared variables in a very local scope so they aren't // accidentally used where they're not intended. There's a lot of @@ -643,32 +641,33 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) RString s; const XNode* pTemp; - pNode->GetChildValue("DisplayName", profile->m_sDisplayName); - pNode->GetChildValue("CharacterID", profile->m_sCharacterID); - pNode->GetChildValue("LastUsedHighScoreName", profile->m_sLastUsedHighScoreName); - pNode->GetChildValue("Guid", profile->m_sGuid); - pNode->GetChildValue("SortOrder", s); profile->m_SortOrder = StringToSortOrder(s); - pNode->GetChildValue("LastDifficulty", s); profile->m_LastDifficulty = StringToDifficulty(s); - pNode->GetChildValue("LastStepsType", s); profile->m_LastStepsType = GAMEMAN->StringToStepsType(s); - pTemp = pNode->GetChild("Song"); if (pTemp) profile->m_lastSong.LoadFromNode(pTemp); - pNode->GetChildValue("CurrentCombo", profile->m_iCurrentCombo); - pNode->GetChildValue("TotalSessions", profile->m_iTotalSessions); - pNode->GetChildValue("TotalSessionSeconds", profile->m_iTotalSessionSeconds); - pNode->GetChildValue("TotalGameplaySeconds", profile->m_iTotalGameplaySeconds); - pNode->GetChildValue("LastPlayedMachineGuid", profile->m_sLastPlayedMachineGuid); - pNode->GetChildValue("LastPlayedDate", s); profile->m_LastPlayedDate.FromString(s); - pNode->GetChildValue("TotalDancePoints", profile->m_iTotalDancePoints); - pNode->GetChildValue("NumExtraStagesPassed", profile->m_iNumExtraStagesPassed); - pNode->GetChildValue("NumExtraStagesFailed", profile->m_iNumExtraStagesFailed); - pNode->GetChildValue("NumToasties", profile->m_iNumToasties); - pNode->GetChildValue("TotalTapsAndHolds", profile->m_iTotalTapsAndHolds); - pNode->GetChildValue("TotalJumps", profile->m_iTotalJumps); - pNode->GetChildValue("TotalHolds", profile->m_iTotalHolds); - pNode->GetChildValue("TotalRolls", profile->m_iTotalRolls); - pNode->GetChildValue("TotalMines", profile->m_iTotalMines); - pNode->GetChildValue("TotalHands", profile->m_iTotalHands); - pNode->GetChildValue("TotalLifts", profile->m_iTotalLifts); - pNode->GetChildValue("PlayerRating", profile->m_fPlayerRating); + pNode->GetChildValue("DisplayName", loadingProfile->m_sDisplayName); + pNode->GetChildValue("CharacterID", loadingProfile->m_sCharacterID); + pNode->GetChildValue("LastUsedHighScoreName", loadingProfile->m_sLastUsedHighScoreName); + pNode->GetChildValue("Guid", s); + loadingProfile->m_sGuid = s; + pNode->GetChildValue("SortOrder", s); loadingProfile->m_SortOrder = StringToSortOrder(s); + pNode->GetChildValue("LastDifficulty", s); loadingProfile->m_LastDifficulty = StringToDifficulty(s); + pNode->GetChildValue("LastStepsType", s); loadingProfile->m_LastStepsType = GAMEMAN->StringToStepsType(s); + pTemp = pNode->GetChild("Song"); if (pTemp) loadingProfile->m_lastSong.LoadFromNode(pTemp); + pNode->GetChildValue("CurrentCombo", loadingProfile->m_iCurrentCombo); + pNode->GetChildValue("TotalSessions", loadingProfile->m_iTotalSessions); + pNode->GetChildValue("TotalSessionSeconds", loadingProfile->m_iTotalSessionSeconds); + pNode->GetChildValue("TotalGameplaySeconds", loadingProfile->m_iTotalGameplaySeconds); + pNode->GetChildValue("LastPlayedMachineGuid", loadingProfile->m_sLastPlayedMachineGuid); + pNode->GetChildValue("LastPlayedDate", s); loadingProfile->m_LastPlayedDate.FromString(s); + pNode->GetChildValue("TotalDancePoints", loadingProfile->m_iTotalDancePoints); + pNode->GetChildValue("NumExtraStagesPassed", loadingProfile->m_iNumExtraStagesPassed); + pNode->GetChildValue("NumExtraStagesFailed", loadingProfile->m_iNumExtraStagesFailed); + pNode->GetChildValue("NumToasties", loadingProfile->m_iNumToasties); + pNode->GetChildValue("TotalTapsAndHolds", loadingProfile->m_iTotalTapsAndHolds); + pNode->GetChildValue("TotalJumps", loadingProfile->m_iTotalJumps); + pNode->GetChildValue("TotalHolds", loadingProfile->m_iTotalHolds); + pNode->GetChildValue("TotalRolls", loadingProfile->m_iTotalRolls); + pNode->GetChildValue("TotalMines", loadingProfile->m_iTotalMines); + pNode->GetChildValue("TotalHands", loadingProfile->m_iTotalHands); + pNode->GetChildValue("TotalLifts", loadingProfile->m_iTotalLifts); + pNode->GetChildValue("PlayerRating", loadingProfile->m_fPlayerRating); { const XNode* pDefaultModifiers = pNode->GetChild("DefaultModifiers"); @@ -676,7 +675,7 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) { FOREACH_CONST_Child(pDefaultModifiers, game_type) { - game_type->GetTextValue(profile->m_sDefaultModifiers[game_type->GetName()]); + game_type->GetTextValue(loadingProfile->m_sDefaultModifiers[game_type->GetName()]); } } } @@ -687,13 +686,13 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) FOREACH_CONST_Child(pFavorites, ck) { RString tmp = ck->GetName(); // handle duplicated entries caused by an oversight - mina bool duplicated = false; - FOREACHS(string, profile->FavoritedCharts, chartkey) + FOREACHS(string, loadingProfile->FavoritedCharts, chartkey) if (*chartkey == tmp) duplicated = true; if (!duplicated) - profile->FavoritedCharts.emplace(tmp); + loadingProfile->FavoritedCharts.emplace(tmp); } - SONGMAN->SetFavoritedStatus(profile->FavoritedCharts); + SONGMAN->SetFavoritedStatus(loadingProfile->FavoritedCharts); } } @@ -701,7 +700,7 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) const XNode* pPlayerSkillsets = pNode->GetChild("PlayerSkillsets"); if (pPlayerSkillsets) { FOREACH_ENUM(Skillset, ss) - pPlayerSkillsets->GetChildValue(SkillsetToString(ss), profile->m_fPlayerSkillsets[ss]); + pPlayerSkillsets->GetChildValue(SkillsetToString(ss), loadingProfile->m_fPlayerSkillsets[ss]); } } @@ -709,7 +708,7 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) const XNode* pNumSongsPlayedByPlayMode = pNode->GetChild("NumSongsPlayedByPlayMode"); if (pNumSongsPlayedByPlayMode) FOREACH_ENUM(PlayMode, pm) - pNumSongsPlayedByPlayMode->GetChildValue(PlayModeToString(pm), profile->m_iNumSongsPlayedByPlayMode[pm]); + pNumSongsPlayedByPlayMode->GetChildValue(PlayModeToString(pm), loadingProfile->m_iNumSongsPlayedByPlayMode[pm]); } { @@ -727,7 +726,7 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) if (!sID.IsValid()) WARN_AND_CONTINUE; - style->GetTextValue(profile->m_iNumSongsPlayedByStyle[sID]); + style->GetTextValue(loadingProfile->m_iNumSongsPlayedByStyle[sID]); } } } @@ -736,30 +735,30 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) const XNode* pNumSongsPlayedByDifficulty = pNode->GetChild("NumSongsPlayedByDifficulty"); if (pNumSongsPlayedByDifficulty) FOREACH_ENUM(Difficulty, dc) - pNumSongsPlayedByDifficulty->GetChildValue(DifficultyToString(dc), profile->m_iNumSongsPlayedByDifficulty[dc]); + pNumSongsPlayedByDifficulty->GetChildValue(DifficultyToString(dc), loadingProfile->m_iNumSongsPlayedByDifficulty[dc]); } { const XNode* pNumSongsPlayedByMeter = pNode->GetChild("NumSongsPlayedByMeter"); if (pNumSongsPlayedByMeter) for (int i = 0; iGetChildValue(ssprintf("Meter%d", i), profile->m_iNumSongsPlayedByMeter[i]); + pNumSongsPlayedByMeter->GetChildValue(ssprintf("Meter%d", i), loadingProfile->m_iNumSongsPlayedByMeter[i]); } - pNode->GetChildValue("NumTotalSongsPlayed", profile->m_iNumTotalSongsPlayed); + pNode->GetChildValue("NumTotalSongsPlayed", loadingProfile->m_iNumTotalSongsPlayed); { const XNode* pNumStagesPassedByGrade = pNode->GetChild("NumStagesPassedByGrade"); if (pNumStagesPassedByGrade) FOREACH_ENUM(Grade, g) - pNumStagesPassedByGrade->GetChildValue(GradeToString(g), profile->m_iNumStagesPassedByGrade[g]); + pNumStagesPassedByGrade->GetChildValue(GradeToString(g), loadingProfile->m_iNumStagesPassedByGrade[g]); } { const XNode* pNumStagesPassedByPlayMode = pNode->GetChild("NumStagesPassedByPlayMode"); if (pNumStagesPassedByPlayMode) FOREACH_ENUM(PlayMode, pm) - pNumStagesPassedByPlayMode->GetChildValue(PlayModeToString(pm), profile->m_iNumStagesPassedByPlayMode[pm]); + pNumStagesPassedByPlayMode->GetChildValue(PlayModeToString(pm), loadingProfile->m_iNumStagesPassedByPlayMode[pm]); } @@ -773,7 +772,7 @@ void XMLProfile::LoadGeneralDataFromNode(const XNode* pNode) else lua_newtable(L); - profile->m_UserTable.SetFromStack(L); + loadingProfile->m_UserTable.SetFromStack(L); LUA->Release(L); } @@ -786,29 +785,29 @@ void XMLProfile::LoadEttGeneralDataFromNode(const XNode* pNode) { RString s; const XNode* pTemp; - pNode->GetChildValue("DisplayName", profile->m_sDisplayName); - pNode->GetChildValue("CharacterID", profile->m_sCharacterID); - pNode->GetChildValue("LastUsedHighScoreName", profile->m_sLastUsedHighScoreName); - pNode->GetChildValue("Guid", profile->m_sGuid); - pNode->GetChildValue("SortOrder", s); profile->m_SortOrder = StringToSortOrder(s); - pNode->GetChildValue("LastDifficulty", s); profile->m_LastDifficulty = StringToDifficulty(s); - pNode->GetChildValue("LastStepsType", s); profile->m_LastStepsType = GAMEMAN->StringToStepsType(s); - pTemp = pNode->GetChild("Song"); if (pTemp) profile->m_lastSong.LoadFromNode(pTemp); - pNode->GetChildValue("CurrentCombo", profile->m_iCurrentCombo); - pNode->GetChildValue("TotalSessions", profile->m_iTotalSessions); - pNode->GetChildValue("TotalSessionSeconds", profile->m_iTotalSessionSeconds); - pNode->GetChildValue("TotalGameplaySeconds", profile->m_iTotalGameplaySeconds); - pNode->GetChildValue("LastPlayedDate", s); profile->m_LastPlayedDate.FromString(s); - pNode->GetChildValue("TotalDancePoints", profile->m_iTotalDancePoints); - pNode->GetChildValue("NumToasties", profile->m_iNumToasties); - pNode->GetChildValue("TotalTapsAndHolds", profile->m_iTotalTapsAndHolds); - pNode->GetChildValue("TotalJumps", profile->m_iTotalJumps); - pNode->GetChildValue("TotalHolds", profile->m_iTotalHolds); - pNode->GetChildValue("TotalRolls", profile->m_iTotalRolls); - pNode->GetChildValue("TotalMines", profile->m_iTotalMines); - pNode->GetChildValue("TotalHands", profile->m_iTotalHands); - pNode->GetChildValue("TotalLifts", profile->m_iTotalLifts); - pNode->GetChildValue("PlayerRating", profile->m_fPlayerRating); + pNode->GetChildValue("DisplayName", loadingProfile->m_sDisplayName); + pNode->GetChildValue("CharacterID", loadingProfile->m_sCharacterID); + pNode->GetChildValue("LastUsedHighScoreName", loadingProfile->m_sLastUsedHighScoreName); + pNode->GetChildValue("Guid", (*loadingProfile->GetGuid())); + pNode->GetChildValue("SortOrder", s); loadingProfile->m_SortOrder = StringToSortOrder(s); + pNode->GetChildValue("LastDifficulty", s); loadingProfile->m_LastDifficulty = StringToDifficulty(s); + pNode->GetChildValue("LastStepsType", s); loadingProfile->m_LastStepsType = GAMEMAN->StringToStepsType(s); + pTemp = pNode->GetChild("Song"); if (pTemp) loadingProfile->m_lastSong.LoadFromNode(pTemp); + pNode->GetChildValue("CurrentCombo", loadingProfile->m_iCurrentCombo); + pNode->GetChildValue("TotalSessions", loadingProfile->m_iTotalSessions); + pNode->GetChildValue("TotalSessionSeconds", loadingProfile->m_iTotalSessionSeconds); + pNode->GetChildValue("TotalGameplaySeconds", loadingProfile->m_iTotalGameplaySeconds); + pNode->GetChildValue("LastPlayedDate", s); loadingProfile->m_LastPlayedDate.FromString(s); + pNode->GetChildValue("TotalDancePoints", loadingProfile->m_iTotalDancePoints); + pNode->GetChildValue("NumToasties", loadingProfile->m_iNumToasties); + pNode->GetChildValue("TotalTapsAndHolds", loadingProfile->m_iTotalTapsAndHolds); + pNode->GetChildValue("TotalJumps", loadingProfile->m_iTotalJumps); + pNode->GetChildValue("TotalHolds", loadingProfile->m_iTotalHolds); + pNode->GetChildValue("TotalRolls", loadingProfile->m_iTotalRolls); + pNode->GetChildValue("TotalMines", loadingProfile->m_iTotalMines); + pNode->GetChildValue("TotalHands", loadingProfile->m_iTotalHands); + pNode->GetChildValue("TotalLifts", loadingProfile->m_iTotalLifts); + pNode->GetChildValue("PlayerRating", loadingProfile->m_fPlayerRating); { const XNode* pDefaultModifiers = pNode->GetChild("DefaultModifiers"); @@ -816,7 +815,7 @@ void XMLProfile::LoadEttGeneralDataFromNode(const XNode* pNode) { { FOREACH_CONST_Child(pDefaultModifiers, game_type) { - game_type->GetTextValue(profile->m_sDefaultModifiers[game_type->GetName()]); + game_type->GetTextValue(loadingProfile->m_sDefaultModifiers[game_type->GetName()]); } } } @@ -825,7 +824,7 @@ void XMLProfile::LoadEttGeneralDataFromNode(const XNode* pNode) { const XNode* pPlayerSkillsets = pNode->GetChild("PlayerSkillsets"); if (pPlayerSkillsets) { FOREACH_ENUM(Skillset, ss) - pPlayerSkillsets->GetChildValue(SkillsetToString(ss), profile->m_fPlayerSkillsets[ss]); + pPlayerSkillsets->GetChildValue(SkillsetToString(ss), loadingProfile->m_fPlayerSkillsets[ss]); } } @@ -839,12 +838,12 @@ void XMLProfile::LoadEttGeneralDataFromNode(const XNode* pNode) { else lua_newtable(L); - profile->m_UserTable.SetFromStack(L); + loadingProfile->m_UserTable.SetFromStack(L); LUA->Release(L); } -XNode* XMLProfile::SaveSongScoresCreateNode() const +XNode* XMLProfile::SaveSongScoresCreateNode(const Profile* profile) const { CHECKPOINT_M("Getting the node to save song scores."); @@ -888,7 +887,7 @@ XNode* XMLProfile::SaveSongScoresCreateNode() const } -XNode* XMLProfile::SaveEttScoresCreateNode() const { +XNode* XMLProfile::SaveEttScoresCreateNode(const Profile* profile) const { CHECKPOINT_M("Saving the player scores node."); ASSERT(profile != NULL); @@ -915,11 +914,11 @@ void XMLProfile::LoadScreenshotDataFromNode(const XNode* pScreenshotData) Screenshot ss; ss.LoadFromNode(pScreenshot); - profile->m_vScreenshots.push_back(ss); + loadingProfile->m_vScreenshots.push_back(ss); } } -XNode* XMLProfile::SaveScreenshotDataCreateNode() const +XNode* XMLProfile::SaveScreenshotDataCreateNode(const Profile* profile) const { CHECKPOINT_M("Getting the node containing screenshot data."); @@ -968,14 +967,14 @@ void XMLProfile::LoadCategoryScoresFromNode(const XNode* pCategoryScores) if (pHighScoreListNode == NULL) WARN_AND_CONTINUE; - HighScoreList &hsl = profile->GetCategoryHighScoreList(st, rc); + HighScoreList &hsl = loadingProfile->GetCategoryHighScoreList(st, rc); hsl.LoadFromNode(pHighScoreListNode); } } } -XNode* XMLProfile::SaveCategoryScoresCreateNode() const +XNode* XMLProfile::SaveCategoryScoresCreateNode(const Profile* profile) const { CHECKPOINT_M("Getting the node that saves category scores."); @@ -1064,9 +1063,9 @@ ProfileLoadResult XMLProfile::LoadStatsXmlFromNode(const XNode *xml, bool bIgnor } // These are loaded from Editable, so we usually want to ignore them here. - RString sName = profile->m_sDisplayName; - RString sCharacterID = profile->m_sCharacterID; - RString sLastUsedHighScoreName = profile->m_sLastUsedHighScoreName; + RString sName = loadingProfile->m_sDisplayName; + RString sCharacterID = loadingProfile->m_sCharacterID; + RString sLastUsedHighScoreName = loadingProfile->m_sLastUsedHighScoreName; LOAD_NODE(GeneralData); LOAD_NODE(SongScores); @@ -1075,9 +1074,9 @@ ProfileLoadResult XMLProfile::LoadStatsXmlFromNode(const XNode *xml, bool bIgnor if (bIgnoreEditable) { - profile->m_sDisplayName = sName; - profile->m_sCharacterID = sCharacterID; - profile->m_sLastUsedHighScoreName = sLastUsedHighScoreName; + loadingProfile->m_sDisplayName = sName; + loadingProfile->m_sCharacterID = sCharacterID; + loadingProfile->m_sLastUsedHighScoreName = sLastUsedHighScoreName; } return ProfileLoadResult_Success; @@ -1116,42 +1115,42 @@ void XMLProfile::LoadSongScoresFromNode(const XNode* pSongScores) if (pHighScoreListNode == NULL) WARN_AND_CONTINUE; - HighScoreList &hsl = profile->m_SongHighScores[songID].m_StepsHighScores[stepsID].hsl; + HighScoreList &hsl = loadingProfile->m_SongHighScores[songID].m_StepsHighScores[stepsID].hsl; hsl.LoadFromNode(pHighScoreListNode); } } } -XNode *XMLProfile::SaveStatsXmlCreateNode() const +XNode *XMLProfile::SaveStatsXmlCreateNode(const Profile* profile) const { XNode *xml = new XNode("Stats"); - xml->AppendChild(SaveGeneralDataCreateNode()); - xml->AppendChild(SaveSongScoresCreateNode()); - xml->AppendChild(SaveCategoryScoresCreateNode()); - xml->AppendChild(SaveScreenshotDataCreateNode()); + xml->AppendChild(SaveGeneralDataCreateNode(profile)); + xml->AppendChild(SaveSongScoresCreateNode(profile)); + xml->AppendChild(SaveCategoryScoresCreateNode(profile)); + xml->AppendChild(SaveScreenshotDataCreateNode(profile)); return xml; } -XNode *XMLProfile::SaveEttXmlCreateNode() const +XNode *XMLProfile::SaveEttXmlCreateNode(const Profile* profile) const { XNode *xml = new XNode("Stats"); - xml->AppendChild(SaveEttGeneralDataCreateNode()); + xml->AppendChild(SaveEttGeneralDataCreateNode(profile)); if (!profile->FavoritedCharts.empty()) - xml->AppendChild(SaveFavoritesCreateNode()); + xml->AppendChild(SaveFavoritesCreateNode(profile)); if (!profile->PermaMirrorCharts.empty()) - xml->AppendChild(SavePermaMirrorCreateNode()); + xml->AppendChild(SavePermaMirrorCreateNode(profile)); if (!SONGMAN->allplaylists.empty()) - xml->AppendChild(SavePlaylistsCreateNode()); + xml->AppendChild(SavePlaylistsCreateNode(profile)); if (!profile->goalmap.empty()) - xml->AppendChild(SaveScoreGoalsCreateNode()); + xml->AppendChild(SaveScoreGoalsCreateNode(profile)); - xml->AppendChild(SaveEttScoresCreateNode()); + xml->AppendChild(SaveEttScoresCreateNode(profile)); return xml; } diff --git a/src/XMLProfile.h b/src/XMLProfile.h index d4ff4bba14..3784476f38 100644 --- a/src/XMLProfile.h +++ b/src/XMLProfile.h @@ -16,9 +16,18 @@ class XMLProfile { static void MoveBackupToDir(const RString &sFromDir, const RString &sToDir); // For converting to etterna from stats.xml void LoadStatsXmlForConversion(); - ProfileLoadResult LoadStatsXmlFromNode(const XNode* pNode, bool bIgnoreEditable = true); + // Etterna profile ProfileLoadResult LoadEttFromDir(RString dir); + ProfileLoadResult LoadStatsFromDir(RString dir, bool require_signature); + + bool SaveStatsXmlToDir(RString sDir, bool bSignData, const Profile* profile); + bool SaveEttXmlToDir(RString sDir, const Profile* profile) const; + void SetLoadingProfile(Profile* p) { loadingProfile = p; } +private: + Profile* loadingProfile; + ProfileLoadResult LoadStatsXmlFromNode(const XNode* pNode, bool bIgnoreEditable = true); + ProfileLoadResult LoadEttXmlFromNode(const XNode* pNode); void LoadEttGeneralDataFromNode(const XNode* pNode); @@ -31,32 +40,27 @@ class XMLProfile { void LoadSongScoresFromNode(const XNode* pNode); void LoadCategoryScoresFromNode(const XNode* pNode); void LoadScreenshotDataFromNode(const XNode* pNode); - ProfileLoadResult LoadStatsFromDir(RString dir, bool require_signature); - bool SaveStatsXmlToDir(RString sDir, bool bSignData); - bool SaveEttXmlToDir(RString sDir) const; - bool SaveStatsXmlToDir(RString sDir, bool bSignData) const; - XNode* SaveEttGeneralDataCreateNode() const; - XNode* SaveEttScoresCreateNode() const; - XNode* SaveEttXmlCreateNode() const; + XNode* SaveEttGeneralDataCreateNode(const Profile* profile) const; + XNode* SaveEttScoresCreateNode(const Profile* profile) const; + XNode* SaveEttXmlCreateNode(const Profile* profile) const; - Profile* profile; - XNode* SaveFavoritesCreateNode() const; - XNode* SavePermaMirrorCreateNode() const; - XNode* SaveScoreGoalsCreateNode() const; - XNode* SavePlaylistsCreateNode() const; + XNode* SaveFavoritesCreateNode(const Profile* profile) const; + XNode* SavePermaMirrorCreateNode(const Profile* profile) const; + XNode* SaveScoreGoalsCreateNode(const Profile* profile) const; + XNode* SavePlaylistsCreateNode(const Profile* profile) const; - XNode* SaveStatsXmlCreateNode() const; + XNode* SaveStatsXmlCreateNode(const Profile* profile) const; - XNode* SaveGeneralDataCreateNode() const; - XNode* SaveSongScoresCreateNode() const; + XNode* SaveGeneralDataCreateNode(const Profile* profile) const; + XNode* SaveSongScoresCreateNode(const Profile* profile) const; - XNode* SaveCategoryScoresCreateNode() const; - XNode* SaveScreenshotDataCreateNode() const; + XNode* SaveCategoryScoresCreateNode(const Profile* profile) const; + XNode* SaveScreenshotDataCreateNode(const Profile* profile) const; - XNode* SaveCoinDataCreateNode() const; + XNode* SaveCoinDataCreateNode(const Profile* profile) const; RString profiledir; };