diff --git a/src/common/HighScoresManager.cxx b/src/common/HighScoresManager.cxx index 1d5ee6005..2614f0190 100644 --- a/src/common/HighScoresManager.cxx +++ b/src/common/HighScoresManager.cxx @@ -564,6 +564,24 @@ Int32 HighScoresManager::fromBCD(uInt8 bcd) const return (bcd >> 4) * 10 + bcd % 16; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string HighScoresManager::hash(ScoresData& data) const +{ + ostringstream buf; + + buf << HIGHSCORE_HEADER << data.md5 << md5Props() << data.variation; + + for(uInt32 r = 0; r < NUM_RANKS && data.scores[r].score; ++r) + { + buf << data.scores[r].score + << data.scores[r].special + << data.scores[r].name + << data.scores[r].date; + } + + return MD5::hash(buf.str()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void HighScoresManager::saveHighScores(ScoresData& data) const { @@ -571,14 +589,13 @@ void HighScoresManager::saveHighScores(ScoresData& data) const { json hsObject = json::object(); json hsData = json::object(); + json jScores = json::array(); // Add header so that if the high score format changes in the future, // we'll know right away, without having to parse the rest of the data - hsData[VERSION] = HIGHSCORE_HEADER; - hsData[MD5] = data.md5; - hsData[VARIATION] = data.variation; - - json jScores = json::array(); + hsObject[VERSION] = HIGHSCORE_HEADER; + hsObject[MD5] = data.md5; + hsObject[PROPCHECK] = md5Props(); for(uInt32 r = 0; r < NUM_RANKS && data.scores[r].score; ++r) { @@ -592,10 +609,10 @@ void HighScoresManager::saveHighScores(ScoresData& data) const jScores.push_back(jScore); } hsData[SCORES] = jScores; - hsData[PROPCHECK] = md5Props(); + hsData[VARIATION] = data.variation; hsObject[DATA] = hsData; - hsObject[CHECKSUM] = MD5::hash(hsData.dump()); + hsObject[CHECKSUM] = hash(data); myHighscoreRepository->save(data.md5, to_string(data.variation), hsObject.dump(2)); } @@ -608,11 +625,11 @@ void HighScoresManager::saveHighScores(ScoresData& data) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void HighScoresManager::loadHighScores(ScoresData& data) { + bool invalid = false; ostringstream buf; clearHighScores(data); - bool invalid = false; try { Variant serializedHighscore; @@ -620,27 +637,27 @@ void HighScoresManager::loadHighScores(ScoresData& data) { const json hsObject = json::parse(serializedHighscore.toString()); - if(hsObject.contains(DATA)) + // First test if we have a valid header + // If so, do a complete high score data load + if(!hsObject.contains(VERSION) || hsObject.at(VERSION) != HIGHSCORE_HEADER) + buf << "Error: Incompatible high scores data for variation " << data.variation << "."; + else if(hsObject.contains(DATA)) { const json hsData = hsObject.at(DATA); - // First test if we have a valid header - // If so, do a complete high score data load - if(!hsData.contains(VERSION) || hsData.at(VERSION) != HIGHSCORE_HEADER) - buf << "Error: Incompatible high scores data for variation " << data.variation << "."; + if(!load(hsData, data) + || !hsObject.contains(MD5) || hsObject.at(MD5) != data.md5 + || !hsObject.contains(PROPCHECK) || hsObject.at(PROPCHECK) != md5Props() + || !hsObject.contains(CHECKSUM) || hsObject.at(CHECKSUM) != hash(data)) + invalid = true; else - { - if(!load(hsData, data) - || !hsData.contains(PROPCHECK) || hsData.at(PROPCHECK) != md5Props() - || !hsObject.contains(CHECKSUM) || hsObject.at(CHECKSUM) != MD5::hash(hsData.dump())) - invalid = true; - else - return; - } + return; // scores loaded OK } else invalid = true; } + else + return; // no scores for variation found } catch(...) { invalid = true; } @@ -655,8 +672,7 @@ void HighScoresManager::loadHighScores(ScoresData& data) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool HighScoresManager::load(const json& hsData, ScoresData& data) { - if(!hsData.contains(MD5) || hsData.at(MD5) != data.md5 - || !hsData.contains(VARIATION) || hsData.at(VARIATION) != data.variation + if(!hsData.contains(VARIATION) || hsData.at(VARIATION) != data.variation || !hsData.contains(SCORES)) return false; diff --git a/src/common/HighScoresManager.hxx b/src/common/HighScoresManager.hxx index 6d83feb35..39b7b0409 100644 --- a/src/common/HighScoresManager.hxx +++ b/src/common/HighScoresManager.hxx @@ -18,7 +18,7 @@ #ifndef HIGHSCORES_MANAGER_HXX #define HIGHSCORES_MANAGER_HXX -#define HIGHSCORE_HEADER "06010000highscores" +#define HIGHSCORE_HEADER "06050000highscores" class OSystem; @@ -232,6 +232,7 @@ class HighScoresManager uInt16 fromHexStr(const string& addr) const; Int32 fromBCD(uInt8 bcd) const; + string hash(HSM::ScoresData& data) const; /** Loads the current high scores for this game and variation from the given JSON object. diff --git a/src/gui/HighScoresDialog.cxx b/src/gui/HighScoresDialog.cxx index 21ad3c0fe..a55dcc922 100644 --- a/src/gui/HighScoresDialog.cxx +++ b/src/gui/HighScoresDialog.cxx @@ -204,6 +204,8 @@ HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent, addDefaultsOKCancelBGroup(wid, _font, "Save", "Cancel", " Reset "); _defaultWidget->setToolTip("Click to reset all high scores of this variation."); addToFocusList(wid); + + _focusedWidget = _okWidget; // start with focus on 'Save' button } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -