settings_state.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #include "settings_state.h"
  2. #include <array>
  3. #include <cstddef>
  4. namespace sunrise::state::account::settings {
  5. namespace {
  6. template <typename Value> struct Range {
  7. Value minimum;
  8. Value maximum;
  9. };
  10. /** The controller layout menu publishes these 7 stored values. */
  11. constexpr std::array<std::int8_t, 7> kButtonLayouts{0, 1, 2, 3, 5, 6, 9};
  12. /** The movement-layout menu stores 4 choices, 0 to 3. */
  13. constexpr Range<std::int8_t> kMovementModes{0, 3};
  14. /** Controller sensitivity stores the 10 menu choices as 0 to 9. */
  15. constexpr Range<std::int8_t> kControllerSensitivity{0, 9};
  16. /** Mouse sensitivity accepts the menu's 1 to 100 scale. */
  17. constexpr Range<std::int32_t> kMouseSensitivity{1, 100};
  18. /** ADS sensitivity stores the menu's 0.5 through 1.5 multiplier. */
  19. constexpr Range<float> kAdsSensitivity{0.5F, 1.5F};
  20. /** Double-press delay stores the 5 menu choices, 0 to 4. */
  21. constexpr Range<std::int8_t> kDoublePressDelay{0, 4};
  22. /** Two-choice selectors store only the menu values 0 and 1. */
  23. constexpr Range<std::int8_t> kTwoChoiceSelector{0, 1};
  24. /** Voice output stores 3 menu choices, 0 to 2. */
  25. constexpr Range<std::int8_t> kVoiceOutputMode{0, 2};
  26. /** Chat volume exposes 9 menu levels, 0 to 8. */
  27. constexpr Range<std::int8_t> kChatVolume{0, 8};
  28. /** Sound, dialogue and music expose 11 levels, 0 to 10. */
  29. constexpr Range<std::int8_t> kAudioVolume{0, 10};
  30. /** Brightness stores the 7 menu choices, 0 to 6. */
  31. constexpr Range<std::int8_t> kBrightness{0, 6};
  32. /** VSync stores the DXGI presentation interval from 0 to 4. */
  33. constexpr Range<std::uint8_t> kVerticalSyncInterval{0, 4};
  34. /** Field of view exposes the 55 through 105 degree range. */
  35. constexpr Range<std::int32_t> kFieldOfView{55, 105};
  36. /** The first unidentified calibration field uses the working renderer fallback. */
  37. constexpr float kCalibrationPrimary = 10000.0F;
  38. /** The second unidentified calibration field uses the working renderer alpha. */
  39. constexpr float kCalibrationAlpha = 0.0F;
  40. /** Subtitle mode stores 3 menu choices, 0 to 2. */
  41. constexpr Range<std::int8_t> kSubtitlesMode{0, 2};
  42. /** Colorblind mode stores 4 menu choices, 0 to 3. */
  43. constexpr Range<std::int8_t> kColorblindMode{0, 3};
  44. /** HUD opacity stores 4 menu choices, 0 to 3. */
  45. constexpr Range<std::int8_t> kHudOpacity{0, 3};
  46. /** Background opacity stores 5 menu choices, 0 to 4. */
  47. constexpr Range<std::int8_t> kBackgroundOpacity{0, 4};
  48. /** Reticle color stores 7 menu choices, 0 to 6. */
  49. constexpr Range<std::int8_t> kReticleColor{0, 6};
  50. /** Text size stores 5 menu choices, 0 to 4. */
  51. constexpr Range<std::int8_t> kTextSize{0, 4};
  52. /** Text color and background style each store 4 choices, 0 to 3. */
  53. constexpr Range<std::int8_t> kTextPresentationMode{0, 3};
  54. /** Unidentified text settings stay on their only known working value. */
  55. constexpr std::int8_t kUnidentifiedTextValue = 0;
  56. /** Text chat stores 4 menu choices, 0 to 3. */
  57. constexpr Range<std::int8_t> kTextChatMode{0, 3};
  58. /**
  59. * Tests one scalar against an inclusive Sunrise settings policy range.
  60. * @param range Inclusive supported range.
  61. * @return True when the value is inside the range.
  62. */
  63. template <typename Value> [[nodiscard]] bool within(Value value, Range<Value> range) noexcept {
  64. return value >= range.minimum && value <= range.maximum;
  65. }
  66. /**
  67. * Tests one scalar against a Sunrise settings policy domain with gaps.
  68. * @param values Every supported value.
  69. * @return True when the candidate is one of them.
  70. */
  71. template <typename Value, std::size_t Count>
  72. [[nodiscard]] bool contains(const std::array<Value, Count>& values, Value candidate) noexcept {
  73. for (const Value value : values) {
  74. if (value == candidate) {
  75. return true;
  76. }
  77. }
  78. return false;
  79. }
  80. /**
  81. * Checks controller and mouse values against their authored menu domains.
  82. * @return True when every number belongs to its supported domain.
  83. */
  84. [[nodiscard]] bool valid_controls(const Controls& value) noexcept {
  85. return contains(kButtonLayouts, value.buttonLayout)
  86. && within(value.movementMode, kMovementModes)
  87. && within(value.controllerLookSensitivity, kControllerSensitivity)
  88. && within(value.mouseLookSensitivity, kMouseSensitivity)
  89. && within(value.adsSensitivityModifier, kAdsSensitivity)
  90. && within(value.doublePressDelay, kDoublePressDelay);
  91. }
  92. /**
  93. * Checks voice and volume values against their authored menu domains.
  94. * @return True when every number belongs to its supported domain.
  95. */
  96. [[nodiscard]] bool valid_audio(const Audio& value) noexcept {
  97. return within(value.voiceOutputMode, kVoiceOutputMode)
  98. && within(value.teamVoiceChannel, kTwoChoiceSelector)
  99. && within(value.reservedMode, kTwoChoiceSelector)
  100. && value.migrationVersion == kCompletedAudioMigrationVersion
  101. && within(value.chatVolume, kChatVolume)
  102. && within(value.soundEffectsVolume, kAudioVolume)
  103. && within(value.dialogueVolume, kAudioVolume) && within(value.musicVolume, kAudioVolume);
  104. }
  105. /**
  106. * Checks renderer values against the supported choices and working calibration values.
  107. * @return True when every number belongs to its supported domain.
  108. */
  109. [[nodiscard]] bool valid_display(const Display& value) noexcept {
  110. return within(value.brightness, kBrightness) && within(value.hdrMode, kTwoChoiceSelector)
  111. && within(value.verticalSyncInterval, kVerticalSyncInterval)
  112. && within(value.fieldOfView, kFieldOfView)
  113. && value.calibrationPrimary == kCalibrationPrimary
  114. && value.calibrationAlpha == kCalibrationAlpha;
  115. }
  116. /**
  117. * Checks HUD and text values against their authored menu domains.
  118. * @return True when every number belongs to its supported domain.
  119. */
  120. [[nodiscard]] bool valid_interface(const Interface& value) noexcept {
  121. return within(value.subtitlesMode, kSubtitlesMode)
  122. && within(value.colorblindMode, kColorblindMode)
  123. && within(value.helmetMode, kTwoChoiceSelector) && within(value.hudOpacity, kHudOpacity)
  124. && within(value.backgroundOpacity, kBackgroundOpacity)
  125. && within(value.reticleLocation, kTwoChoiceSelector)
  126. && within(value.reticleColor, kReticleColor) && within(value.textSize, kTextSize)
  127. && within(value.textColor, kTextPresentationMode)
  128. && within(value.textBackgroundStyle, kTextPresentationMode)
  129. && within(value.textBackgroundOpacity, kBackgroundOpacity)
  130. && value.reservedTextMode == kUnidentifiedTextValue
  131. && value.subtitleOptionsEntry == kUnidentifiedTextValue;
  132. }
  133. /**
  134. * Checks chat values against their authored menu domains.
  135. * @return True when every number belongs to its supported domain.
  136. */
  137. [[nodiscard]] bool valid_social(const Social& value) noexcept {
  138. return within(value.textChatMode, kTextChatMode)
  139. && within(value.whisperChatMode, kTwoChoiceSelector)
  140. && within(value.teamChatJoinMode, kTwoChoiceSelector)
  141. && within(value.localChatJoinMode, kTwoChoiceSelector)
  142. && within(value.clanChatJoinMode, kTwoChoiceSelector)
  143. && within(value.chatAutoHideMode, kTwoChoiceSelector);
  144. }
  145. } // namespace
  146. /** Checks a whole account-settings object against the supported menu domains. */
  147. bool valid(const AccountSettings& value) noexcept {
  148. const bool validBindingSource = value.keyBindingSource == KeyBindingSource::account
  149. || value.keyBindingSource == KeyBindingSource::computer;
  150. return value.configured && value.keyBindings.configured && validBindingSource
  151. && valid_controls(value.controls) && valid_audio(value.audio)
  152. && valid_display(value.display) && valid_interface(value.interface)
  153. && valid_social(value.social);
  154. }
  155. } // namespace sunrise::state::account::settings