SettingsHandler.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // Copyright 2012 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. // Thanks to Treeki for writing the original class - 29/01/2012
  4. #include "Common/SettingsHandler.h"
  5. #include <algorithm>
  6. #include <cstddef>
  7. #include <ctime>
  8. #include <iomanip>
  9. #include <string>
  10. #include <fmt/chrono.h>
  11. #include "Common/CommonTypes.h"
  12. namespace Common
  13. {
  14. namespace
  15. {
  16. // Key used to encrypt/decrypt setting.txt contents
  17. constexpr u32 INITIAL_SEED = 0x73B5DBFA;
  18. } // namespace
  19. SettingsWriter::SettingsWriter() : m_buffer{}, m_position{0}, m_key{INITIAL_SEED}
  20. {
  21. }
  22. const SettingsBuffer& SettingsWriter::GetBytes() const
  23. {
  24. return m_buffer;
  25. }
  26. std::string SettingsReader::GetValue(std::string_view key) const
  27. {
  28. constexpr char delim[] = "\n";
  29. std::string toFind = std::string(delim).append(key).append("=");
  30. size_t found = m_decoded.find(toFind);
  31. if (found != std::string_view::npos)
  32. {
  33. size_t delimFound = m_decoded.find(delim, found + toFind.length());
  34. if (delimFound == std::string_view::npos)
  35. delimFound = m_decoded.length() - 1;
  36. return m_decoded.substr(found + toFind.length(), delimFound - (found + toFind.length()));
  37. }
  38. else
  39. {
  40. toFind = std::string(key).append("=");
  41. found = m_decoded.find(toFind);
  42. if (found == 0)
  43. {
  44. size_t delimFound = m_decoded.find(delim, found + toFind.length());
  45. if (delimFound == std::string_view::npos)
  46. delimFound = m_decoded.length() - 1;
  47. return m_decoded.substr(found + toFind.length(), delimFound - (found + toFind.length()));
  48. }
  49. }
  50. return "";
  51. }
  52. SettingsReader::SettingsReader(const SettingsBuffer& buffer) : m_decoded{""}
  53. {
  54. u32 key = INITIAL_SEED;
  55. for (u32 position = 0; position < buffer.size(); ++position)
  56. {
  57. m_decoded.push_back((u8)(buffer[position] ^ key));
  58. key = (key >> 31) | (key << 1);
  59. }
  60. // The decoded data normally uses CRLF line endings, but occasionally
  61. // (see the comment in WriteLine), lines can be separated by CRLFLF.
  62. // To handle this, we remove every CR and treat LF as the line ending.
  63. // (We ignore empty lines.)
  64. std::erase(m_decoded, '\x0d');
  65. }
  66. void SettingsWriter::AddSetting(std::string_view key, std::string_view value)
  67. {
  68. WriteLine(fmt::format("{}={}\r\n", key, value));
  69. }
  70. void SettingsWriter::WriteLine(std::string_view str)
  71. {
  72. const u32 old_position = m_position;
  73. const u32 old_key = m_key;
  74. // Encode and write the line
  75. for (char c : str)
  76. WriteByte(c);
  77. // If the encoded data contains a null byte, Nintendo's decoder will stop at that null byte
  78. // instead of decoding all the data. To avoid this: If the data we just wrote contains
  79. // a null byte, add an LF right before the line to prod the values into being different,
  80. // just like Nintendo does. Due to the chosen key, LF itself never encodes into a null byte.
  81. const auto begin = m_buffer.cbegin() + old_position;
  82. const auto end = m_buffer.cbegin() + m_position;
  83. if (std::find(begin, end, 0) != end)
  84. {
  85. m_key = old_key;
  86. m_position = old_position;
  87. WriteByte('\n');
  88. WriteLine(str);
  89. }
  90. }
  91. void SettingsWriter::WriteByte(u8 b)
  92. {
  93. if (m_position >= m_buffer.size())
  94. return;
  95. m_buffer[m_position] = b ^ m_key;
  96. m_position++;
  97. m_key = (m_key >> 31) | (m_key << 1);
  98. }
  99. std::string SettingsWriter::GenerateSerialNumber()
  100. {
  101. const std::time_t t = std::time(nullptr);
  102. // Must be 9 characters at most; otherwise the serial number will be rejected by SDK libraries,
  103. // as there is a check to ensure the string length is strictly lower than 10.
  104. // 3 for %j, 2 for %H, 2 for %M, 2 for %S.
  105. return fmt::format("{:%j%H%M%S}", fmt::localtime(t));
  106. }
  107. } // namespace Common