changed high score (file) format to JSON

This commit is contained in:
thrust26 2020-12-01 16:20:09 +01:00
parent 2e0c0549e7
commit ebbec177f6
3 changed files with 126 additions and 59 deletions

View File

@ -347,7 +347,6 @@ void ContextMenu::drawCurrentSelection(int item)
if(_selectedOffset != item) if(_selectedOffset != item)
{ {
_selectedOffset = item; _selectedOffset = item;
cerr << "ContextMenu" << endl;
setDirty(); setDirty();
} }
} }

View File

@ -534,10 +534,28 @@ void HighScoresDialog::saveHighScores(Int32 variation) const
buf << instance().stateDir() << cartName() << ".hs" << variation; buf << instance().stateDir() << cartName() << ".hs" << variation;
// Make sure the file can be opened for writing //// Make sure the file can be opened for writing
Serializer out(buf.str()); //Serializer out(buf.str());
if(!out) //if(!out)
//{
// buf.str("");
// buf << "Can't open/save to high scores file for variation " << variation;
// instance().frameBuffer().showTextMessage(buf.str());
//}
//// Do a complete high scores save
//if(!save(out, variation))
//{
// buf.str("");
// buf << "Error saving high scores for variation" << variation;
// instance().frameBuffer().showTextMessage(buf.str());
//}
// Make sure the file can be opened for writing
FilesystemNode node(buf.str());
if(!node.isWritable())
{ {
buf.str(""); buf.str("");
buf << "Can't open/save to high scores file for variation " << variation; buf << "Can't open/save to high scores file for variation " << variation;
@ -545,13 +563,12 @@ void HighScoresDialog::saveHighScores(Int32 variation) const
} }
// Do a complete high scores save // Do a complete high scores save
if(!save(out, variation)) if(!save(node, variation))
{ {
buf.str(""); buf.str("");
buf << "Error saving high scores for variation" << variation; buf << "Error saving high scores for variation" << variation;
instance().frameBuffer().showTextMessage(buf.str()); instance().frameBuffer().showTextMessage(buf.str());
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -569,53 +586,78 @@ void HighScoresDialog::loadHighScores(Int32 variation)
buf << instance().stateDir() << cartName() << ".hs" << variation; buf << instance().stateDir() << cartName() << ".hs" << variation;
// Make sure the file can be opened in read-only mode FilesystemNode node(buf.str());
Serializer in(buf.str(), Serializer::Mode::ReadOnly); stringstream in;
if(!in)
return;
// First test if we have a valid header
// If so, do a complete high scores load
buf.str(""); buf.str("");
try // Make sure the file can be opened
{ try {
if (in.getString() != HIGHSCORE_HEADER) node.read(in);
buf << "Incompatible high scores for variation " << variation << " file"; }
else catch(...) { return; }
try {
string highscores;
buf.str("");
// Make sure the file can be opened
node.read(in);
if(getline(in, highscores) && highscores.length() != 0)
{ {
if (load(in, variation)) json hsData = json::parse(highscores);
return;
// First test if we have a valid header
// If so, do a complete high scores load
if(!hsData.contains(VERSION) || hsData.at(VERSION) != HIGHSCORE_HEADER)
buf << "Incompatible high scores for variation " << variation << " file";
else else
buf << "Invalid data in high scores for variation " << variation << " file"; {
if(load(hsData, variation))
return;
else
buf << "Invalid data in high scores for variation " << variation << " file";
}
} }
} }
catch(...) catch(...) {
{
buf << "Invalid data in high scores for variation " << variation << " file"; buf << "Invalid data in high scores for variation " << variation << " file";
} }
instance().frameBuffer().showTextMessage(buf.str()); instance().frameBuffer().showTextMessage(buf.str());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresDialog::save(Serializer& out, Int32 variation) const bool HighScoresDialog::save(FilesystemNode& node, Int32 variation) const
{ {
try try
{ {
json jData = json::object();
// Add header so that if the high score format changes in the future, // 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 file // we'll know right away, without having to parse the rest of the file
out.putString(HIGHSCORE_HEADER); jData[VERSION] = HIGHSCORE_HEADER;
jData[MD5] = myMD5;
jData[VARIATION] = variation;
out.putString(myMD5); json jScores = json::array();
out.putInt(variation);
for (uInt32 r = 0; r < NUM_RANKS; ++r) for(uInt32 r = 0; r < NUM_RANKS; ++r)
{ {
out.putInt(myHighScores[r]); json jScore = json::object();
out.putInt(mySpecials[r]);
out.putString(myNames[r]); jScore[SCORE] = myHighScores[r];
out.putString(myDates[r]); jScore[SPECIAL] = mySpecials[r];
jScore[NAME] = myNames[r];
jScore[DATE] = myDates[r];
jScores.push_back(jScore);
} }
jData[SCORES] = jScores;
//stringstream ss(jData.dump());
//node.write(ss);
node.write(stringstream(jData.dump()));
} }
catch(...) catch(...)
{ {
@ -626,27 +668,28 @@ bool HighScoresDialog::save(Serializer& out, Int32 variation) const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresDialog::load(Serializer& in, Int32 variation) bool HighScoresDialog::load(const json& hsData, Int32 variation)
{ {
try if(!hsData.contains(MD5) || hsData.at(MD5) != myMD5
{ || !hsData.contains(VARIATION) || hsData.at(VARIATION) != variation
if (in.getString() != myMD5) || !hsData.contains(SCORES))
return false;
if (Int32(in.getInt()) != variation)
return false;
for (uInt32 r = 0; r < NUM_RANKS; ++r)
{
myHighScores[r] = in.getInt();
mySpecials[r] = in.getInt();
myNames[r] = in.getString();
myDates[r] = in.getString();
}
}
catch(...)
{
cerr << "ERROR: HighScoresDialog::load() exception\n";
return false; return false;
const json& jScores = hsData.at(SCORES);
if(!jScores.empty() && jScores.is_array())
{
uInt32 r = 0;
for(const json& jScore : jScores)
{
if(jScore.contains(SCORE)) myHighScores[r] = jScore.at(SCORE).get<Int32>();
if(jScore.contains(SPECIAL)) mySpecials[r] = jScore.at(SPECIAL).get<Int32>();
if(jScore.contains(NAME)) myNames[r] = jScore.at(NAME).get<string>();
if(jScore.contains(DATE)) myDates[r] = jScore.at(DATE).get<string>();
if(++r == NUM_RANKS)
break;
}
} }
return true; return true;
} }
@ -666,3 +709,13 @@ string HighScoresDialog::now() const
return ss.str(); return ss.str();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresDialog::VERSION = "version";
const string HighScoresDialog::MD5 = "md5";
const string HighScoresDialog::VARIATION = "variation";
const string HighScoresDialog::SCORES = "scores";
const string HighScoresDialog::SCORE = "score";
const string HighScoresDialog::SPECIAL = "special";
const string HighScoresDialog::NAME = "name";
const string HighScoresDialog::DATE = "date";

View File

@ -33,6 +33,11 @@ class Serializer;
#include "Menu.hxx" #include "Menu.hxx"
#include "Dialog.hxx" #include "Dialog.hxx"
#include "FSNode.hxx"
#include "json_lib.hxx"
using json = nlohmann::json;
/** /**
The dialog for displaying high scores in Stella. The dialog for displaying high scores in Stella.
@ -66,20 +71,20 @@ class HighScoresDialog : public Dialog
void loadHighScores(Int32 variation); void loadHighScores(Int32 variation);
/** /**
Saves the current high scores for this game and variation to the given Serializer. Saves the current high scores for this game and variation to the given file system node.
@param out The serializer device to save to. @param node The file system node to save to.
@return The result of the save. True on success, false on failure. @return The result of the save. True on success, false on failure.
*/ */
bool save(Serializer& out, Int32 variation) const; bool save(FilesystemNode& node, Int32 variation) const;
/** /**
Loads the current high scores for this game and variation from the given Serializer. Loads the current high scores for this game and variation from the given JSON object.
@param in The Serializer device to load from. @param hsData The JSON to parse
@return The result of the load. True on success, false on failure. @return The result of the load. True on success, false on failure.
*/ */
bool load(Serializer& in, Int32 variation); bool load(const json& hsData, Int32 variation);
string now() const; string now() const;
@ -92,6 +97,16 @@ class HighScoresDialog : public Dialog
kCancelSave = 'CcSv' kCancelSave = 'CcSv'
}; };
private:
static const string VERSION;
static const string MD5;
static const string VARIATION;
static const string SCORES;
static const string SCORE;
static const string SPECIAL;
static const string NAME;
static const string DATE;
private: private:
bool myUserDefVar; // allow the user to define the variation bool myUserDefVar; // allow the user to define the variation
bool myDirty; bool myDirty;