#include "settings_state.h" #include #include #include namespace sunrise::state::account::settings { namespace { /** Inclusive domain for one authored setting value. */ template struct SettingsRange { Value minimum; Value maximum; }; /** The controller layout menu publishes these 7 stored values. */ constexpr std::array kButtonLayouts{0, 1, 2, 3, 5, 6, 9}; /** The movement-layout menu stores 4 choices, 0 to 3. */ constexpr SettingsRange kMovementModes{0, 3}; /** Controller sensitivity stores the 10 menu choices as 0 to 9. */ constexpr SettingsRange kControllerSensitivity{0, 9}; /** Mouse sensitivity accepts the menu's 1 to 100 scale. */ constexpr SettingsRange kMouseSensitivity{1, 100}; /** ADS sensitivity stores the menu's 0.5 through 1.5 multiplier. */ constexpr SettingsRange kAdsSensitivity{0.5F, 1.5F}; /** Double-press delay stores the 5 menu choices, 0 to 4. */ constexpr SettingsRange kDoublePressDelay{0, 4}; /** Two-choice selectors store only the menu values 0 and 1. */ constexpr SettingsRange kTwoChoiceSelector{0, 1}; /** Voice output stores 3 menu choices, 0 to 2. */ constexpr SettingsRange kVoiceOutputMode{0, 2}; /** Chat volume exposes 9 menu levels, 0 to 8. */ constexpr SettingsRange kChatVolume{0, 8}; /** Sound, dialogue and music expose 11 levels, 0 to 10. */ constexpr SettingsRange kAudioVolume{0, 10}; /** Brightness stores the 7 menu choices, 0 to 6. */ constexpr SettingsRange kBrightness{0, 6}; /** VSync stores the DXGI presentation interval from 0 to 4. */ constexpr SettingsRange kVerticalSyncInterval{0, 4}; /** Field of view accepts the game's full 55 through 155 degree range. */ constexpr SettingsRange kFieldOfView{55, 155}; /** The first unidentified calibration field uses the working renderer fallback. */ constexpr float kCalibrationPrimary = 10000.0F; /** The second unidentified calibration field uses the working renderer alpha. */ constexpr float kCalibrationAlpha = 0.0F; /** Subtitle mode stores 3 menu choices, 0 to 2. */ constexpr SettingsRange kSubtitlesMode{0, 2}; /** Colorblind mode stores 4 menu choices, 0 to 3. */ constexpr SettingsRange kColorblindMode{0, 3}; /** HUD opacity stores 4 menu choices, 0 to 3. */ constexpr SettingsRange kHudOpacity{0, 3}; /** Background opacity stores 5 menu choices, 0 to 4. */ constexpr SettingsRange kBackgroundOpacity{0, 4}; /** Reticle color stores 7 menu choices, 0 to 6. */ constexpr SettingsRange kReticleColor{0, 6}; /** Text size stores 5 menu choices, 0 to 4. */ constexpr SettingsRange kTextSize{0, 4}; /** Text color and background style each store 4 choices, 0 to 3. */ constexpr SettingsRange kTextPresentationMode{0, 3}; /** Unidentified text settings stay on their only known working value. */ constexpr std::int8_t kUnidentifiedTextValue = 0; /** Text chat stores 4 menu choices, 0 to 3. */ constexpr SettingsRange kTextChatMode{0, 3}; /** * Tests one scalar against an inclusive Sunrise settings policy range. * @param range Inclusive supported range. * @return True when the value is inside the range. */ template [[nodiscard]] bool within(Value value, SettingsRange range) noexcept { return value >= range.minimum && value <= range.maximum; } /** * Tests one scalar against a Sunrise settings policy domain with gaps. * @param values Every supported value. * @return True when the candidate is one of them. */ template [[nodiscard]] bool contains(const std::array& values, Value candidate) noexcept { for (const Value value : values) { if (value == candidate) { return true; } } return false; } /** * Checks controller and mouse values against their authored menu domains. * @return True when every number belongs to its supported domain. */ [[nodiscard]] bool valid_controls(const Controls& value) noexcept { return contains(kButtonLayouts, value.buttonLayout) && within(value.movementMode, kMovementModes) && within(value.controllerLookSensitivity, kControllerSensitivity) && within(value.mouseLookSensitivity, kMouseSensitivity) && within(value.adsSensitivityModifier, kAdsSensitivity) && within(value.doublePressDelay, kDoublePressDelay); } /** * Checks voice and volume values against their authored menu domains. * @return True when every number belongs to its supported domain. */ [[nodiscard]] bool valid_audio(const Audio& value) noexcept { return within(value.voiceOutputMode, kVoiceOutputMode) && within(value.teamVoiceChannel, kTwoChoiceSelector) && within(value.reservedMode, kTwoChoiceSelector) && value.migrationVersion == kCompletedAudioMigrationVersion && within(value.chatVolume, kChatVolume) && within(value.soundEffectsVolume, kAudioVolume) && within(value.dialogueVolume, kAudioVolume) && within(value.musicVolume, kAudioVolume); } /** * Checks renderer values against the supported choices and working calibration values. * @return True when every number belongs to its supported domain. */ [[nodiscard]] bool valid_display(const Display& value) noexcept { return within(value.brightness, kBrightness) && within(value.hdrMode, kTwoChoiceSelector) && within(value.verticalSyncInterval, kVerticalSyncInterval) && within(value.fieldOfView, kFieldOfView) && value.calibrationPrimary == kCalibrationPrimary && value.calibrationAlpha == kCalibrationAlpha; } /** * Checks HUD and text values against their authored menu domains. * @return True when every number belongs to its supported domain. */ [[nodiscard]] bool valid_interface(const Interface& value) noexcept { return within(value.subtitlesMode, kSubtitlesMode) && within(value.colorblindMode, kColorblindMode) && within(value.helmetMode, kTwoChoiceSelector) && within(value.hudOpacity, kHudOpacity) && within(value.backgroundOpacity, kBackgroundOpacity) && within(value.reticleLocation, kTwoChoiceSelector) && within(value.reticleColor, kReticleColor) && within(value.textSize, kTextSize) && within(value.textColor, kTextPresentationMode) && within(value.textBackgroundStyle, kTextPresentationMode) && within(value.textBackgroundOpacity, kBackgroundOpacity) && value.reservedTextMode == kUnidentifiedTextValue && value.subtitleOptionsEntry == kUnidentifiedTextValue; } /** * Checks chat values against their authored menu domains. * @return True when every number belongs to its supported domain. */ [[nodiscard]] bool valid_social(const Social& value) noexcept { return within(value.textChatMode, kTextChatMode) && within(value.whisperChatMode, kTwoChoiceSelector) && within(value.teamChatJoinMode, kTwoChoiceSelector) && within(value.localChatJoinMode, kTwoChoiceSelector) && within(value.clanChatJoinMode, kTwoChoiceSelector) && within(value.chatAutoHideMode, kTwoChoiceSelector); } /** Checks one optional native input code without interpreting an absent binding half. */ [[nodiscard]] bool valid_input_code(const std::optional& value) noexcept { if (!value.has_value()) { return true; } const std::uint16_t input = *value & bindings::kInputCodeMask; const std::uint16_t modifier = *value & bindings::kModifierMask; return input <= bindings::kMaximumBindableInputCode && (modifier == 0 || modifier == bindings::kAltModifierFlag || modifier == bindings::kControlModifierFlag || modifier == bindings::kShiftModifierFlag); } /** Checks every fixed binding row and rejects unsupported or combined modifier bits. */ [[nodiscard]] bool valid_key_bindings(const bindings::KeyBindings& value) noexcept { if (!value.configured) { return false; } for (const bindings::Binding& binding : value.values) { if (!valid_input_code(binding.primary) || !valid_input_code(binding.secondary)) { return false; } } return true; } } // namespace /** Checks a whole account-settings object against the supported menu domains. */ bool valid(const AccountSettings& value) noexcept { const bool validBindingSource = value.keyBindingSource == KeyBindingSource::account || value.keyBindingSource == KeyBindingSource::computer; return value.configured && valid_key_bindings(value.keyBindings) && validBindingSource && valid_controls(value.controls) && valid_audio(value.audio) && valid_display(value.display) && valid_interface(value.interface) && valid_social(value.social); } } // namespace sunrise::state::account::settings