diff --git a/src/common/HighScoresManager.cxx b/src/common/HighScoresManager.cxx index c07bebca5..2197ba25e 100644 --- a/src/common/HighScoresManager.cxx +++ b/src/common/HighScoresManager.cxx @@ -102,6 +102,14 @@ string HighScoresManager::getPropIdx(const Properties& props, PropType type, uIn return result; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool HighScoresManager::enabled() const +{ + Properties props; + + return (!getPropIdx(properties(props), PropType::Cart_Addresses, 0).empty()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 HighScoresManager::numPlayers(const Properties& props) const { @@ -490,7 +498,7 @@ Int32 HighScoresManager::special(uInt16 addr, bool varBCD, bool zeroBased) const Int32 HighScoresManager::fromBCD(uInt8 bcd) const { // verify if score is legit - if (bcd >= 160) + if ((bcd & 0xF0) >= 0xA0 || (bcd & 0xF) >= 0xA) return -1; return (bcd >> 4) * 10 + bcd % 16; diff --git a/src/common/HighScoresManager.hxx b/src/common/HighScoresManager.hxx index 939ef3d94..787afcb10 100644 --- a/src/common/HighScoresManager.hxx +++ b/src/common/HighScoresManager.hxx @@ -72,6 +72,10 @@ class HighScoresManager HighScoresManager(OSystem& osystem); virtual ~HighScoresManager() = default; + + // check if high score data has been defined + bool enabled() const; + /** Get the highscore data of game's properties diff --git a/src/common/PKeyboardHandler.cxx b/src/common/PKeyboardHandler.cxx index 4bbd24ff4..71dadc1d1 100644 --- a/src/common/PKeyboardHandler.cxx +++ b/src/common/PKeyboardHandler.cxx @@ -507,7 +507,7 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo {Event::UnwindAllMenu, KBDK_UP, MOD3}, {Event::ShowScore, KBDK_S, KBDM_SHIFT | KBDM_CTRL}, {Event::ShowVariation, KBDK_V, KBDM_SHIFT | KBDM_CTRL}, - {Event::HighScoresMenuMode, KBDK_HOME}, + {Event::HighScoresMenuMode, KBDK_INSERT}, #if defined(RETRON77) {Event::ConsoleColorToggle, KBDK_F4}, // back ("COLOR","B/W") diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 3c71cc9ef..e8a17699f 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -1041,14 +1041,16 @@ bool EventHandler::changeStateByEvent(Event::Type type) break; case Event::OptionsMenuMode: - if (myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) + if (myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE + || myState == EventHandlerState::TIMEMACHINE) enterMenuMode(EventHandlerState::OPTIONSMENU); else handled = false; break; case Event::CmdMenuMode: - if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) + if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE + || myState == EventHandlerState::TIMEMACHINE) enterMenuMode(EventHandlerState::CMDMENU); else if(myState == EventHandlerState::CMDMENU && !myOSystem.settings().getBool("minimal_ui")) // The extra check for "minimal_ui" allows mapping e.g. right joystick fire @@ -1059,8 +1061,14 @@ bool EventHandler::changeStateByEvent(Event::Type type) break; case Event::HighScoresMenuMode: - if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) - enterMenuMode(EventHandlerState::HIGHSCORESMENU); + if (myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE + || myState == EventHandlerState::TIMEMACHINE) + { + if (myOSystem.highScores().enabled()) + enterMenuMode(EventHandlerState::HIGHSCORESMENU); + else + myOSystem.frameBuffer().showMessage("No high scores data defined"); + } else if(myState == EventHandlerState::HIGHSCORESMENU) leaveMenuMode(); else @@ -2029,6 +2037,7 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { { { Event::Combo16, "Combo 16", "" }, { Event::ShowScore, "Display current score", "" }, { Event::ShowVariation, "Display current variation", "" }, + { Event::HighScoresMenuMode, "Toggle High Scores UI", "" }, } }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index d27b37d80..929e99f8c 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -468,7 +468,7 @@ class EventHandler #else PNG_SIZE = 0, #endif - EMUL_ACTIONLIST_SIZE = 144 + 2 + PNG_SIZE + COMBO_SIZE, + EMUL_ACTIONLIST_SIZE = 144 + 3 + PNG_SIZE + COMBO_SIZE, MENU_ACTIONLIST_SIZE = 18 ; diff --git a/src/gui/HighScoresDialog.cxx b/src/gui/HighScoresDialog.cxx index 20014d8b2..daa8f0555 100644 --- a/src/gui/HighScoresDialog.cxx +++ b/src/gui/HighScoresDialog.cxx @@ -15,6 +15,7 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ +#include "OSystem.hxx" #include "Console.hxx" #include "EventHandler.hxx" #include "Font.hxx" @@ -29,8 +30,9 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent, - const GUI::Font& font, int max_w, int max_h) - : Dialog(osystem, parent, font, "High Scores") + const GUI::Font& font, int max_w, int max_h) + : Dialog(osystem, parent, font, "High Scores"), + myInitials("") { const GUI::Font& ifont = instance().frameBuffer().infoFont(); const int lineHeight = font.getLineHeight(), @@ -56,7 +58,9 @@ HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent, StaticTextWidget* s = new StaticTextWidget(this, font, xpos, ypos + 1, "Variation "); myVariationWidget = new PopUpWidget(this, font, s->getRight(), ypos, - font.getStringWidth("256") - 4, lineHeight, items, "", 0, 0); + font.getStringWidth("256") - 4, lineHeight, items, "", 0, + kVariationChanged); + wid.push_back(myVariationWidget); ypos += lineHeight + VGAP * 4; @@ -68,7 +72,7 @@ HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent, int nWidth = font.getStringWidth("ABC") + 4; new StaticTextWidget(this, font, xposRank, ypos + 1, "Rank"); - new StaticTextWidget(this, font, xposScore, ypos + 1, "Score"); + new StaticTextWidget(this, font, xposScore, ypos + 1, " Score"); mySpecialLabelWidget = new StaticTextWidget(this, font, xposSpecial, ypos + 1, "Round"); new StaticTextWidget(this, font, xposName - 2, ypos + 1, "Name"); new StaticTextWidget(this, font, xposDate+16, ypos + 1, "Date Time"); @@ -81,13 +85,11 @@ HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent, (p < 9 ? " " : "") + std::to_string(p + 1)); myScoresWidget[p] = new StaticTextWidget(this, font, xposScore, ypos + 1, "123456"); mySpecialsWidget[p] = new StaticTextWidget(this, font, xposSpecial + 8, ypos + 1, "123"); - myEditNamesWidget[p] = new EditTextWidget(this, font, xposName, ypos - 1, nWidth, lineHeight, "JTZ"); + myNamesWidget[p] = new StaticTextWidget(this, font, xposName + 2, ypos + 1, " "); + myEditNamesWidget[p] = new EditTextWidget(this, font, xposName, ypos - 1, nWidth, lineHeight); myEditNamesWidget[p]->setFlags(EditTextWidget::FLAG_INVISIBLE); + myEditNamesWidget[p]->setEnabled(false); wid.push_back(myEditNamesWidget[p]); - myNamesWidget[p] = new StaticTextWidget(this, font, xposName + 2, ypos + 1, "JTZ"); - - //new StaticTextWidget(this, font, xposDate, ypos + 1, "12.02.20 17:15"); - //new StaticTextWidget(this, font, xposDate, ypos + 1, "02/12/20 12:30am"); myDatesWidget[p] = new StaticTextWidget(this, font, xposDate, ypos + 1, "12-02-20 17:15"); ypos += lineHeight + VGAP; @@ -96,9 +98,8 @@ HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent, myMD5Widget = new StaticTextWidget(this, ifont, xpos, ypos + 1, "MD5: 12345678901234567890123456789012"); - wid.clear(); addOKCancelBGroup(wid, font); - addBGroupToFocusList(wid); + addToFocusList(wid); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -110,7 +111,7 @@ HighScoresDialog::~HighScoresDialog() void HighScoresDialog::loadConfig() { // Enable blending (only once is necessary) - if(!surface().attributes().blending) + if (!surface().attributes().blending) { surface().attributes().blending = true; surface().attributes().blendalpha = 90; @@ -120,9 +121,6 @@ void HighScoresDialog::loadConfig() VariantList items; items.clear(); - - myVariation = instance().highScores().variation(); - for (Int32 i = 1; i <= instance().highScores().numVariations(); ++i) { ostringstream buf; @@ -130,87 +128,44 @@ void HighScoresDialog::loadConfig() VarList::push_back(items, buf.str(), i); } myVariationWidget->addItems(items); - - myScore = instance().highScores().score(); - mySpecial = instance().highScores().special(); - myVariationWidget->setSelected(myVariation); - - - ///////////////////////////////////////////////////////////////////////////////////////////////// - // mock data - const Int32 SCORES[NUM_POSITIONS] = {999999, 250000, 100000, 50000, 20000, - 5000, 2000, 200, 20, 0}; - const Int32 SPECIALS[NUM_POSITIONS] = {200, 150, 90, 70, 45, - 30, 25, 10, 7, 0}; - const string NAMES[NUM_POSITIONS] = {"RAM", "CDW", "AD ", "JTZ", "DOA", - "ROM", "VCS", "N.S", "JWC", " -"}; - const string DATES[NUM_POSITIONS] = {"19-12-24 21:00", "19-07-18 00:00", "20-01-01 12:00", - "20-02-12 21:50", "20-02-11 14:16", "20-02-11 13:11", - "20-02-10 19:45", "10-02-10 20:04", "05-02-09 22:32", - " - -"}; - - for (Int32 p = 0; p < NUM_POSITIONS; ++p) - { - myScores[p] = SCORES[p]; - mySpecials[p] = SPECIALS[p]; - myNames[p] = NAMES[p]; - myDates[p] = DATES[p]; - } - ///////////////////////////////////////////////////////////////////////////////////////////////// - - Int32 pos; + myDisplayedVariation = instance().highScores().variation(); + myVariationWidget->setSelected(myDisplayedVariation); mySpecialLabelWidget->setLabel(instance().highScores().specialLabel()); + // TDOO: required when leaving with hot key for (Int32 p = 0; p < NUM_POSITIONS; ++p) + { + myNamesWidget[p]->clearFlags(EditTextWidget::FLAG_INVISIBLE); myEditNamesWidget[p]->setFlags(EditTextWidget::FLAG_INVISIBLE); - - if (myScore > 0) - { - for (pos = 0; pos < NUM_POSITIONS; ++pos) - { - if (myScore > myScores[pos] || - (myScore == myScores[pos] && mySpecial > mySpecials[pos])) - break; - } - - if (pos < NUM_POSITIONS) - { - for (Int32 p = NUM_POSITIONS - 1; p > pos; --p) - { - myScores[p] = myScores[p - 1]; - mySpecials[p] = mySpecials[p - 1]; - myNames[p] = myNames[p - 1]; - myDates[p] = myDates[p - 1]; - } - myScores[pos] = myScore; - myNames[pos] = ""; - mySpecials[pos] = mySpecial; - myDates[pos] = now(); - - myEditNamesWidget[pos]->clearFlags(EditTextWidget::FLAG_INVISIBLE); - } + myEditNamesWidget[p]->setEnabled(false); } - for (Int32 p = 0; p < NUM_POSITIONS; ++p) - { - ostringstream buf; + myMD5 = instance().console().properties().get(PropType::Cart_MD5); + myMD5Widget->setLabel("MD5: " + myMD5); - buf << std::setw(HSM::MAX_SCORE_DIGITS) << std::setfill(' ') << myScores[p]; - myScoresWidget[p]->setLabel(buf.str()); - buf.str(""); - buf << std::setw(HSM::MAX_SPECIAL_DIGITS) << std::setfill(' ') << mySpecials[p]; - mySpecialsWidget[p]->setLabel(buf.str()); - myNamesWidget[p]->setLabel(myNames[p]); - myEditNamesWidget[p]->setText(myNames[p]); - myDatesWidget[p]->setLabel(myDates[p]); - } + + myPlayedVariation = instance().highScores().variation(); + + myEditPos = myHighScorePos = -1; + myNow = now(); + handleVariation(true); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void HighScoresDialog::saveConfig() { - + if (myHighScorePos != -1) + { + if (myDisplayedVariation != myPlayedVariation) + { + loadHighScores(myPlayedVariation); + handlePlayedVariation(); + } + myInitials = myEditNamesWidget[myHighScorePos]->getText(); + myNames[myHighScorePos] = myInitials; + saveHighScores(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -220,12 +175,14 @@ void HighScoresDialog::handleCommand(CommandSender* sender, int cmd, int data, i { case kOKCmd: saveConfig(); + // falls through... case kCloseCmd: + resetVisibility(); instance().eventHandler().leaveMenuMode(); -// instance().eventHandler().handleEvent(Event::ExitMode); + break; -// case GuiObject::kOKCmd: -// close(); + case kVariationChanged: + handleVariation(); break; default: @@ -248,3 +205,258 @@ string HighScoresDialog::now() const return ss.str(); } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void HighScoresDialog::resetVisibility() +{ + if (myEditPos != -1) + { + // hide again + myEditNamesWidget[myEditPos]->setFlags(EditTextWidget::FLAG_INVISIBLE); + myEditNamesWidget[myEditPos]->setEnabled(false); + myNamesWidget[myEditPos]->clearFlags(EditTextWidget::FLAG_INVISIBLE); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void HighScoresDialog::handlePlayedVariation() +{ + Int32 newScore = instance().highScores().score(); + + if (newScore > 0) + { + Int32 newSpecial = instance().highScores().special(); + for (myHighScorePos = 0; myHighScorePos < NUM_POSITIONS; ++myHighScorePos) + { + if (newScore > myHighScores[myHighScorePos] || + (newScore == myHighScores[myHighScorePos] && newSpecial > mySpecials[myHighScorePos])) + break; + } + + if (myHighScorePos < NUM_POSITIONS) + { + myEditPos = myHighScorePos; + for (Int32 p = NUM_POSITIONS - 1; p > myHighScorePos; --p) + { + myHighScores[p] = myHighScores[p - 1]; + mySpecials[p] = mySpecials[p - 1]; + myNames[p] = myNames[p - 1]; + myDates[p] = myDates[p - 1]; + } + myHighScores[myHighScorePos] = newScore; + //myNames[myHighScorePos] = ""; + mySpecials[myHighScorePos] = newSpecial; + myDates[myHighScorePos] = myNow; + } + else + myHighScorePos = -1; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void HighScoresDialog::handleVariation(bool init) +{ + resetVisibility(); + + myDisplayedVariation = myVariationWidget->getSelectedTag().toInt(); + + loadHighScores(myDisplayedVariation); + + myEditPos = -1; + + if (myDisplayedVariation == myPlayedVariation) + { + handlePlayedVariation(); + + if (myHighScorePos != -1) + { + myNamesWidget[myHighScorePos]->setFlags(EditTextWidget::FLAG_INVISIBLE); + myEditNamesWidget[myHighScorePos]->clearFlags(EditTextWidget::FLAG_INVISIBLE); + myEditNamesWidget[myHighScorePos]->setEnabled(true); + myEditNamesWidget[myHighScorePos]->setEditable(true); + if (init) + myEditNamesWidget[myHighScorePos]->setText(myInitials); + } + } + + for (Int32 p = 0; p < NUM_POSITIONS; ++p) + { + ostringstream buf; + + if (myHighScores[p] > 0) + buf << std::setw(HSM::MAX_SCORE_DIGITS) << std::setfill(' ') << myHighScores[p]; + myScoresWidget[p]->setLabel(buf.str()); + + buf.str(""); + if (mySpecials[p] > 0) + buf << std::setw(HSM::MAX_SPECIAL_DIGITS) << std::setfill(' ') << mySpecials[p]; + mySpecialsWidget[p]->setLabel(buf.str()); + + myNamesWidget[p]->setLabel(myNames[p]); + myDatesWidget[p]->setLabel(myDates[p]); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void HighScoresDialog::saveHighScores() const +{ + if(instance().hasConsole()) + { + ostringstream buf; + buf << instance().stateDir() + << instance().console().properties().get(PropType::Cart_Name) + << ".hs" << myPlayedVariation; + + // Make sure the file can be opened for writing + Serializer out(buf.str()); + + if(!out) + { + buf.str(""); + buf << "Can't open/save to high scores file for variation " << myPlayedVariation; + instance().frameBuffer().showMessage(buf.str()); + return; + } + + // Do a complete high scores save + if (!save(out)) + { + buf.str(""); + buf << "Error saving high scores for variation" << myPlayedVariation; + instance().frameBuffer().showMessage(buf.str()); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void HighScoresDialog::loadHighScores(Int32 variation) +{ + for (Int32 p = 0; p < NUM_POSITIONS; ++p) + { + myHighScores[p] = 0; + mySpecials[p] = 0; + myNames[p] = ""; + myDates[p] = ""; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + /* + // mock data + const Int32 SCORES[NUM_POSITIONS] = {999999, 250000, 100000, 50000, 20000, + 5000, 2000, 200, 20, 0}; + const Int32 SPECIALS[NUM_POSITIONS] = {200, 150, 90, 70, 45, + 30, 25, 10, 7, 0}; + const string NAMES[NUM_POSITIONS] = {"RAM", "CDW", "AD ", "JTZ", "DOA", + "ROM", "VCS", "N.S", "JWC", " -"}; + const string DATES[NUM_POSITIONS] = {"19-12-24 21:00", "19-07-18 00:00", "20-01-01 12:00", + "20-02-12 21:50", "20-02-11 14:16", "20-02-11 13:11", + "20-02-10 19:45", "10-02-10 20:04", "05-02-09 22:32", + " - -"}; + + for (Int32 p = 0; p < NUM_POSITIONS; ++p) + { + myHighScores[p] = SCORES[p]; + mySpecials[p] = SPECIALS[p]; + myNames[p] = NAMES[p]; + myDates[p] = DATES[p]; + } + */ + ///////////////////////////////////////////////////////////////////////////////////////////////// + + if(instance().hasConsole()) + { + ostringstream buf; + buf << instance().stateDir() + << instance().console().properties().get(PropType::Cart_Name) + << ".hs" << variation; + + // Make sure the file can be opened in read-only mode + Serializer in(buf.str(), Serializer::Mode::ReadOnly); + if(!in) + { + //buf.str(""); + //buf << "No high scores file for variation " << variation << " found."; + //instance().frameBuffer().showMessage(buf.str()); + return; + } + + // First test if we have a valid header + // If so, do a complete high scores load + buf.str(""); + try + { + if (in.getString() != HIGHSCORE_HEADER) + buf << "Incompatible high scores for variation " << variation << " file"; + else + { + if (load(in, variation)) + //buf << "High scores for variation " << variation << " loaded"; + return; + else + buf << "Invalid data in high scores for variation " << variation << " file"; + } + } + catch(...) + { + buf << "Invalid data in high scores for variation " << variation << " file"; + } + + instance().frameBuffer().showMessage(buf.str()); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool HighScoresDialog::save(Serializer& out) const +{ + try + { + // 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 + out.putString(HIGHSCORE_HEADER); + + out.putString(myMD5); + out.putInt(myPlayedVariation); + for (Int32 p = 0; p < NUM_POSITIONS; ++p) + { + out.putInt(myHighScores[p]); + out.putInt(mySpecials[p]); + out.putString(myNames[p]); + out.putString(myDates[p]); + } + } + catch(...) + { + cerr << "ERROR: HighScoresDialog::save() exception\n"; + return false; + } + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool HighScoresDialog::load(Serializer& in, Int32 variation) +{ + try + { + if (in.getString() != myMD5) + { + return false; + } + if (Int32(in.getInt()) != variation) + { + return false; + } + for (Int32 p = 0; p < NUM_POSITIONS; ++p) + { + myHighScores[p] = in.getInt(); + mySpecials[p] = in.getInt(); + myNames[p] = in.getString(); + myDates[p] = in.getString(); + } + } + catch(...) + { + cerr << "ERROR: HighScoresDialog::load() exception\n"; + return false; + } + return true; +} diff --git a/src/gui/HighScoresDialog.hxx b/src/gui/HighScoresDialog.hxx index 4bb89bf5b..7f11bff98 100644 --- a/src/gui/HighScoresDialog.hxx +++ b/src/gui/HighScoresDialog.hxx @@ -15,8 +15,10 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#ifndef MESSAGE_DIALOG_HXX -#define MESSAGE_DIALOG_HXX +#ifndef HIGHSCORE_DIALOG_HXX +#define HIGHSCORE_DIALOG_HXX + +#define HIGHSCORE_HEADER "06000000highscores" //class Properties; class CommandSender; @@ -25,6 +27,8 @@ class OSystem; class EditTextWidget; class PopUpWidget; +#include "Serializable.hxx" + #include "Dialog.hxx" /** @@ -47,12 +51,43 @@ class HighScoresDialog : public Dialog void saveConfig() override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - private: - Int32 myVariation; - Int32 myScore; - Int32 mySpecial; + void handlePlayedVariation(); + void handleVariation(bool init = false); + void resetVisibility(); - Int32 myScores[NUM_POSITIONS]; + void saveHighScores() const; + void loadHighScores(Int32 variation); + + /** + Saves the current high scores for this game and variation to the given Serializer. + + @param out The serializer device to save to. + @return The result of the save. True on success, false on failure. + */ + bool save(Serializer& out) const; + + /** + Loads the current high scores for this game and variation from the given Serializer. + + @param in The Serializer device to load from. + @return The result of the load. True on success, false on failure. + */ + bool load(Serializer& in, Int32 variation); + + enum { + kVariationChanged = 'Vach' + }; + + + private: + string myInitials; + Int32 myDisplayedVariation; + Int32 myPlayedVariation; + Int32 myEditPos; + Int32 myHighScorePos; + string myNow; + + Int32 myHighScores[NUM_POSITIONS]; Int32 mySpecials[NUM_POSITIONS]; string myNames[NUM_POSITIONS]; string myDates[NUM_POSITIONS];