diff --git a/src/common/HighScoresManager.cxx b/src/common/HighScoresManager.cxx index 397d93be9..515eb966d 100644 --- a/src/common/HighScoresManager.cxx +++ b/src/common/HighScoresManager.cxx @@ -23,9 +23,9 @@ B, ; variation format (BCD, HEX) 0, ; zero-based variation Addresses (in hex): - n*p-times xx, ; score addresses for each player, high to low + n*p-times xx, ; score info for each player, high to low xx, ; variation address (if more than 1 variation) - xx, ; player address (if more than 1 player) + xx ; player address (if more than 1 player) TODO: - variation bits (Centipede) @@ -41,6 +41,10 @@ #include "HighScoresManager.hxx" +using namespace BSPF; +using namespace std; +using namespace HSM; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - HighScoresManager::HighScoresManager(OSystem& osystem) : myOSystem(osystem) @@ -79,8 +83,8 @@ string HighScoresManager::getPropIdx(const Properties& props, PropType type, uIn { string property = props.get(type); - std::replace(property.begin(), property.end(), ',', ' '); - std::replace(property.begin(), property.end(), '|', ' '); + replace(property.begin(), property.end(), ',', ' '); + replace(property.begin(), property.end(), '|', ' '); istringstream buf(property); string result; @@ -96,8 +100,7 @@ uInt32 HighScoresManager::numPlayers(const Properties& props) const { string numPlayers = getPropIdx(props, PropType::Cart_Players); - return numPlayers == EmptyString ? - DEFAULT_PLAYER : std::min(uInt32(stoi(numPlayers)), MAX_PLAYERS); + return min(uInt32(stringToInt(numPlayers, DEFAULT_PLAYER)), MAX_PLAYERS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -105,25 +108,24 @@ uInt32 HighScoresManager::numVariations(const Properties& props) const { string numVariations = getPropIdx(props, PropType::Cart_Variations); - return numVariations == EmptyString ? - DEFAULT_VARIATION : std::min(uInt32(stoi(numVariations)), MAX_VARIATIONS); + return min(uInt32(stringToInt(numVariations, DEFAULT_VARIATION)), MAX_VARIATIONS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool HighScoresManager::get(const Properties& props, uInt32& numPlayersR, uInt32& numVariationsR, - Formats& formats, Addresses& addresses) const + ScoresInfo& info) const { numPlayersR = numPlayers(props); numVariationsR = numVariations(props); - formats.numDigits = numDigits(props); - formats.trailingZeroes = trailingZeroes(props); - formats.scoreBCD = scoreBCD(props); - formats.varsBCD = varBCD(props); - formats.varsZeroBased = varZeroBased(props); + info.numDigits = numDigits(props); + info.trailingZeroes = trailingZeroes(props); + info.scoreBCD = scoreBCD(props); + info.varsBCD = varBCD(props); + info.varsZeroBased = varZeroBased(props); - addresses.playersAddr = playerAddress(props); - addresses.varsAddr = varAddress(props); + info.playersAddr = playerAddress(props); + info.varsAddr = varAddress(props); for (uInt32 p = 0; p < MAX_PLAYERS; ++p) { if (p < numPlayersR) @@ -133,12 +135,12 @@ bool HighScoresManager::get(const Properties& props, uInt32& numPlayersR, uInt32 uInt32 idx = p * numAddrBytes(props) + a; string addr = getPropIdx(props, PropType::Cart_Addresses, idx); - addresses.scoresAddr[p][a] = (addr == EmptyString ? 0 : stoi(addr, nullptr, 16)); + info.scoresAddr[p][a] = stringToIntBase16(addr); } } else for (uInt32 a = 0; a < numAddrBytes(props); ++a) - addresses.scoresAddr[p][a] = -1; + info.scoresAddr[p][a] = -1; } return (EmptyString != getPropIdx(props, PropType::Cart_Addresses, 0)); @@ -146,31 +148,43 @@ bool HighScoresManager::get(const Properties& props, uInt32& numPlayersR, uInt32 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void HighScoresManager::set(Properties& props, uInt32 numPlayers, uInt32 numVariations, - const Formats& formats, const Addresses& addresses) const + const ScoresInfo& info) const { ostringstream buf; + string output; - props.set(PropType::Cart_Players, std::to_string(numPlayers)); - props.set(PropType::Cart_Variations, std::to_string(std::min(numVariations, MAX_VARIATIONS))); + props.set(PropType::Cart_Players, to_string(numPlayers)); + props.set(PropType::Cart_Variations, to_string(min(numVariations, MAX_VARIATIONS))); + // fill from the back to skip default values + if (info.varsZeroBased != DEFAULT_VARS_ZERO_BASED) + output = info.varsZeroBased ? ",1" : ",0"; + if (output.length() || info.varsBCD != DEFAULT_VARS_BCD) + output.insert(0, info.varsBCD ? ",B" : ",D"); + if (output.length() || info.scoreBCD != DEFAULT_SCORE_BCD) + output.insert(0, info.scoreBCD ? ",B" : ",H"); + if (output.length() || info.trailingZeroes != DEFAULT_TRAILING) + output.insert(0, "," + to_string(info.trailingZeroes)); + if (output.length() || info.numDigits != DEFAULT_DIGITS) + output.insert(0, to_string(info.numDigits)); + props.set(PropType::Cart_Formats, output); - buf << formats.numDigits << "," - << formats.trailingZeroes << "," - << (formats.scoreBCD ? "B" : "H") << "," - << (formats.varsBCD ? "B" : "D") << "," - << formats.varsZeroBased; - props.set(PropType::Cart_Formats, buf.str()); - - buf.str(""); for (uInt32 p = 0; p < numPlayers; ++p) { - for (uInt32 a = 0; a < numAddrBytes(formats.numDigits, formats.trailingZeroes); ++a) - buf << std::hex << addresses.scoresAddr[p][a] << ","; + for (uInt32 a = 0; a < numAddrBytes(info.numDigits, info.trailingZeroes); ++a) + buf << hex << info.scoresAddr[p][a] << ","; } - buf << addresses.varsAddr << ","; - buf << addresses.playersAddr; - props.set(PropType::Cart_Addresses, buf.str()); + + // add optional addresses + if (numVariations > 1 || numPlayers > 1) + buf << info.varsAddr << "," ; + if (numPlayers > 1) + buf << info.playersAddr << "," ; + + output = buf.str(); + output.pop_back(); + props.set(PropType::Cart_Addresses, output); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -178,7 +192,7 @@ uInt32 HighScoresManager::numDigits(const Properties& props) const { string digits = getPropIdx(props, PropType::Cart_Formats, 0); - return digits == EmptyString ? DEFAULT_DIGITS : stoi(digits); + return min(uInt32(stringToInt(digits, DEFAULT_DIGITS)), MAX_DIGITS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -186,7 +200,8 @@ uInt32 HighScoresManager::trailingZeroes(const Properties& props) const { string trailing = getPropIdx(props, PropType::Cart_Formats, 1); - return trailing == EmptyString ? DEFAULT_TRAILING : stoi(trailing);} + return min(uInt32(stringToInt(trailing, DEFAULT_TRAILING)), MAX_TRAILING); +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool HighScoresManager::scoreBCD(const Properties& props) const @@ -225,7 +240,7 @@ uInt16 HighScoresManager::playerAddress(const Properties& props) const uInt32 idx = numAddrBytes(props) * numPlayers(props) + 1; string addr = getPropIdx(props, PropType::Cart_Addresses, idx); - return addr == EmptyString ? DEFAULT_ADDRESS : stoi(addr, nullptr, 16); + return stringToIntBase16(addr, DEFAULT_ADDRESS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -234,7 +249,7 @@ uInt16 HighScoresManager::varAddress(const Properties& props) const uInt32 idx = numAddrBytes(props) * numPlayers(props); string addr = getPropIdx(props, PropType::Cart_Addresses, idx); - return addr == EmptyString ? DEFAULT_ADDRESS : stoi(addr, nullptr, 16); + return stringToIntBase16(addr, DEFAULT_ADDRESS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -250,8 +265,8 @@ uInt32 HighScoresManager::numAddrBytes(const Properties& props) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Int32 HighScoresManager::score(uInt32 player, uInt32 numAddrBytes, uInt32 trailingZeroes, bool isBCD, - const ScoreAddresses& scoreAddr) const +Int32 HighScoresManager::score(uInt32 player, uInt32 numAddrBytes, uInt32 trailingZeroes, + bool isBCD, const ScoreAddresses& scoreAddr) const { if (!myOSystem.hasConsole()) return -1; @@ -322,7 +337,7 @@ Int32 HighScoresManager::score() const if (addr == EmptyString) return -1; - scoreAddr[b] = stoi(addr, nullptr, 16); + scoreAddr[b] = stringToIntBase16(addr); } return score(currentPlayer, numBytes, trailingZeroes(props), scoreBCD(props), scoreAddr); diff --git a/src/common/HighScoresManager.hxx b/src/common/HighScoresManager.hxx index 74167827b..08b1533ad 100644 --- a/src/common/HighScoresManager.hxx +++ b/src/common/HighScoresManager.hxx @@ -20,91 +20,128 @@ class OSystem; -class HighScoresManager -{ - -public: +namespace HSM { static const uInt32 MAX_PLAYERS = 4; static const uInt32 MAX_SCORE_ADDR = 3; + static const uInt32 DEFAULT_PLAYER = 1; + static const uInt32 DEFAULT_VARIATION = 1; + static const uInt32 DEFAULT_ADDRESS = 0; + using ScoreAddresses = array; using ScoresAddresses = array; - struct Formats { + struct ScoresInfo { + // Formats uInt32 numDigits; uInt32 trailingZeroes; bool scoreBCD; bool varsBCD; bool varsZeroBased; - }; - - struct Addresses { + // Addresses ScoresAddresses scoresAddr; uInt16 varsAddr; uInt16 playersAddr; }; - HighScoresManager(OSystem& osystem); - virtual ~HighScoresManager() = default; +} // namespace HSM - /* - Methods for returning high scores related variables - */ - bool get(const Properties& props, uInt32& numPlayers, uInt32& numVariations, - Formats& formats, Addresses& addresses) const; - void set(Properties& props, uInt32 numPlayers, uInt32 numVariations, - const Formats& formats, const Addresses& addresses) const; +using namespace HSM; - uInt32 numAddrBytes(Int32 digits, Int32 trailing) const; - Int32 score(uInt32 player, uInt32 numAddrBytes, uInt32 trailingZeroes, bool isBCD, - const ScoreAddresses& scoreAddr) const; +/** + This class provides an interface to define, load and save scores. It is meant + for games which do not support saving highscores. - // retrieve current values - Int32 player() const; - Int32 variation() const; - Int32 score() const; + TODO: load and saves scores -private: - static const uInt32 MAX_VARIATIONS = 256; - static const uInt32 DEFAULT_PLAYER = 1; - static const uInt32 DEFAULT_VARIATION = 1; - static const uInt32 DEFAULT_DIGITS = 4; - static const uInt32 DEFAULT_TRAILING = 0; - static const bool DEFAULT_SCORE_BCD = true; - static const bool DEFAULT_VARS_BCD = true; - static const bool DEFAULT_VARS_ZERO_BASED = false; - static const bool DEFAULT_PLAYERS_ZERO_BASED = true; - static const uInt32 DEFAULT_ADDRESS = 0; + @author Thomas Jentzsch +*/ -private: - uInt32 numVariations(const Properties& props) const; - uInt32 numPlayers(const Properties& props) const; - uInt16 varAddress(const Properties& props) const; - uInt16 playerAddress(const Properties& props) const; - uInt32 numDigits(const Properties& props) const; - uInt32 trailingZeroes(const Properties& props) const; - bool scoreBCD(const Properties& props) const; - bool varBCD(const Properties& props) const; - bool varZeroBased(const Properties& props) const; - bool playerZeroBased(const Properties& props) const; +class HighScoresManager +{ + public: + HighScoresManager(OSystem& osystem); + virtual ~HighScoresManager() = default; - uInt32 numAddrBytes(const Properties& props) const; + /** + Get the highscore data of game's properties - Properties& properties(Properties& props) const; - string getPropIdx(const Properties& props, PropType type, uInt32 idx = 0) const; - Int16 peek(uInt16 addr) const; + @return True if highscore data exists, else false + */ + bool get(const Properties& props, uInt32& numPlayers, uInt32& numVariations, + ScoresInfo& info) const; + /** + Set the highscore data of game's properties + */ + void set(Properties& props, uInt32 numPlayers, uInt32 numVariations, + const ScoresInfo& info) const; -private: - // Reference to the osystem object - OSystem& myOSystem; + /** + Calculate the number of bytes for one player's score from given parameters -private: - // Following constructors and assignment operators not supported - HighScoresManager() = delete; - HighScoresManager(const HighScoresManager&) = delete; - HighScoresManager(HighScoresManager&&) = delete; - HighScoresManager& operator=(const HighScoresManager&) = delete; - HighScoresManager& operator=(HighScoresManager&&) = delete; + @return The number of score address bytes + */ + uInt32 numAddrBytes(Int32 digits, Int32 trailing) const; + + /** + Calculate the score from given parameters + + @return The current score or -1 if no valid data exists + */ + Int32 score(uInt32 player, uInt32 numAddrBytes, uInt32 trailingZeroes, bool isBCD, + const ScoreAddresses& scoreAddr) const; + + // Retrieve current values (using game's properties) + Int32 player() const; + Int32 variation() const; + Int32 score() const; + + private: + static const uInt32 MAX_VARIATIONS = 256; + + static const uInt32 MAX_DIGITS = 6; + static const uInt32 MAX_TRAILING = 3; + static const uInt32 DEFAULT_DIGITS = 4; + static const uInt32 DEFAULT_TRAILING = 0; + static const bool DEFAULT_SCORE_BCD = true; + static const bool DEFAULT_VARS_BCD = true; + static const bool DEFAULT_VARS_ZERO_BASED = false; + static const bool DEFAULT_PLAYERS_ZERO_BASED = true; + + private: + // Get individual highscore info from properties + uInt32 numVariations(const Properties& props) const; + uInt32 numPlayers(const Properties& props) const; + uInt16 varAddress(const Properties& props) const; + uInt16 playerAddress(const Properties& props) const; + uInt32 numDigits(const Properties& props) const; + uInt32 trailingZeroes(const Properties& props) const; + bool scoreBCD(const Properties& props) const; + bool varBCD(const Properties& props) const; + bool varZeroBased(const Properties& props) const; + bool playerZeroBased(const Properties& props) const; + + // Calculate the number of bytes for one player's score from property parameters + uInt32 numAddrBytes(const Properties& props) const; + + // Get properties + Properties& properties(Properties& props) const; + // Get value from highscore propterties at given index + string getPropIdx(const Properties& props, PropType type, uInt32 idx = 0) const; + // Peek into memory + Int16 peek(uInt16 addr) const; + + private: + // Reference to the osystem object + OSystem& myOSystem; + + private: + // Following constructors and assignment operators not supported + HighScoresManager() = delete; + HighScoresManager(const HighScoresManager&) = delete; + HighScoresManager(HighScoresManager&&) = delete; + HighScoresManager& operator=(const HighScoresManager&) = delete; + HighScoresManager& operator=(HighScoresManager&&) = delete; }; #endif diff --git a/src/common/bspf.hxx b/src/common/bspf.hxx index e7cc60024..d47d076bd 100644 --- a/src/common/bspf.hxx +++ b/src/common/bspf.hxx @@ -152,6 +152,13 @@ namespace BSPF catch(...) { return defaultValue; } } + // Convert string with base 16 to integer, using default value on any error + inline int stringToIntBase16(const string& s, const int defaultValue = 0) + { + try { return std::stoi(s, nullptr, 16); } + catch(...) { return defaultValue; } + } + // Compare two strings, ignoring case inline int compareIgnoreCase(const string& s1, const string& s2) { diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index 26b56fdcf..f68b93fb4 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -42,6 +42,9 @@ #include "GameInfoDialog.hxx" +using namespace std; +using namespace BSPF; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GameInfoDialog::GameInfoDialog( OSystem& osystem, DialogContainer& parent, const GUI::Font& font, @@ -373,7 +376,7 @@ GameInfoDialog::GameInfoDialog( pwidth = font.getStringWidth("4"); myPlayersLabel = new StaticTextWidget(myTab, font, xpos, ypos + 1, lwidth, fontHeight, "Players"); - myPlayers = new PopUpWidget(myTab, font, xpos + lwidth, ypos, pwidth, lineHeight, items, "", 0, kPlayersChanged); + myPlayers = new PopUpWidget(myTab, font, xpos + lwidth, ypos, pwidth, lineHeight, items, "", 0, kHiScoresChanged); wid.push_back(myPlayers); int awidth = font.getStringWidth("FFFF") + 4; @@ -402,10 +405,10 @@ GameInfoDialog::GameInfoDialog( myVarAddressVal = new EditTextWidget(myTab, font, myVarAddress->getRight() + 2, ypos - 1, vwidth, lineHeight); myVarAddressVal->setEditable(false); - myVarsBCD = new CheckboxWidget(myTab, font, myVarAddressVal->getRight() + 16, ypos + 1, "BCD", kVarBcdChanged); + myVarsBCD = new CheckboxWidget(myTab, font, myVarAddressVal->getRight() + 16, ypos + 1, "BCD", kHiScoresChanged); wid.push_back(myVarsBCD); - myVarsZeroBased = new CheckboxWidget(myTab, font, myVarsBCD->getRight() + 16, ypos + 1, "0-based", kVarZeroBasedChanged); + myVarsZeroBased = new CheckboxWidget(myTab, font, myVarsBCD->getRight() + 16, ypos + 1, "0-based", kHiScoresChanged); wid.push_back(myVarsZeroBased); ypos += lineHeight + VGAP; @@ -425,7 +428,7 @@ GameInfoDialog::GameInfoDialog( myScoreDigitsLabel = new StaticTextWidget(myTab, font, xpos, ypos + 1, "Digits "); myScoreDigits = new PopUpWidget(myTab, font, myScoreDigitsLabel->getRight(), ypos, pwidth, lineHeight, - items, "", 0, kScoreDigitsChanged); + items, "", 0, kHiScoresChanged); wid.push_back(myScoreDigits); items.clear(); @@ -437,22 +440,22 @@ GameInfoDialog::GameInfoDialog( myTrailingZeroesLabel = new StaticTextWidget(myTab, font, myScoreDigits->getRight() + 20, ypos + 1, "0-Digits "); myTrailingZeroes = new PopUpWidget(myTab, font, myTrailingZeroesLabel->getRight(), ypos, pwidth, lineHeight, - items, "", 0, kScoreZeroesChanged); + items, "", 0, kHiScoresChanged); wid.push_back(myTrailingZeroes); - myScoreBCD = new CheckboxWidget(myTab, font, myVarsBCD->getLeft(), ypos + 1, "BCD", kScoreBcdChanged); + myScoreBCD = new CheckboxWidget(myTab, font, myVarsBCD->getLeft(), ypos + 1, "BCD", kHiScoresChanged); wid.push_back(myScoreBCD); - for (uInt32 p = 0; p < HighScoresManager::MAX_PLAYERS; ++p) + for (uInt32 p = 0; p < HSM::MAX_PLAYERS; ++p) { uInt32 s_xpos = xpos; ypos += lineHeight + VGAP; myScoreAddressesLabel[p] = new StaticTextWidget(myTab, font, s_xpos, ypos + 1, - "P" + std::to_string(p + 1) + " Addresses "); + "P" + to_string(p + 1) + " Addresses "); s_xpos += myScoreAddressesLabel[p]->getWidth(); - for (uInt32 a = 0; a < HighScoresManager::MAX_SCORE_ADDR; ++a) + for (uInt32 a = 0; a < HSM::MAX_SCORE_ADDR; ++a) { myScoreAddress[p][a] = new EditTextWidget(myTab, font, s_xpos, ypos - 1, awidth, lineHeight); myScoreAddress[p][a]->setTextFilter(fAddr); @@ -584,10 +587,10 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props) myPPBlend->setEnabled(!alwaysPhosphor && usePhosphor); const string& blend = props.get(PropType::Display_PPBlend); - myPPBlend->setValue(BSPF::stringToInt(blend)); + myPPBlend->setValue(stringToInt(blend)); // set vertical center - Int32 vcenter = BSPF::stringToInt(props.get(PropType::Display_VCenter)); + Int32 vcenter = stringToInt(props.get(PropType::Display_VCenter)); myVCenter->setValueLabel(vcenter); myVCenter->setValue(vcenter); myVCenter->setValueUnit(vcenter ? "px" : ""); @@ -620,7 +623,7 @@ void GameInfoDialog::loadControllerProperties(const Properties& props) istringstream m_axis(props.get(PropType::Controller_MouseAxis)); string m_control, m_range; m_axis >> m_control; - bool autoAxis = BSPF::equalsIgnoreCase(m_control, "AUTO"); + bool autoAxis = equalsIgnoreCase(m_control, "AUTO"); myMouseControl->setState(!autoAxis); if(autoAxis) { @@ -636,7 +639,7 @@ void GameInfoDialog::loadControllerProperties(const Properties& props) myMouseY->setEnabled(!autoAxis); if(m_axis >> m_range) { - myMouseRange->setValue(BSPF::stringToInt(m_range)); + myMouseRange->setValue(stringToInt(m_range)); } else { @@ -660,51 +663,49 @@ void GameInfoDialog::loadCartridgeProperties(const Properties& props) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void GameInfoDialog::loadHighScoresProperties(const Properties& props) { - HighScoresManager::Formats formats; - HighScoresManager::Addresses addresses; + HSM::ScoresInfo info; uInt32 numPlayers, numVariations; - bool enable = instance().highScores().get(props, numPlayers, numVariations, - formats, addresses); + bool enable = instance().highScores().get(props, numPlayers, numVariations, info); myHighScores->setState(enable); myPlayers->setSelected(numPlayers); - myVariations->setText(std::to_string(numVariations)); + myVariations->setText(to_string(numVariations)); ostringstream ss; - myScoreDigits->setSelected(formats.numDigits); - myTrailingZeroes->setSelected(formats.trailingZeroes); - myScoreBCD->setState(formats.scoreBCD); - myVarsBCD->setState(formats.varsBCD); - myVarsZeroBased->setState(formats.varsZeroBased); + myScoreDigits->setSelected(info.numDigits); + myTrailingZeroes->setSelected(info.trailingZeroes); + myScoreBCD->setState(info.scoreBCD); + myVarsBCD->setState(info.varsBCD); + myVarsZeroBased->setState(info.varsZeroBased); ss.str(""); - ss << std::hex << std::right << std::setw(4) << std::setfill('0') - << std::uppercase << addresses.playersAddr; + ss << hex << right << setw(4) << setfill('0') + << uppercase << info.playersAddr; myPlayersAddress->setText(ss.str()); ss.str(""); - ss << std::hex << std::right << std::setw(4) << std::setfill('0') - << std::uppercase << addresses.varsAddr; + ss << hex << right << setw(4) << setfill('0') + << uppercase << info.varsAddr; myVarAddress->setText(ss.str()); - for (uInt32 p = 0; p < HighScoresManager::MAX_PLAYERS; ++p) + for (uInt32 p = 0; p < HSM::MAX_PLAYERS; ++p) { - for (uInt32 a = 0; a < instance().highScores().numAddrBytes(formats.numDigits, formats.trailingZeroes); ++a) + for (uInt32 a = 0; a < instance().highScores().numAddrBytes(info.numDigits, info.trailingZeroes); ++a) { if (p < numPlayers) { ss.str(""); - ss << std::hex << std::right << std::setw(4) << std::setfill('0') - << std::uppercase << addresses.scoresAddr[p][a]; + ss << hex << right << setw(4) << setfill('0') + << uppercase << info.scoresAddr[p][a]; myScoreAddress[p][a]->setText(ss.str()); } else myScoreAddress[p][a]->setText(""); } } - handleHighScoresWidgets(); + updateHighScoresWidgets(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -719,7 +720,7 @@ void GameInfoDialog::saveConfig() myPPBlend->getValueLabel()); Int32 vcenter = myVCenter->getValue(); - myGameProperties.set(PropType::Display_VCenter, std::to_string(vcenter)); + myGameProperties.set(PropType::Display_VCenter, to_string(vcenter)); myGameProperties.set(PropType::Cart_Sound, mySound->getState() ? "STEREO" : "MONO"); // Console properties @@ -780,40 +781,38 @@ void GameInfoDialog::saveConfig() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void GameInfoDialog::saveHighScoresProperties() { - HighScoresManager::Formats formats; - HighScoresManager::Addresses addresses; + HSM::ScoresInfo info; if (myHighScores->getState()) { - // fill formats - formats.varsZeroBased = myVarsZeroBased->getState(); - formats.varsBCD = myVarsBCD->getState(); - formats.numDigits = myScoreDigits->getSelected() + 1; - formats.trailingZeroes = myTrailingZeroes->getSelected(); - formats.scoreBCD = myScoreBCD->getState(); + // fill info + info.varsZeroBased = myVarsZeroBased->getState(); + info.varsBCD = myVarsBCD->getState(); + info.numDigits = myScoreDigits->getSelected() + 1; + info.trailingZeroes = myTrailingZeroes->getSelected(); + info.scoreBCD = myScoreBCD->getState(); - // fill addresses + // fill info string strAddr; strAddr = myPlayersAddress->getText(); - addresses.playersAddr = strAddr == EmptyString ? 1 : stoi(strAddr, nullptr, 16); + info.playersAddr = stringToIntBase16(strAddr, HSM::DEFAULT_ADDRESS); strAddr = myVarAddress->getText(); - addresses.varsAddr = strAddr == EmptyString ? 1 : stoi(strAddr, nullptr, 16); + info.varsAddr = stringToIntBase16(strAddr, HSM::DEFAULT_ADDRESS); - for (uInt32 p = 0; p < HighScoresManager::MAX_PLAYERS; ++p) + for (uInt32 p = 0; p < HSM::MAX_PLAYERS; ++p) { - for (uInt32 a = 0; a < HighScoresManager::MAX_SCORE_ADDR; ++a) + for (uInt32 a = 0; a < HSM::MAX_SCORE_ADDR; ++a) { strAddr = myScoreAddress[p][a]->getText(); - addresses.scoresAddr[p][a] = strAddr == EmptyString ? 0 : stoi(strAddr, nullptr, 16); + info.scoresAddr[p][a] = stringToIntBase16(strAddr, HSM::DEFAULT_ADDRESS); } } string strVars = myVariations->getText(); instance().highScores().set(myGameProperties, myPlayers->getSelected() + 1, - strVars == EmptyString ? 1 : stoi(strVars), - formats, addresses); + stringToInt(strVars, HSM::DEFAULT_VARIATION), info); } else { @@ -913,10 +912,10 @@ void GameInfoDialog::updateControllerStates() // Compumate bankswitching scheme doesn't allow to select controllers bool enableSelectControl = myBSType->getSelectedTag() != "CM"; // Enable Swap Paddles checkbox only for paddle games - bool enableSwapPaddles = BSPF::startsWithIgnoreCase(contrLeft, "PADDLES") || - BSPF::startsWithIgnoreCase(contrRight, "PADDLES") || - BSPF::startsWithIgnoreCase(myLeftPortDetected->getLabel(), "Paddles") || - BSPF::startsWithIgnoreCase(myRightPortDetected->getLabel(), "Paddles"); + bool enableSwapPaddles = startsWithIgnoreCase(contrLeft, "PADDLES") || + startsWithIgnoreCase(contrRight, "PADDLES") || + startsWithIgnoreCase(myLeftPortDetected->getLabel(), "Paddles") || + startsWithIgnoreCase(myRightPortDetected->getLabel(), "Paddles"); if(instance().hasConsole()) { @@ -966,7 +965,7 @@ void GameInfoDialog::eraseEEPROM() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void GameInfoDialog::handleHighScoresWidgets() +void GameInfoDialog::updateHighScoresWidgets() { bool enable = myHighScores->getState(); uInt32 players = myPlayers->getSelected() + 1; @@ -990,7 +989,7 @@ void GameInfoDialog::handleHighScoresWidgets() myVarAddress->setEnabled(enableVars); myVarAddress->setEditable(enableVars); myVarAddressVal->setEnabled(enableVars); - myVarsBCD->setEnabled(enableVars); + myVarsBCD->setEnabled(enableVars && stringToInt(myVariations->getText(), 1) >= 10); myVarsZeroBased->setEnabled(enableVars); myScoresLabel->setEnabled(enable); @@ -1001,12 +1000,12 @@ void GameInfoDialog::handleHighScoresWidgets() myTrailingZeroes->setEnabled(enable); myCurrentScoreLabel->setEnabled(enable); - for (uInt32 p = 0; p < HighScoresManager::MAX_PLAYERS; ++p) + for (uInt32 p = 0; p < HSM::MAX_PLAYERS; ++p) { enable &= players > p; myScoreAddressesLabel[p]->setEnabled(enable); - for (uInt32 a = 0; a < HighScoresManager::MAX_SCORE_ADDR; ++a) + for (uInt32 a = 0; a < HSM::MAX_SCORE_ADDR; ++a) { myScoreAddress[p][a]->setEnabled(enable && numAddr > a); myScoreAddressVal[p][a]->setEnabled(enable && numAddr > a); @@ -1027,17 +1026,17 @@ void GameInfoDialog::handleHighScoresWidgets() setAddressVal(myVarAddress, myVarAddressVal, myVarsBCD->getState(), myVarsZeroBased->getState() ? 1 : 0); // update score RAM values and resulting scores - for (uInt32 p = 0; p < HighScoresManager::MAX_PLAYERS; ++p) + for (uInt32 p = 0; p < HSM::MAX_PLAYERS; ++p) { if (p < players) { - HighScoresManager::ScoreAddresses scoreAddr; + HSM::ScoreAddresses scoreAddr; for (uInt32 a = 0; a < numAddr; ++a) { setAddressVal(myScoreAddress[p][a], myScoreAddressVal[p][a]); string strAddr = myScoreAddress[p][a]->getText(); - scoreAddr[a] = strAddr == EmptyString ? 0 : stoi(strAddr, nullptr, 16); + scoreAddr[a] = stringToIntBase16(strAddr, HSM::DEFAULT_ADDRESS); } Int32 score = instance().highScores().score(p, numAddr, myTrailingZeroes->getSelected(), @@ -1047,7 +1046,7 @@ void GameInfoDialog::handleHighScoresWidgets() ostringstream ss; ss.str(""); - ss << std::right << std::setw(myScoreDigits->getSelected() + 1) << std::setfill(' ') << score; + ss << right << setw(myScoreDigits->getSelected() + 1) << setfill(' ') << score; myCurrentScore[p]->setLabel(ss.str()); } else @@ -1081,13 +1080,13 @@ void GameInfoDialog::setAddressVal(EditTextWidget* addressWidget, EditTextWidget ostringstream ss; // convert to number and read from memory - addr = strAddr == EmptyString ? 0 : stoi(strAddr, nullptr, 16); + addr = stringToIntBase16(strAddr, HSM::DEFAULT_ADDRESS); val = system.peek(addr) + incVal; // format output and display in value widget if (isBCD) - ss << std::hex; - ss << std::right << std::setw(2) << std::setfill('0') - << std::uppercase << uInt16(val); + ss << hex; + ss << right << setw(2) << setfill('0') + << uppercase << uInt16(val); valWidget->setText(ss.str()); } else @@ -1163,13 +1162,7 @@ void GameInfoDialog::handleCommand(CommandSender* sender, int cmd, case EditTextWidget::kChangedCmd: case kHiScoresChanged: - case kPlayersChanged: - case kVarZeroBasedChanged: - case kVarBcdChanged: - case kScoreDigitsChanged: - case kScoreZeroesChanged: - case kScoreBcdChanged: - handleHighScoresWidgets(); + updateHighScoresWidgets(); break; default: diff --git a/src/gui/GameInfoDialog.hxx b/src/gui/GameInfoDialog.hxx index 30f94cce1..ebdc99ce1 100644 --- a/src/gui/GameInfoDialog.hxx +++ b/src/gui/GameInfoDialog.hxx @@ -59,10 +59,15 @@ class GameInfoDialog : public Dialog, public CommandSender // load the properties of the 'High Scores' tab void saveHighScoresProperties(); + // update 'Controller' tab widgets void updateControllerStates(); + // erase SaveKey/AtariVox pages for current game void eraseEEPROM(); - void handleHighScoresWidgets(); - void setAddressVal(EditTextWidget* address, EditTextWidget* val, bool isBCD = true, uInt8 incVal = 0); + // update 'High Scores' tab widgets + void updateHighScoresWidgets(); + // set formatted memory value for given address field + void setAddressVal(EditTextWidget* address, EditTextWidget* val, + bool isBCD = true, uInt8 incVal = 0); private: TabWidget* myTab{nullptr}; @@ -111,48 +116,42 @@ class GameInfoDialog : public Dialog, public CommandSender // High Scores properties CheckboxWidget* myHighScores{nullptr}; - StaticTextWidget* myPlayersLabel{ nullptr }; + StaticTextWidget* myPlayersLabel{nullptr}; PopUpWidget* myPlayers{nullptr}; - StaticTextWidget* myPlayersAddressLabel{ nullptr }; - EditTextWidget* myPlayersAddress{ nullptr }; - EditTextWidget* myPlayersAddressVal{ nullptr }; + StaticTextWidget* myPlayersAddressLabel{nullptr}; + EditTextWidget* myPlayersAddress{nullptr}; + EditTextWidget* myPlayersAddressVal{nullptr}; - StaticTextWidget* myVariationsLabel{ nullptr }; + StaticTextWidget* myVariationsLabel{nullptr}; EditTextWidget* myVariations{nullptr}; - StaticTextWidget* myVarAddressLabel{ nullptr }; - EditTextWidget* myVarAddress{ nullptr }; - EditTextWidget* myVarAddressVal{ nullptr }; - CheckboxWidget* myVarsBCD{ nullptr }; - CheckboxWidget* myVarsZeroBased{ nullptr }; + StaticTextWidget* myVarAddressLabel{nullptr}; + EditTextWidget* myVarAddress{nullptr}; + EditTextWidget* myVarAddressVal{nullptr}; + CheckboxWidget* myVarsBCD{nullptr}; + CheckboxWidget* myVarsZeroBased{nullptr}; - StaticTextWidget* myScoresLabel{ nullptr }; - StaticTextWidget* myScoreDigitsLabel{ nullptr }; + StaticTextWidget* myScoresLabel{nullptr}; + StaticTextWidget* myScoreDigitsLabel{nullptr}; PopUpWidget* myScoreDigits{nullptr}; - StaticTextWidget* myTrailingZeroesLabel{ nullptr }; + StaticTextWidget* myTrailingZeroesLabel{nullptr}; PopUpWidget* myTrailingZeroes{nullptr}; CheckboxWidget* myScoreBCD{nullptr}; - StaticTextWidget* myScoreAddressesLabel[HighScoresManager::MAX_PLAYERS]{ nullptr }; - EditTextWidget* myScoreAddress[HighScoresManager::MAX_PLAYERS][HighScoresManager::MAX_SCORE_ADDR]{nullptr}; - EditTextWidget* myScoreAddressVal[HighScoresManager::MAX_PLAYERS][HighScoresManager::MAX_SCORE_ADDR]{nullptr}; + StaticTextWidget* myScoreAddressesLabel[HSM::MAX_PLAYERS]{nullptr}; + EditTextWidget* myScoreAddress[HSM::MAX_PLAYERS][HSM::MAX_SCORE_ADDR]{nullptr}; + EditTextWidget* myScoreAddressVal[HSM::MAX_PLAYERS][HSM::MAX_SCORE_ADDR]{nullptr}; StaticTextWidget* myCurrentScoreLabel; - StaticTextWidget* myCurrentScore[HighScoresManager::MAX_PLAYERS]; + StaticTextWidget* myCurrentScore[HSM::MAX_PLAYERS]; enum { - kVCenterChanged = 'Vcch', - kPhosphorChanged = 'PPch', - kPPBlendChanged = 'PBch', - kLeftCChanged = 'LCch', - kRightCChanged = 'RCch', - kMCtrlChanged = 'MCch', - kEEButtonPressed = 'EEgb', - kHiScoresChanged = 'HSch', - kPlayersChanged = 'Plch', - kVarZeroBasedChanged = 'VZch', - kVarBcdChanged = 'VBch', - kScoreDigitsChanged = 'SDch', - kScoreZeroesChanged = 'SZch', - kScoreBcdChanged = 'SBch', + kVCenterChanged = 'Vcch', + kPhosphorChanged = 'PPch', + kPPBlendChanged = 'PBch', + kLeftCChanged = 'LCch', + kRightCChanged = 'RCch', + kMCtrlChanged = 'MCch', + kEEButtonPressed = 'EEgb', + kHiScoresChanged = 'HSch', }; // Game properties for currently loaded ROM