LCOV - code coverage report
Current view: top level - core - Config.h (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 486 486
Test Date: 2026-03-18 19:01:10 Functions: 100.0 % 30 30
Branches: 81.8 % 236 193

             Branch data     Line data    Source code
       1                 :             : #ifndef CONFIG_H
       2                 :             : #define CONFIG_H
       3                 :             : 
       4                 :             : #include "FFBEngine.h"
       5                 :             : #include <string>
       6                 :             : #include <vector>
       7                 :             : #include <chrono>
       8                 :             : #include <map>
       9                 :             : #include <atomic>
      10                 :             : #include "Version.h"
      11                 :             : 
      12                 :             : struct Preset {
      13                 :             :     std::string name;
      14                 :             :     bool is_builtin = false;
      15                 :             :     std::string app_version = LMUFFB_VERSION; // NEW: Track if this is hardcoded or user-created
      16                 :             :     
      17                 :             :     // 1. SINGLE SOURCE OF TRUTH: Default Preset Values
      18                 :             :     // These defaults are used by:
      19                 :             :     // - FFBEngine constructor (via ApplyDefaultsToEngine)
      20                 :             :     // - "Default" preset in LoadPresets()
      21                 :             :     // - "Reset Defaults" button in GUI
      22                 :             :     // - Test presets that don't explicitly set these values
      23                 :             :     //
      24                 :             :     // ⚠️ IMPORTANT: When changing these defaults, you MUST also update:
      25                 :             :     // 1. SetAdvancedBraking() default parameters below (abs_f, lockup_f)
      26                 :             :     // 2. test_ffb_engine.cpp: expected_abs_freq and expected_lockup_freq_scale
      27                 :             :     // 3. Any test presets in Config.cpp that rely on these defaults
      28                 :             :     //
      29                 :             :     // Current defaults match: GT3 DD 15 Nm (Simagic Alpha) - v0.6.35
      30                 :             :    float gain = 1.0f;
      31                 :             :     float understeer = 1.0f;  // New scale: 0.0-2.0, where 1.0 = proportional
      32                 :             :     float understeer_gamma = 1.0f; // NEW: shapes the grip loss curve
      33                 :             :     float sop = 1.666f;
      34                 :             :     float lateral_load = 0.0f; // New v0.7.121
      35                 :             :     int lat_load_transform = 0; // New v0.7.154 (Issue #282)
      36                 :             :     float sop_scale = 1.0f;
      37                 :             :     float sop_smoothing = 0.0f;
      38                 :             :     float slip_smoothing = 0.002f;
      39                 :             :     float min_force = 0.0f;
      40                 :             :     float oversteer_boost = 2.52101f;
      41                 :             :     float long_load_effect = 0.0f; // Renamed from dynamic_weight_gain (#301)
      42                 :             :     float long_load_smoothing = 0.15f; // Renamed from dynamic_weight_smoothing (#301)
      43                 :             :     int long_load_transform = 0; // New #301
      44                 :             : 
      45                 :             :     // FFB Safety (Issue #316)
      46                 :             :     float safety_window_duration = FFBEngine::DEFAULT_SAFETY_WINDOW_DURATION;
      47                 :             :     float safety_gain_reduction = FFBEngine::DEFAULT_SAFETY_GAIN_REDUCTION;
      48                 :             :     float safety_smoothing_tau = FFBEngine::DEFAULT_SAFETY_SMOOTHING_TAU;
      49                 :             :     float spike_detection_threshold = FFBEngine::DEFAULT_SPIKE_DETECTION_THRESHOLD;
      50                 :             :     float immediate_spike_threshold = FFBEngine::DEFAULT_IMMEDIATE_SPIKE_THRESHOLD;
      51                 :             :     float safety_slew_full_scale_time_s = FFBEngine::DEFAULT_SAFETY_SLEW_FULL_SCALE_TIME_S;
      52                 :             :     bool stutter_safety_enabled = FFBEngine::DEFAULT_STUTTER_SAFETY_ENABLED;
      53                 :             :     float stutter_threshold = FFBEngine::DEFAULT_STUTTER_THRESHOLD;
      54                 :             : 
      55                 :             :     float grip_smoothing_steady = 0.05f;    // v0.7.47
      56                 :             :     float grip_smoothing_fast = 0.005f;     // v0.7.47
      57                 :             :     float grip_smoothing_sensitivity = 0.1f; // v0.7.47
      58                 :             :     
      59                 :             :     bool lockup_enabled = true;
      60                 :             :     float lockup_gain = 0.37479f;
      61                 :             :     float lockup_start_pct = 1.0f;  // New v0.5.11
      62                 :             :     float lockup_full_pct = 5.0f;  // New v0.5.11
      63                 :             :     float lockup_rear_boost = 10.0f; // New v0.5.11
      64                 :             :     float lockup_gamma = 0.1f;           // New v0.6.0
      65                 :             :     float lockup_prediction_sens = 10.0f; // New v0.6.0
      66                 :             :     float lockup_bump_reject = 0.1f;     // New v0.6.0
      67                 :             :     float brake_load_cap = 2.0f;    // New v0.5.11
      68                 :             :     float texture_load_cap = 1.5f;  // NEW v0.6.25
      69                 :             :     
      70                 :             :     bool abs_pulse_enabled = false;       // New v0.6.0
      71                 :             :     float abs_gain = 2.0f;               // New v0.6.0
      72                 :             :     float abs_freq = 25.5f;              // New v0.6.20
      73                 :             :     
      74                 :             :     bool spin_enabled = true;
      75                 :             :     float spin_gain = 0.5f;
      76                 :             :     float spin_freq_scale = 1.0f;        // New v0.6.20
      77                 :             :     
      78                 :             :     bool slide_enabled = false;
      79                 :             :     float slide_gain = 0.226562f;
      80                 :             :     float slide_freq = 1.0f;
      81                 :             :     
      82                 :             :     bool road_enabled = true;
      83                 :             :     float road_gain = 0.0f;
      84                 :             :     float vibration_gain = 1.0f; // New v0.7.110 (Issue #206)
      85                 :             : 
      86                 :             :     bool dynamic_normalization_enabled = false;
      87                 :             :     bool auto_load_normalization_enabled = false;
      88                 :             : 
      89                 :             :     bool soft_lock_enabled = true;
      90                 :             :     float soft_lock_stiffness = 20.0f;
      91                 :             :     float soft_lock_damping = 0.5f;
      92                 :             :     
      93                 :             :     float wheelbase_max_nm = 15.0f; // Default DD
      94                 :             :     float target_rim_nm = 10.0f;    // Default target
      95                 :             :     
      96                 :             :     float lockup_freq_scale = 1.02f;      // New v0.6.20
      97                 :             :     int bottoming_method = 0;
      98                 :             :     float scrub_drag_gain = 0.0f;
      99                 :             :     
     100                 :             :     float rear_align_effect = 0.666f;
     101                 :             :     float kerb_strike_rejection = 0.0f; // NEW
     102                 :             :     float sop_yaw_gain = 0.333f;
     103                 :             :     float gyro_gain = 0.0f;
     104                 :             :     
     105                 :             :     float steering_shaft_gain = 1.0f;
     106                 :             :     float ingame_ffb_gain = 1.0f; // New v0.7.71 (Issue #160)
     107                 :             :     int torque_source = 0;   // 0=Shaft, 1=Direct
     108                 :             :     bool torque_passthrough = false; // v0.7.63
     109                 :             :     
     110                 :             :     // NEW: Grip & Smoothing (v0.5.7)
     111                 :             :     float optimal_slip_angle = 0.1f;
     112                 :             :     float optimal_slip_ratio = 0.12f;
     113                 :             :     float steering_shaft_smoothing = 0.0f;
     114                 :             :     
     115                 :             :     // NEW: Advanced Smoothing (v0.5.8)
     116                 :             :     float gyro_smoothing = 0.0f;
     117                 :             :     float yaw_smoothing = 0.001f;
     118                 :             :     float chassis_smoothing = 0.0f;
     119                 :             : 
     120                 :             :     // v0.4.41: Signal Filtering
     121                 :             :     bool flatspot_suppression = false;
     122                 :             :     float notch_q = 2.0f;
     123                 :             :     float flatspot_strength = 1.0f;
     124                 :             :     
     125                 :             :     bool static_notch_enabled = false;
     126                 :             :     float static_notch_freq = 11.0f;
     127                 :             :     float static_notch_width = 2.0f; // New v0.6.10
     128                 :             :     float yaw_kick_threshold = 0.0f; // New v0.6.10
     129                 :             : 
     130                 :             :     // Unloaded Yaw Kick (Braking/Lift-off) - v0.7.164 (Issue #322)
     131                 :             :     float unloaded_yaw_gain = 0.0f;
     132                 :             :     float unloaded_yaw_threshold = 0.2f;
     133                 :             :     float unloaded_yaw_sens = 1.0f;
     134                 :             :     float unloaded_yaw_gamma = 0.5f;
     135                 :             :     float unloaded_yaw_punch = 0.05f;
     136                 :             : 
     137                 :             :     // Power Yaw Kick (Acceleration) - v0.7.164 (Issue #322)
     138                 :             :     float power_yaw_gain = 0.0f;
     139                 :             :     float power_yaw_threshold = 0.2f;
     140                 :             :     float power_slip_threshold = 0.10f;
     141                 :             :     float power_yaw_gamma = 0.5f;
     142                 :             :     float power_yaw_punch = 0.05f;
     143                 :             : 
     144                 :             :     // v0.6.23 New Settings with HIGHER DEFAULTS
     145                 :             :     float speed_gate_lower = 1.0f; // 3.6 km/h
     146                 :             :     float speed_gate_upper = 5.0f; // 18.0 km/h (Fixes idle shake)
     147                 :             :     
     148                 :             :     // Reserved for future implementation (v0.6.23+)
     149                 :             :     float road_fallback_scale = 0.05f;      // Planned: Road texture fallback scaling
     150                 :             :     bool understeer_affects_sop = false;     // Planned: Understeer modulation of SoP
     151                 :             : 
     152                 :             :     // ===== SLOPE DETECTION (v0.7.0 → v0.7.1 defaults) =====
     153                 :             :     bool slope_detection_enabled = false;
     154                 :             :     int slope_sg_window = 15;
     155                 :             :     float slope_sensitivity = 0.5f;          // Reduced from 1.0 (less aggressive)
     156                 :             : 
     157                 :             :     float slope_smoothing_tau = 0.04f;       // Changed from 0.02 (smoother transitions)
     158                 :             : 
     159                 :             :     // v0.7.3: Slope detection stability fixes
     160                 :             :     float slope_alpha_threshold = 0.02f;
     161                 :             :     float slope_decay_rate = 5.0f;
     162                 :             :     bool slope_confidence_enabled = true;
     163                 :             : 
     164                 :             :     // v0.7.11: Min/Max Threshold System
     165                 :             :     float slope_min_threshold = -0.3f;
     166                 :             :     float slope_max_threshold = -2.0f;
     167                 :             : 
     168                 :             :     // NEW v0.7.40: Advanced Slope Settings
     169                 :             :     float slope_g_slew_limit = 50.0f;
     170                 :             :     bool slope_use_torque = true;
     171                 :             :     float slope_torque_sensitivity = 0.5f;
     172                 :             :     float slope_confidence_max_rate = 0.10f;
     173                 :             : 
     174                 :             :     bool rest_api_enabled = false;
     175                 :             :     int rest_api_port = 6397;
     176                 :             : 
     177                 :             :     // 2. Constructors
     178         [ +  - ]:        2049 :     Preset(std::string n, bool builtin = false) : name(n), is_builtin(builtin), app_version(LMUFFB_VERSION) {}
     179   [ +  -  +  - ]:        7055 :     Preset() : name("Unnamed"), is_builtin(false), app_version(LMUFFB_VERSION) {} // Default constructor for file loading
     180                 :             : 
     181                 :             :     // 3. Fluent Setters (The "Python Dictionary" feel)
     182                 :         308 :     Preset& SetGain(float v) { gain = v; return *this; }
     183                 :         448 :     Preset& SetUndersteer(float v) { understeer = v; return *this; }
     184                 :             :     Preset& SetUndersteerGamma(float v) { understeer_gamma = v; return *this; }
     185                 :         448 :     Preset& SetSoP(float v) { sop = v; return *this; }
     186                 :         168 :     Preset& SetSoPScale(float v) { sop_scale = v; return *this; }
     187                 :         448 :     Preset& SetSmoothing(float v) { sop_smoothing = v; return *this; }
     188                 :             :     Preset& SetMinForce(float v) { min_force = v; return *this; }
     189                 :         252 :     Preset& SetOversteer(float v) { oversteer_boost = v; return *this; }
     190                 :             :     Preset& SetLongitudinalLoad(float v) { long_load_effect = v; return *this; }
     191                 :             :     Preset& SetLongitudinalLoadSmoothing(float v) { long_load_smoothing = v; return *this; }
     192                 :             :     Preset& SetLongitudinalLoadTransform(int v) { long_load_transform = v; return *this; }
     193                 :             :     Preset& SetGripSmoothing(float steady, float fast, float sens) {
     194                 :             :         grip_smoothing_steady = steady;
     195                 :             :         grip_smoothing_fast = fast;
     196                 :             :         grip_smoothing_sensitivity = sens;
     197                 :             :         return *this;
     198                 :             :     }
     199                 :         448 :     Preset& SetSlipSmoothing(float v) { slip_smoothing = v; return *this; }
     200                 :             :     
     201                 :         280 :     Preset& SetLockup(bool enabled, float g, float start = 5.0f, float full = 15.0f, float boost = 1.5f) { 
     202                 :         280 :         lockup_enabled = enabled; 
     203                 :         280 :         lockup_gain = g; 
     204                 :         280 :         lockup_start_pct = start;
     205                 :         280 :         lockup_full_pct = full;
     206                 :         280 :         lockup_rear_boost = boost;
     207                 :         280 :         return *this; 
     208                 :             :     }
     209                 :             :     Preset& SetBrakeCap(float v) { brake_load_cap = v; return *this; }
     210                 :         280 :     Preset& SetSpin(bool enabled, float g, float scale = 1.0f) { 
     211                 :         280 :         spin_enabled = enabled; 
     212                 :         280 :         spin_gain = g; 
     213                 :         280 :         spin_freq_scale = scale;
     214                 :         280 :         return *this; 
     215                 :             :     }
     216                 :         448 :     Preset& SetSlide(bool enabled, float g, float f = 1.0f) { 
     217                 :         448 :         slide_enabled = enabled; 
     218                 :         448 :         slide_gain = g; 
     219                 :         448 :         slide_freq = f; 
     220                 :         448 :         return *this; 
     221                 :             :     }
     222                 :         280 :     Preset& SetRoad(bool enabled, float g) { road_enabled = enabled; road_gain = g; return *this; }
     223                 :             :     Preset& SetVibrationGain(float v) { vibration_gain = v; return *this; }
     224                 :             :     Preset& SetDynamicNormalization(bool enabled) { dynamic_normalization_enabled = enabled; return *this; }
     225                 :             : 
     226                 :             :     Preset& SetSoftLock(bool enabled, float stiffness, float damping) {
     227                 :             :         soft_lock_enabled = enabled;
     228                 :             :         soft_lock_stiffness = stiffness;
     229                 :             :         soft_lock_damping = damping;
     230                 :             :         return *this;
     231                 :             :     }
     232                 :             :     
     233                 :             :     Preset& SetHardwareScaling(float wheelbase, float target) {
     234                 :             :         wheelbase_max_nm = wheelbase;
     235                 :             :         target_rim_nm = target;
     236                 :             :         return *this;
     237                 :             :     }
     238                 :             :     
     239                 :             :     Preset& SetBottoming(int method) { bottoming_method = method; return *this; }
     240                 :         252 :     Preset& SetScrub(float v) { scrub_drag_gain = v; return *this; }
     241                 :         448 :     Preset& SetRearAlign(float v) { rear_align_effect = v; return *this; }
     242                 :             :     Preset& SetKerbStrikeRejection(float v) { kerb_strike_rejection = v; return *this; }
     243                 :         258 :     Preset& SetSoPYaw(float v) { sop_yaw_gain = v; return *this; }
     244                 :         168 :     Preset& SetGyro(float v) { gyro_gain = v; return *this; }
     245                 :             :     
     246                 :             :     Preset& SetShaftGain(float v) { steering_shaft_gain = v; return *this; }
     247                 :             :     Preset& SetInGameGain(float v) { ingame_ffb_gain = v; return *this; }
     248                 :             :     Preset& SetTorqueSource(int v, bool passthrough = false) { torque_source = v; torque_passthrough = passthrough; return *this; }
     249                 :             :     Preset& SetFlatspot(bool enabled, float strength = 1.0f, float q = 2.0f) { 
     250                 :             :         flatspot_suppression = enabled; 
     251                 :             :         flatspot_strength = strength;
     252                 :             :         notch_q = q; 
     253                 :             :         return *this; 
     254                 :             :     }
     255                 :             :     
     256                 :             :     Preset& SetStaticNotch(bool enabled, float freq, float width = 2.0f) {
     257                 :             :         static_notch_enabled = enabled;
     258                 :             :         static_notch_freq = freq;
     259                 :             :         static_notch_width = width;
     260                 :             :         return *this;
     261                 :             :     }
     262                 :          29 :     Preset& SetYawKickThreshold(float v) { yaw_kick_threshold = v; return *this; }
     263                 :             : 
     264                 :           2 :     Preset& SetUnloadedYawKick(float gain, float threshold, float sens, float gamma, float punch) {
     265                 :           2 :         unloaded_yaw_gain = gain;
     266                 :           2 :         unloaded_yaw_threshold = threshold;
     267                 :           2 :         unloaded_yaw_sens = sens;
     268                 :           2 :         unloaded_yaw_gamma = gamma;
     269                 :           2 :         unloaded_yaw_punch = punch;
     270                 :           2 :         return *this;
     271                 :             :     }
     272                 :             : 
     273                 :           5 :     Preset& SetPowerYawKick(float gain, float threshold, float slip, float gamma, float punch) {
     274                 :           5 :         power_yaw_gain = gain;
     275                 :           5 :         power_yaw_threshold = threshold;
     276                 :           5 :         power_slip_threshold = slip;
     277                 :           5 :         power_yaw_gamma = gamma;
     278                 :           5 :         power_yaw_punch = punch;
     279                 :           5 :         return *this;
     280                 :             :     }
     281                 :             : 
     282                 :          34 :     Preset& SetSpeedGate(float lower, float upper) { speed_gate_lower = lower; speed_gate_upper = upper; return *this; }
     283                 :             : 
     284                 :          28 :     Preset& SetOptimalSlip(float angle, float ratio) {
     285                 :          28 :         optimal_slip_angle = angle;
     286                 :          28 :         optimal_slip_ratio = ratio;
     287                 :          28 :         return *this;
     288                 :             :     }
     289                 :             :     Preset& SetShaftSmoothing(float v) { steering_shaft_smoothing = v; return *this; }
     290                 :             :     
     291                 :             :     Preset& SetGyroSmoothing(float v) { gyro_smoothing = v; return *this; }
     292                 :          28 :     Preset& SetYawSmoothing(float v) { yaw_smoothing = v; return *this; }
     293                 :             :     Preset& SetChassisSmoothing(float v) { chassis_smoothing = v; return *this; }
     294                 :             :     
     295                 :             :     Preset& SetSlopeDetection(bool enabled, int window = 15, float min_thresh = -0.3f, float max_thresh = -2.0f, float tau = 0.04f) {
     296                 :             :         slope_detection_enabled = enabled;
     297                 :             :         slope_sg_window = window;
     298                 :             :         slope_min_threshold = min_thresh;
     299                 :             :         slope_max_threshold = max_thresh;
     300                 :             :         slope_smoothing_tau = tau;
     301                 :             :         return *this;
     302                 :             :     }
     303                 :             : 
     304                 :             :     Preset& SetSlopeStability(float alpha_thresh = 0.02f, float decay = 5.0f, bool conf = true) {
     305                 :             :         slope_alpha_threshold = alpha_thresh;
     306                 :             :         slope_decay_rate = decay;
     307                 :             :         slope_confidence_enabled = conf;
     308                 :             :         return *this;
     309                 :             :     }
     310                 :             : 
     311                 :             :     Preset& SetSlopeAdvanced(float slew = 50.0f, bool use_torque = true, float torque_sens = 0.5f) {
     312                 :             :         slope_g_slew_limit = slew;
     313                 :             :         slope_use_torque = use_torque;
     314                 :             :         slope_torque_sensitivity = torque_sens;
     315                 :             :         return *this;
     316                 :             :     }
     317                 :             : 
     318                 :             :     Preset& SetRestApiFallback(bool enabled, int port = 6397) {
     319                 :             :         rest_api_enabled = enabled;
     320                 :             :         rest_api_port = port;
     321                 :             :         return *this;
     322                 :             :     }
     323                 :             : 
     324                 :             :     Preset& SetSafety(float duration, float gain, float smoothing, float threshold, float immediate, float slew, bool stutter = false, float stutter_thresh = 1.5f) {
     325                 :             :         safety_window_duration = duration;
     326                 :             :         safety_gain_reduction = gain;
     327                 :             :         safety_smoothing_tau = smoothing;
     328                 :             :         spike_detection_threshold = threshold;
     329                 :             :         immediate_spike_threshold = immediate;
     330                 :             :         safety_slew_full_scale_time_s = slew;
     331                 :             :         stutter_safety_enabled = stutter;
     332                 :             :         stutter_threshold = stutter_thresh;
     333                 :             :         return *this;
     334                 :             :     }
     335                 :             : 
     336                 :             :     // Advanced Braking (v0.6.0)
     337                 :             :     // ⚠️ IMPORTANT: Default parameters (abs_f, lockup_f) must match Config.h defaults!
     338                 :             :     // When changing Config.h defaults, update these values to match.
     339                 :             :     // Current: abs_f=25.5, lockup_f=1.02 (GT3 DD 15 Nm defaults - v0.6.35)
     340                 :          56 :     Preset& SetAdvancedBraking(float gamma, float sens, float bump, bool abs, float abs_g, float abs_f = 25.5f, float lockup_f = 1.02f) {
     341                 :          56 :         lockup_gamma = gamma;
     342                 :          56 :         lockup_prediction_sens = sens;
     343                 :          56 :         lockup_bump_reject = bump;
     344                 :          56 :         abs_pulse_enabled = abs;
     345                 :          56 :         abs_gain = abs_g;
     346                 :          56 :         abs_freq = abs_f;
     347                 :          56 :         lockup_freq_scale = lockup_f;
     348                 :          56 :         return *this;
     349                 :             :     }
     350                 :             : 
     351                 :             :     // 4. Static method to apply defaults to FFBEngine (Single Source of Truth)
     352                 :             :     // This is called by FFBEngine constructor to initialize with T300 defaults
     353                 :         825 :     static void ApplyDefaultsToEngine(FFBEngine& engine) {
     354         [ +  - ]:         825 :         Preset defaults; // Uses default member initializers (T300 values)
     355         [ +  - ]:         825 :         defaults.Apply(engine);
     356                 :         825 :     }
     357                 :             : 
     358                 :             :     // Apply this preset to an engine instance
     359                 :             :     // v0.7.16: Added comprehensive safety clamping to prevent crashes/NaN from invalid config values
     360                 :         854 :     void Apply(FFBEngine& engine) const {
     361                 :         854 :         engine.m_dynamic_normalization_enabled = dynamic_normalization_enabled;
     362                 :         854 :         engine.m_auto_load_normalization_enabled = auto_load_normalization_enabled;
     363                 :         854 :         engine.m_gain = (std::max)(0.0f, gain);
     364                 :         854 :         engine.m_understeer_effect = (std::max)(0.0f, (std::min)(2.0f, understeer));
     365                 :         854 :         engine.m_understeer_gamma = (std::max)(0.1f, (std::min)(4.0f, understeer_gamma));
     366                 :         854 :         engine.m_sop_effect = (std::max)(0.0f, (std::min)(2.0f, sop));
     367                 :         854 :         engine.m_lat_load_effect = (std::max)(0.0f, (std::min)(2.0f, lateral_load));
     368         [ +  - ]:         854 :         engine.m_lat_load_transform = static_cast<LoadTransform>(std::clamp(lat_load_transform, 0, 3));
     369                 :         854 :         engine.m_sop_scale = (std::max)(0.01f, sop_scale);
     370                 :         854 :         engine.m_sop_smoothing_factor = (std::max)(0.0f, (std::min)(1.0f, sop_smoothing));
     371                 :         854 :         engine.m_slip_angle_smoothing = (std::max)(0.0001f, slip_smoothing);
     372                 :         854 :         engine.m_min_force = (std::max)(0.0f, min_force);
     373                 :         854 :         engine.m_oversteer_boost = (std::max)(0.0f, oversteer_boost);
     374                 :         854 :         engine.m_long_load_effect = (std::max)(0.0f, (std::min)(10.0f, long_load_effect));
     375                 :         854 :         engine.m_long_load_smoothing = (std::max)(0.0f, long_load_smoothing);
     376         [ +  - ]:         854 :         engine.m_long_load_transform = static_cast<LoadTransform>(std::clamp(long_load_transform, 0, 3));
     377                 :         854 :         engine.m_grip_smoothing_steady = (std::max)(0.0f, grip_smoothing_steady);
     378                 :         854 :         engine.m_grip_smoothing_fast = (std::max)(0.0f, grip_smoothing_fast);
     379                 :         854 :         engine.m_grip_smoothing_sensitivity = (std::max)(0.001f, grip_smoothing_sensitivity);
     380                 :             : 
     381                 :             :         // FFB Safety (Issue #316)
     382                 :         854 :         engine.m_safety.m_safety_window_duration = (std::max)(0.0f, safety_window_duration);
     383                 :         854 :         engine.m_safety.m_safety_gain_reduction = (std::max)(0.0f, (std::min)(1.0f, safety_gain_reduction));
     384                 :         854 :         engine.m_safety.m_safety_smoothing_tau = (std::max)(0.001f, safety_smoothing_tau);
     385                 :         854 :         engine.m_safety.m_spike_detection_threshold = (std::max)(1.0f, spike_detection_threshold);
     386                 :         854 :         engine.m_safety.m_immediate_spike_threshold = (std::max)(1.0f, immediate_spike_threshold);
     387                 :         854 :         engine.m_safety.m_safety_slew_full_scale_time_s = (std::max)(0.01f, safety_slew_full_scale_time_s);
     388                 :         854 :         engine.m_safety.m_stutter_safety_enabled = stutter_safety_enabled;
     389                 :         854 :         engine.m_safety.m_stutter_threshold = (std::max)(1.01f, stutter_threshold);
     390                 :             : 
     391                 :         854 :         engine.m_lockup_enabled = lockup_enabled;
     392                 :         854 :         engine.m_lockup_gain = (std::max)(0.0f, lockup_gain);
     393                 :         854 :         engine.m_lockup_start_pct = (std::max)(0.1f, lockup_start_pct);
     394                 :         854 :         engine.m_lockup_full_pct = (std::max)(0.2f, lockup_full_pct);
     395                 :         854 :         engine.m_lockup_rear_boost = (std::max)(0.0f, lockup_rear_boost);
     396                 :         854 :         engine.m_lockup_gamma = (std::max)(0.1f, lockup_gamma); // Critical: prevent pow(0, negative) crash
     397                 :         854 :         engine.m_lockup_prediction_sens = (std::max)(1.0f, lockup_prediction_sens);
     398                 :         854 :         engine.m_lockup_bump_reject = (std::max)(0.01f, lockup_bump_reject);
     399                 :         854 :         engine.m_brake_load_cap = (std::max)(1.0f, brake_load_cap);
     400                 :         854 :         engine.m_texture_load_cap = (std::max)(1.0f, texture_load_cap);
     401                 :             : 
     402                 :         854 :         engine.m_abs_pulse_enabled = abs_pulse_enabled;
     403                 :         854 :         engine.m_abs_gain = (std::max)(0.0f, abs_gain);
     404                 :             : 
     405                 :         854 :         engine.m_spin_enabled = spin_enabled;
     406                 :         854 :         engine.m_spin_gain = (std::max)(0.0f, spin_gain);
     407                 :         854 :         engine.m_slide_texture_enabled = slide_enabled;
     408                 :         854 :         engine.m_slide_texture_gain = (std::max)(0.0f, slide_gain);
     409                 :         854 :         engine.m_slide_freq_scale = (std::max)(0.1f, slide_freq);
     410                 :         854 :         engine.m_road_texture_enabled = road_enabled;
     411                 :         854 :         engine.m_road_texture_gain = (std::max)(0.0f, road_gain);
     412                 :         854 :         engine.m_vibration_gain = (std::max)(0.0f, (std::min)(2.0f, vibration_gain));
     413                 :             : 
     414                 :         854 :         engine.m_soft_lock_enabled = soft_lock_enabled;
     415                 :         854 :         engine.m_soft_lock_stiffness = (std::max)(0.0f, soft_lock_stiffness);
     416                 :         854 :         engine.m_soft_lock_damping = (std::max)(0.0f, soft_lock_damping);
     417                 :             : 
     418                 :         854 :         engine.m_wheelbase_max_nm = (std::max)(1.0f, wheelbase_max_nm);
     419                 :         854 :         engine.m_target_rim_nm = (std::max)(1.0f, target_rim_nm);
     420                 :         854 :         engine.m_abs_freq_hz = (std::max)(1.0f, abs_freq);
     421                 :         854 :         engine.m_lockup_freq_scale = (std::max)(0.1f, lockup_freq_scale);
     422                 :         854 :         engine.m_spin_freq_scale = (std::max)(0.1f, spin_freq_scale);
     423                 :         854 :         engine.m_bottoming_method = bottoming_method;
     424                 :         854 :         engine.m_scrub_drag_gain = (std::max)(0.0f, scrub_drag_gain);
     425                 :         854 :         engine.m_rear_align_effect = (std::max)(0.0f, rear_align_effect);
     426                 :         854 :         engine.m_kerb_strike_rejection = (std::max)(0.0f, (std::min)(1.0f, kerb_strike_rejection));
     427                 :         854 :         engine.m_sop_yaw_gain = (std::max)(0.0f, sop_yaw_gain);
     428                 :         854 :         engine.m_gyro_gain = (std::max)(0.0f, gyro_gain);
     429                 :         854 :         engine.m_steering_shaft_gain = (std::max)(0.0f, steering_shaft_gain);
     430                 :         854 :         engine.m_ingame_ffb_gain = (std::max)(0.0f, ingame_ffb_gain);
     431                 :         854 :         engine.m_torque_source = torque_source;
     432                 :         854 :         engine.m_torque_passthrough = torque_passthrough;
     433                 :         854 :         engine.m_flatspot_suppression = flatspot_suppression;
     434                 :         854 :         engine.m_notch_q = (std::max)(0.1f, notch_q); // Critical for biquad division
     435                 :         854 :         engine.m_flatspot_strength = (std::max)(0.0f, (std::min)(1.0f, flatspot_strength));
     436                 :         854 :         engine.m_static_notch_enabled = static_notch_enabled;
     437                 :         854 :         engine.m_static_notch_freq = (std::max)(1.0f, static_notch_freq);
     438                 :         854 :         engine.m_static_notch_width = (std::max)(0.1f, static_notch_width);
     439                 :         854 :         engine.m_yaw_kick_threshold = (std::max)(0.0f, yaw_kick_threshold);
     440                 :             : 
     441                 :             :         // v0.7.164 (Issue #322)
     442                 :         854 :         engine.m_unloaded_yaw_gain = (std::max)(0.0f, unloaded_yaw_gain);
     443                 :         854 :         engine.m_unloaded_yaw_threshold = (std::max)(0.0f, unloaded_yaw_threshold);
     444                 :         854 :         engine.m_unloaded_yaw_sens = (std::max)(0.1f, unloaded_yaw_sens);
     445                 :         854 :         engine.m_unloaded_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, unloaded_yaw_gamma));
     446                 :         854 :         engine.m_unloaded_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, unloaded_yaw_punch));
     447                 :             : 
     448                 :         854 :         engine.m_power_yaw_gain = (std::max)(0.0f, power_yaw_gain);
     449                 :         854 :         engine.m_power_yaw_threshold = (std::max)(0.0f, power_yaw_threshold);
     450                 :         854 :         engine.m_power_slip_threshold = (std::max)(0.01f, (std::min)(1.0f, power_slip_threshold));
     451                 :         854 :         engine.m_power_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, power_yaw_gamma));
     452                 :         854 :         engine.m_power_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, power_yaw_punch));
     453                 :             : 
     454                 :         854 :         engine.m_speed_gate_lower = (std::max)(0.0f, speed_gate_lower);
     455                 :         854 :         engine.m_speed_gate_upper = (std::max)(0.1f, speed_gate_upper);
     456                 :             :         
     457                 :             :         // NEW: Grip & Smoothing (v0.5.7/v0.5.8)
     458                 :         854 :         engine.m_optimal_slip_angle = (std::max)(0.01f, optimal_slip_angle); // Critical for grip division
     459                 :         854 :         engine.m_optimal_slip_ratio = (std::max)(0.01f, optimal_slip_ratio); // Critical for grip division
     460                 :         854 :         engine.m_steering_shaft_smoothing = (std::max)(0.0f, steering_shaft_smoothing);
     461                 :         854 :         engine.m_gyro_smoothing = (std::max)(0.0f, gyro_smoothing);
     462                 :         854 :         engine.m_yaw_accel_smoothing = (std::max)(0.0f, yaw_smoothing);
     463                 :         854 :         engine.m_chassis_inertia_smoothing = (std::max)(0.0f, chassis_smoothing);
     464                 :         854 :         engine.m_road_fallback_scale = (std::max)(0.0f, road_fallback_scale);
     465                 :         854 :         engine.m_understeer_affects_sop = understeer_affects_sop;
     466                 :             :         
     467                 :             :         // Slope Detection (v0.7.0)
     468                 :         854 :         engine.m_slope_detection_enabled = slope_detection_enabled;
     469                 :         854 :         engine.m_slope_sg_window = (std::max)(5, (std::min)(41, slope_sg_window));
     470         [ -  + ]:         854 :         if (engine.m_slope_sg_window % 2 == 0) engine.m_slope_sg_window++; // Must be odd for SG
     471                 :         854 :         engine.m_slope_sensitivity = (std::max)(0.1f, slope_sensitivity);
     472                 :             : 
     473                 :         854 :         engine.m_slope_smoothing_tau = (std::max)(0.001f, slope_smoothing_tau);
     474                 :             : 
     475                 :             :         // v0.7.3: Slope stability fixes
     476                 :         854 :         engine.m_slope_alpha_threshold = (std::max)(0.001f, slope_alpha_threshold); // Critical for slope division
     477                 :         854 :         engine.m_slope_decay_rate = (std::max)(0.1f, slope_decay_rate);
     478                 :         854 :         engine.m_slope_confidence_enabled = slope_confidence_enabled;
     479                 :         854 :         engine.m_slope_confidence_max_rate = (std::max)(engine.m_slope_alpha_threshold + 0.01f, slope_confidence_max_rate);
     480                 :             : 
     481                 :             :         // v0.7.11: Min/Max thresholds
     482                 :         854 :         engine.m_slope_min_threshold = slope_min_threshold;
     483                 :         854 :         engine.m_slope_max_threshold = slope_max_threshold;
     484                 :             : 
     485                 :             :         // NEW v0.7.40: Advanced Slope Settings
     486                 :         854 :         engine.m_slope_g_slew_limit = (std::max)(1.0f, slope_g_slew_limit);
     487                 :         854 :         engine.m_slope_use_torque = slope_use_torque;
     488                 :         854 :         engine.m_slope_torque_sensitivity = (std::max)(0.01f, slope_torque_sensitivity);
     489                 :             : 
     490                 :         854 :         engine.m_rest_api_enabled = rest_api_enabled;
     491                 :         854 :         engine.m_rest_api_port = (std::max)(1, rest_api_port);
     492                 :             : 
     493                 :             :         // Stage 1 & 2 Normalization (Issue #152 & #153)
     494                 :             :         // Initialize session peak from target rim torque to provide a sane starting point.
     495                 :         854 :         engine.m_session_peak_torque = (std::max)(1.0, (double)target_rim_nm);
     496                 :         854 :         engine.m_smoothed_structural_mult = 1.0 / engine.m_session_peak_torque;
     497                 :         854 :     }
     498                 :             : 
     499                 :             :     // NEW: Ensure values are within safe ranges (v0.7.16)
     500                 :          25 :     void Validate() {
     501                 :          25 :         gain = (std::max)(0.0f, gain);
     502                 :          25 :         understeer = (std::max)(0.0f, (std::min)(2.0f, understeer));
     503                 :          25 :         understeer_gamma = (std::max)(0.1f, (std::min)(4.0f, understeer_gamma));
     504                 :          25 :         sop = (std::max)(0.0f, (std::min)(2.0f, sop));
     505                 :          25 :         lateral_load = (std::max)(0.0f, (std::min)(2.0f, lateral_load));
     506         [ +  - ]:          25 :         lat_load_transform = std::clamp(lat_load_transform, 0, 3);
     507                 :          25 :         sop_scale = (std::max)(0.01f, sop_scale);
     508                 :          25 :         sop_smoothing = (std::max)(0.0f, (std::min)(1.0f, sop_smoothing));
     509                 :          25 :         slip_smoothing = (std::max)(0.0001f, slip_smoothing);
     510                 :          25 :         min_force = (std::max)(0.0f, min_force);
     511                 :          25 :         oversteer_boost = (std::max)(0.0f, oversteer_boost);
     512                 :          25 :         long_load_effect = (std::max)(0.0f, (std::min)(10.0f, long_load_effect));
     513                 :          25 :         long_load_smoothing = (std::max)(0.0f, long_load_smoothing);
     514         [ +  - ]:          25 :         long_load_transform = std::clamp(long_load_transform, 0, 3);
     515                 :          25 :         grip_smoothing_steady = (std::max)(0.0f, grip_smoothing_steady);
     516                 :          25 :         grip_smoothing_fast = (std::max)(0.0f, grip_smoothing_fast);
     517                 :          25 :         grip_smoothing_sensitivity = (std::max)(0.001f, grip_smoothing_sensitivity);
     518                 :          25 :         lockup_gain = (std::max)(0.0f, lockup_gain);
     519                 :          25 :         lockup_start_pct = (std::max)(0.1f, lockup_start_pct);
     520                 :          25 :         lockup_full_pct = (std::max)(0.2f, lockup_full_pct);
     521                 :          25 :         lockup_rear_boost = (std::max)(0.0f, lockup_rear_boost);
     522                 :          25 :         lockup_gamma = (std::max)(0.1f, lockup_gamma);
     523                 :          25 :         lockup_prediction_sens = (std::max)(1.0f, lockup_prediction_sens);
     524                 :          25 :         lockup_bump_reject = (std::max)(0.01f, lockup_bump_reject);
     525                 :          25 :         brake_load_cap = (std::max)(1.0f, brake_load_cap);
     526                 :          25 :         texture_load_cap = (std::max)(1.0f, texture_load_cap);
     527                 :          25 :         abs_gain = (std::max)(0.0f, abs_gain);
     528                 :          25 :         spin_gain = (std::max)(0.0f, spin_gain);
     529                 :          25 :         slide_gain = (std::max)(0.0f, slide_gain);
     530                 :          25 :         slide_freq = (std::max)(0.1f, slide_freq);
     531                 :          25 :         road_gain = (std::max)(0.0f, road_gain);
     532                 :          25 :         vibration_gain = (std::max)(0.0f, (std::min)(2.0f, vibration_gain));
     533                 :          25 :         soft_lock_stiffness = (std::max)(0.0f, soft_lock_stiffness);
     534                 :          25 :         soft_lock_damping = (std::max)(0.0f, soft_lock_damping);
     535                 :          25 :         wheelbase_max_nm = (std::max)(1.0f, wheelbase_max_nm);
     536                 :          25 :         target_rim_nm = (std::max)(1.0f, target_rim_nm);
     537                 :          25 :         abs_freq = (std::max)(1.0f, abs_freq);
     538                 :          25 :         lockup_freq_scale = (std::max)(0.1f, lockup_freq_scale);
     539                 :          25 :         spin_freq_scale = (std::max)(0.1f, spin_freq_scale);
     540                 :          25 :         scrub_drag_gain = (std::max)(0.0f, scrub_drag_gain);
     541                 :          25 :         rear_align_effect = (std::max)(0.0f, rear_align_effect);
     542                 :          25 :         kerb_strike_rejection = (std::max)(0.0f, (std::min)(1.0f, kerb_strike_rejection));
     543                 :          25 :         sop_yaw_gain = (std::max)(0.0f, sop_yaw_gain);
     544                 :          25 :         gyro_gain = (std::max)(0.0f, gyro_gain);
     545                 :          25 :         steering_shaft_gain = (std::max)(0.0f, steering_shaft_gain);
     546                 :          25 :         ingame_ffb_gain = (std::max)(0.0f, ingame_ffb_gain);
     547                 :          25 :         torque_source = (std::max)(0, (std::min)(1, torque_source));
     548                 :             :         // torque_passthrough is bool, no clamp needed
     549                 :          25 :         notch_q = (std::max)(0.1f, notch_q);
     550                 :          25 :         flatspot_strength = (std::max)(0.0f, (std::min)(1.0f, flatspot_strength));
     551                 :          25 :         static_notch_freq = (std::max)(1.0f, static_notch_freq);
     552                 :          25 :         static_notch_width = (std::max)(0.1f, static_notch_width);
     553                 :             : 
     554                 :             :         // v0.7.164 (Issue #322)
     555                 :          25 :         unloaded_yaw_gain = (std::max)(0.0f, unloaded_yaw_gain);
     556                 :          25 :         unloaded_yaw_threshold = (std::max)(0.0f, unloaded_yaw_threshold);
     557                 :          25 :         unloaded_yaw_sens = (std::max)(0.1f, unloaded_yaw_sens);
     558                 :          25 :         unloaded_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, unloaded_yaw_gamma));
     559                 :          25 :         unloaded_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, unloaded_yaw_punch));
     560                 :          25 :         power_yaw_gain = (std::max)(0.0f, power_yaw_gain);
     561                 :          25 :         power_yaw_threshold = (std::max)(0.0f, power_yaw_threshold);
     562                 :          25 :         power_slip_threshold = (std::max)(0.01f, (std::min)(1.0f, power_slip_threshold));
     563                 :          25 :         power_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, power_yaw_gamma));
     564                 :          25 :         power_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, power_yaw_punch));
     565                 :             : 
     566                 :          25 :         speed_gate_upper = (std::max)(0.1f, speed_gate_upper);
     567                 :          25 :         optimal_slip_angle = (std::max)(0.01f, optimal_slip_angle);
     568                 :          25 :         optimal_slip_ratio = (std::max)(0.01f, optimal_slip_ratio);
     569                 :          25 :         steering_shaft_smoothing = (std::max)(0.0f, steering_shaft_smoothing);
     570                 :          25 :         gyro_smoothing = (std::max)(0.0f, gyro_smoothing);
     571                 :          25 :         yaw_smoothing = (std::max)(0.0f, yaw_smoothing);
     572                 :          25 :         chassis_smoothing = (std::max)(0.0f, chassis_smoothing);
     573                 :          25 :         road_fallback_scale = (std::max)(0.0f, road_fallback_scale);
     574                 :          25 :         slope_sg_window = (std::max)(5, (std::min)(41, slope_sg_window));
     575         [ -  + ]:          25 :         if (slope_sg_window % 2 == 0) slope_sg_window++;
     576                 :          25 :         slope_sensitivity = (std::max)(0.1f, slope_sensitivity);
     577                 :          25 :         slope_smoothing_tau = (std::max)(0.001f, slope_smoothing_tau);
     578                 :          25 :         slope_alpha_threshold = (std::max)(0.001f, slope_alpha_threshold);
     579                 :          25 :         slope_decay_rate = (std::max)(0.1f, slope_decay_rate);
     580                 :          25 :         slope_g_slew_limit = (std::max)(1.0f, slope_g_slew_limit);
     581                 :          25 :         slope_torque_sensitivity = (std::max)(0.01f, slope_torque_sensitivity);
     582                 :          25 :         slope_confidence_max_rate = (std::max)(slope_alpha_threshold + 0.01f, slope_confidence_max_rate);
     583                 :          25 :         rest_api_port = (std::max)(1, rest_api_port);
     584                 :             : 
     585                 :             :         // FFB Safety (Issue #316)
     586                 :          25 :         safety_window_duration = (std::max)(0.0f, safety_window_duration);
     587                 :          25 :         safety_gain_reduction = (std::max)(0.0f, (std::min)(1.0f, safety_gain_reduction));
     588                 :          25 :         safety_smoothing_tau = (std::max)(0.001f, safety_smoothing_tau);
     589                 :          25 :         spike_detection_threshold = (std::max)(1.0f, spike_detection_threshold);
     590                 :          25 :         immediate_spike_threshold = (std::max)(1.0f, immediate_spike_threshold);
     591                 :          25 :         safety_slew_full_scale_time_s = (std::max)(0.01f, safety_slew_full_scale_time_s);
     592                 :          25 :         stutter_threshold = (std::max)(1.01f, stutter_threshold);
     593                 :          25 :     }
     594                 :             : 
     595                 :             :     // NEW: Capture current engine state into this preset
     596                 :         394 :     void UpdateFromEngine(const FFBEngine& engine) {
     597                 :         394 :         dynamic_normalization_enabled = engine.m_dynamic_normalization_enabled;
     598                 :         394 :         auto_load_normalization_enabled = engine.m_auto_load_normalization_enabled;
     599                 :         394 :         gain = engine.m_gain;
     600                 :         394 :         understeer = engine.m_understeer_effect;
     601                 :         394 :         understeer_gamma = engine.m_understeer_gamma;
     602                 :         394 :         sop = engine.m_sop_effect;
     603                 :         394 :         lateral_load = engine.m_lat_load_effect;
     604                 :         394 :         lat_load_transform = static_cast<int>(engine.m_lat_load_transform);
     605                 :         394 :         sop_scale = engine.m_sop_scale;
     606                 :         394 :         sop_smoothing = engine.m_sop_smoothing_factor;
     607                 :         394 :         slip_smoothing = engine.m_slip_angle_smoothing;
     608                 :         394 :         min_force = engine.m_min_force;
     609                 :         394 :         oversteer_boost = engine.m_oversteer_boost;
     610                 :         394 :         long_load_effect = engine.m_long_load_effect;
     611                 :         394 :         long_load_smoothing = engine.m_long_load_smoothing;
     612                 :         394 :         long_load_transform = static_cast<int>(engine.m_long_load_transform);
     613                 :         394 :         grip_smoothing_steady = engine.m_grip_smoothing_steady;
     614                 :         394 :         grip_smoothing_fast = engine.m_grip_smoothing_fast;
     615                 :         394 :         grip_smoothing_sensitivity = engine.m_grip_smoothing_sensitivity;
     616                 :             : 
     617                 :             :         // FFB Safety (Issue #316)
     618                 :         394 :         safety_window_duration = engine.m_safety.m_safety_window_duration;
     619                 :         394 :         safety_gain_reduction = engine.m_safety.m_safety_gain_reduction;
     620                 :         394 :         safety_smoothing_tau = engine.m_safety.m_safety_smoothing_tau;
     621                 :         394 :         spike_detection_threshold = engine.m_safety.m_spike_detection_threshold;
     622                 :         394 :         immediate_spike_threshold = engine.m_safety.m_immediate_spike_threshold;
     623                 :         394 :         safety_slew_full_scale_time_s = engine.m_safety.m_safety_slew_full_scale_time_s;
     624                 :         394 :         stutter_safety_enabled = engine.m_safety.m_stutter_safety_enabled;
     625                 :         394 :         stutter_threshold = engine.m_safety.m_stutter_threshold;
     626                 :             : 
     627                 :         394 :         lockup_enabled = engine.m_lockup_enabled;
     628                 :         394 :         lockup_gain = engine.m_lockup_gain;
     629                 :         394 :         lockup_start_pct = engine.m_lockup_start_pct;
     630                 :         394 :         lockup_full_pct = engine.m_lockup_full_pct;
     631                 :         394 :         lockup_rear_boost = engine.m_lockup_rear_boost;
     632                 :         394 :         lockup_gamma = engine.m_lockup_gamma;
     633                 :         394 :         lockup_prediction_sens = engine.m_lockup_prediction_sens;
     634                 :         394 :         lockup_bump_reject = engine.m_lockup_bump_reject;
     635                 :         394 :         brake_load_cap = engine.m_brake_load_cap;
     636                 :         394 :         texture_load_cap = engine.m_texture_load_cap;  // NEW v0.6.25
     637                 :         394 :         abs_pulse_enabled = engine.m_abs_pulse_enabled;
     638                 :         394 :         abs_gain = engine.m_abs_gain;
     639                 :             :         
     640                 :         394 :         spin_enabled = engine.m_spin_enabled;
     641                 :         394 :         spin_gain = engine.m_spin_gain;
     642                 :         394 :         slide_enabled = engine.m_slide_texture_enabled;
     643                 :         394 :         slide_gain = engine.m_slide_texture_gain;
     644                 :         394 :         slide_freq = engine.m_slide_freq_scale;
     645                 :         394 :         road_enabled = engine.m_road_texture_enabled;
     646                 :         394 :         road_gain = engine.m_road_texture_gain;
     647                 :         394 :         vibration_gain = engine.m_vibration_gain;
     648                 :             : 
     649                 :         394 :         soft_lock_enabled = engine.m_soft_lock_enabled;
     650                 :         394 :         soft_lock_stiffness = engine.m_soft_lock_stiffness;
     651                 :         394 :         soft_lock_damping = engine.m_soft_lock_damping;
     652                 :             : 
     653                 :         394 :         wheelbase_max_nm = engine.m_wheelbase_max_nm;
     654                 :         394 :         target_rim_nm = engine.m_target_rim_nm;
     655                 :         394 :         abs_freq = engine.m_abs_freq_hz;
     656                 :         394 :         lockup_freq_scale = engine.m_lockup_freq_scale;
     657                 :         394 :         spin_freq_scale = engine.m_spin_freq_scale;
     658                 :         394 :         bottoming_method = engine.m_bottoming_method;
     659                 :         394 :         scrub_drag_gain = engine.m_scrub_drag_gain;
     660                 :         394 :         rear_align_effect = engine.m_rear_align_effect;
     661                 :         394 :         kerb_strike_rejection = engine.m_kerb_strike_rejection;
     662                 :         394 :         sop_yaw_gain = engine.m_sop_yaw_gain;
     663                 :         394 :         gyro_gain = engine.m_gyro_gain;
     664                 :         394 :         steering_shaft_gain = engine.m_steering_shaft_gain;
     665                 :         394 :         ingame_ffb_gain = engine.m_ingame_ffb_gain;
     666                 :         394 :         torque_source = engine.m_torque_source;
     667                 :         394 :         torque_passthrough = engine.m_torque_passthrough;
     668                 :         394 :         flatspot_suppression = engine.m_flatspot_suppression;
     669                 :         394 :         notch_q = engine.m_notch_q;
     670                 :         394 :         flatspot_strength = engine.m_flatspot_strength;
     671                 :         394 :         static_notch_enabled = engine.m_static_notch_enabled;
     672                 :         394 :         static_notch_freq = engine.m_static_notch_freq;
     673                 :         394 :         static_notch_width = engine.m_static_notch_width;
     674                 :         394 :         yaw_kick_threshold = engine.m_yaw_kick_threshold;
     675                 :             : 
     676                 :             :         // v0.7.164 (Issue #322)
     677                 :         394 :         unloaded_yaw_gain = engine.m_unloaded_yaw_gain;
     678                 :         394 :         unloaded_yaw_threshold = engine.m_unloaded_yaw_threshold;
     679                 :         394 :         unloaded_yaw_sens = engine.m_unloaded_yaw_sens;
     680                 :         394 :         unloaded_yaw_gamma = engine.m_unloaded_yaw_gamma;
     681                 :         394 :         unloaded_yaw_punch = engine.m_unloaded_yaw_punch;
     682                 :         394 :         power_yaw_gain = engine.m_power_yaw_gain;
     683                 :         394 :         power_yaw_threshold = engine.m_power_yaw_threshold;
     684                 :         394 :         power_slip_threshold = engine.m_power_slip_threshold;
     685                 :         394 :         power_yaw_gamma = engine.m_power_yaw_gamma;
     686                 :         394 :         power_yaw_punch = engine.m_power_yaw_punch;
     687                 :             : 
     688                 :         394 :         speed_gate_lower = engine.m_speed_gate_lower;
     689                 :         394 :         speed_gate_upper = engine.m_speed_gate_upper;
     690                 :             : 
     691                 :             :         // NEW: Grip & Smoothing (v0.5.7/v0.5.8)
     692                 :         394 :         optimal_slip_angle = engine.m_optimal_slip_angle;
     693                 :         394 :         optimal_slip_ratio = engine.m_optimal_slip_ratio;
     694                 :         394 :         steering_shaft_smoothing = engine.m_steering_shaft_smoothing;
     695                 :         394 :         gyro_smoothing = engine.m_gyro_smoothing;
     696                 :         394 :         yaw_smoothing = engine.m_yaw_accel_smoothing;
     697                 :         394 :         chassis_smoothing = engine.m_chassis_inertia_smoothing;
     698                 :         394 :         road_fallback_scale = engine.m_road_fallback_scale;
     699                 :         394 :         understeer_affects_sop = engine.m_understeer_affects_sop;
     700                 :             : 
     701                 :             :         // Slope Detection (v0.7.0)
     702                 :         394 :         slope_detection_enabled = engine.m_slope_detection_enabled;
     703                 :         394 :         slope_sg_window = engine.m_slope_sg_window;
     704                 :         394 :         slope_sensitivity = engine.m_slope_sensitivity;
     705                 :             : 
     706                 :         394 :         slope_smoothing_tau = engine.m_slope_smoothing_tau;
     707                 :             : 
     708                 :             :         // v0.7.3: Slope stability fixes
     709                 :         394 :         slope_alpha_threshold = engine.m_slope_alpha_threshold;
     710                 :         394 :         slope_decay_rate = engine.m_slope_decay_rate;
     711                 :         394 :         slope_confidence_enabled = engine.m_slope_confidence_enabled;
     712                 :         394 :         slope_confidence_max_rate = engine.m_slope_confidence_max_rate;
     713                 :             : 
     714                 :             :         // v0.7.11: Min/Max thresholds
     715                 :         394 :         slope_min_threshold = engine.m_slope_min_threshold;
     716                 :         394 :         slope_max_threshold = engine.m_slope_max_threshold;
     717                 :             : 
     718                 :             :         // NEW v0.7.40: Advanced Slope Settings
     719                 :         394 :         slope_g_slew_limit = engine.m_slope_g_slew_limit;
     720                 :         394 :         slope_use_torque = engine.m_slope_use_torque;
     721                 :         394 :         slope_torque_sensitivity = engine.m_slope_torque_sensitivity;
     722                 :             : 
     723                 :         394 :         rest_api_enabled = engine.m_rest_api_enabled;
     724                 :         394 :         rest_api_port = engine.m_rest_api_port;
     725                 :             : 
     726                 :         394 :         app_version = LMUFFB_VERSION;
     727                 :         394 :     }
     728                 :             : 
     729                 :         460 :     bool Equals(const Preset& p) const {
     730                 :         460 :         const float eps = 0.0001f;
     731                 :       15772 :         auto is_near = [](float a, float b, float epsilon) { return std::abs(a - b) < epsilon; };
     732                 :             : 
     733         [ +  + ]:         460 :         if (!is_near(gain, p.gain, eps)) return false;
     734         [ +  + ]:         458 :         if (!is_near(understeer, p.understeer, eps)) return false;
     735         [ -  + ]:         457 :         if (!is_near(understeer_gamma, p.understeer_gamma, eps)) return false;
     736         [ +  + ]:         457 :         if (!is_near(sop, p.sop, eps)) return false;
     737         [ -  + ]:         452 :         if (!is_near(lateral_load, p.lateral_load, eps)) return false;
     738         [ -  + ]:         452 :         if (lat_load_transform != p.lat_load_transform) return false;
     739         [ +  + ]:         452 :         if (!is_near(sop_scale, p.sop_scale, eps)) return false;
     740         [ +  + ]:         451 :         if (!is_near(sop_smoothing, p.sop_smoothing, eps)) return false;
     741         [ +  + ]:         450 :         if (!is_near(slip_smoothing, p.slip_smoothing, eps)) return false;
     742         [ +  + ]:         449 :         if (!is_near(min_force, p.min_force, eps)) return false;
     743         [ +  + ]:         448 :         if (!is_near(oversteer_boost, p.oversteer_boost, eps)) return false;
     744         [ +  + ]:         441 :         if (!is_near(long_load_effect, p.long_load_effect, eps)) return false;
     745         [ +  + ]:         440 :         if (!is_near(long_load_smoothing, p.long_load_smoothing, eps)) return false;
     746         [ -  + ]:         439 :         if (long_load_transform != p.long_load_transform) return false;
     747         [ +  + ]:         439 :         if (!is_near(grip_smoothing_steady, p.grip_smoothing_steady, eps)) return false;
     748         [ +  + ]:         438 :         if (!is_near(grip_smoothing_fast, p.grip_smoothing_fast, eps)) return false;
     749         [ +  + ]:         437 :         if (!is_near(grip_smoothing_sensitivity, p.grip_smoothing_sensitivity, eps)) return false;
     750                 :             : 
     751                 :             :         // FFB Safety (Issue #316)
     752         [ -  + ]:         436 :         if (!is_near(safety_window_duration, p.safety_window_duration, eps)) return false;
     753         [ -  + ]:         436 :         if (!is_near(safety_gain_reduction, p.safety_gain_reduction, eps)) return false;
     754         [ -  + ]:         436 :         if (!is_near(safety_smoothing_tau, p.safety_smoothing_tau, eps)) return false;
     755         [ -  + ]:         436 :         if (!is_near(spike_detection_threshold, p.spike_detection_threshold, eps)) return false;
     756         [ -  + ]:         436 :         if (!is_near(immediate_spike_threshold, p.immediate_spike_threshold, eps)) return false;
     757         [ -  + ]:         436 :         if (!is_near(safety_slew_full_scale_time_s, p.safety_slew_full_scale_time_s, eps)) return false;
     758         [ -  + ]:         436 :         if (stutter_safety_enabled != p.stutter_safety_enabled) return false;
     759         [ -  + ]:         436 :         if (!is_near(stutter_threshold, p.stutter_threshold, eps)) return false;
     760                 :             : 
     761         [ +  + ]:         436 :         if (lockup_enabled != p.lockup_enabled) return false;
     762         [ +  + ]:         435 :         if (!is_near(lockup_gain, p.lockup_gain, eps)) return false;
     763         [ +  + ]:         434 :         if (!is_near(lockup_start_pct, p.lockup_start_pct, eps)) return false;
     764         [ +  + ]:         433 :         if (!is_near(lockup_full_pct, p.lockup_full_pct, eps)) return false;
     765         [ +  + ]:         432 :         if (!is_near(lockup_rear_boost, p.lockup_rear_boost, eps)) return false;
     766         [ +  + ]:         431 :         if (!is_near(lockup_gamma, p.lockup_gamma, eps)) return false;
     767         [ +  + ]:         430 :         if (!is_near(lockup_prediction_sens, p.lockup_prediction_sens, eps)) return false;
     768         [ +  + ]:         429 :         if (!is_near(lockup_bump_reject, p.lockup_bump_reject, eps)) return false;
     769         [ +  + ]:         428 :         if (!is_near(brake_load_cap, p.brake_load_cap, eps)) return false;
     770         [ +  + ]:         427 :         if (!is_near(texture_load_cap, p.texture_load_cap, eps)) return false;
     771                 :             : 
     772         [ +  + ]:         426 :         if (abs_pulse_enabled != p.abs_pulse_enabled) return false;
     773         [ +  + ]:          69 :         if (!is_near(abs_gain, p.abs_gain, eps)) return false;
     774         [ +  + ]:          68 :         if (!is_near(abs_freq, p.abs_freq, eps)) return false;
     775                 :             : 
     776         [ +  + ]:          67 :         if (spin_enabled != p.spin_enabled) return false;
     777         [ -  + ]:          66 :         if (!is_near(spin_gain, p.spin_gain, eps)) return false;
     778         [ +  + ]:          66 :         if (!is_near(spin_freq_scale, p.spin_freq_scale, eps)) return false;
     779                 :             : 
     780         [ +  + ]:          65 :         if (slide_enabled != p.slide_enabled) return false;
     781         [ +  + ]:          64 :         if (!is_near(slide_gain, p.slide_gain, eps)) return false;
     782         [ +  + ]:          63 :         if (!is_near(slide_freq, p.slide_freq, eps)) return false;
     783                 :             : 
     784         [ +  + ]:          62 :         if (road_enabled != p.road_enabled) return false;
     785         [ +  + ]:          61 :         if (!is_near(road_gain, p.road_gain, eps)) return false;
     786         [ -  + ]:          60 :         if (!is_near(vibration_gain, p.vibration_gain, eps)) return false;
     787                 :             : 
     788         [ -  + ]:          60 :         if (dynamic_normalization_enabled != p.dynamic_normalization_enabled) return false;
     789         [ -  + ]:          60 :         if (auto_load_normalization_enabled != p.auto_load_normalization_enabled) return false;
     790                 :             : 
     791         [ +  + ]:          60 :         if (soft_lock_enabled != p.soft_lock_enabled) return false;
     792         [ +  + ]:          59 :         if (!is_near(soft_lock_stiffness, p.soft_lock_stiffness, eps)) return false;
     793         [ -  + ]:          58 :         if (!is_near(soft_lock_damping, p.soft_lock_damping, eps)) return false;
     794                 :             : 
     795         [ +  + ]:          58 :         if (!is_near(wheelbase_max_nm, p.wheelbase_max_nm, eps)) return false;
     796         [ +  + ]:          57 :         if (!is_near(target_rim_nm, p.target_rim_nm, eps)) return false;
     797         [ +  + ]:          56 :         if (!is_near(lockup_freq_scale, p.lockup_freq_scale, eps)) return false;
     798         [ +  + ]:          55 :         if (bottoming_method != p.bottoming_method) return false;
     799         [ +  + ]:          54 :         if (!is_near(scrub_drag_gain, p.scrub_drag_gain, eps)) return false;
     800         [ +  + ]:          53 :         if (!is_near(rear_align_effect, p.rear_align_effect, eps)) return false;
     801         [ -  + ]:          52 :         if (!is_near(kerb_strike_rejection, p.kerb_strike_rejection, eps)) return false;
     802         [ +  + ]:          52 :         if (!is_near(sop_yaw_gain, p.sop_yaw_gain, eps)) return false;
     803         [ +  + ]:          51 :         if (!is_near(gyro_gain, p.gyro_gain, eps)) return false;
     804         [ +  + ]:          50 :         if (!is_near(steering_shaft_gain, p.steering_shaft_gain, eps)) return false;
     805         [ +  + ]:          49 :         if (!is_near(ingame_ffb_gain, p.ingame_ffb_gain, eps)) return false;
     806         [ +  + ]:          48 :         if (torque_source != p.torque_source) return false;
     807         [ +  + ]:          43 :         if (torque_passthrough != p.torque_passthrough) return false;
     808                 :             : 
     809         [ +  + ]:          42 :         if (!is_near(optimal_slip_angle, p.optimal_slip_angle, eps)) return false;
     810         [ +  + ]:          41 :         if (!is_near(optimal_slip_ratio, p.optimal_slip_ratio, eps)) return false;
     811         [ +  + ]:          40 :         if (!is_near(steering_shaft_smoothing, p.steering_shaft_smoothing, eps)) return false;
     812         [ +  + ]:          39 :         if (!is_near(gyro_smoothing, p.gyro_smoothing, eps)) return false;
     813         [ +  + ]:          38 :         if (!is_near(yaw_smoothing, p.yaw_smoothing, eps)) return false;
     814         [ +  + ]:          37 :         if (!is_near(chassis_smoothing, p.chassis_smoothing, eps)) return false;
     815                 :             : 
     816         [ +  + ]:          36 :         if (flatspot_suppression != p.flatspot_suppression) return false;
     817         [ +  + ]:          35 :         if (!is_near(notch_q, p.notch_q, eps)) return false;
     818         [ +  + ]:          34 :         if (!is_near(flatspot_strength, p.flatspot_strength, eps)) return false;
     819                 :             : 
     820         [ +  + ]:          33 :         if (static_notch_enabled != p.static_notch_enabled) return false;
     821         [ +  + ]:          32 :         if (!is_near(static_notch_freq, p.static_notch_freq, eps)) return false;
     822         [ +  + ]:          31 :         if (!is_near(static_notch_width, p.static_notch_width, eps)) return false;
     823         [ +  + ]:          30 :         if (!is_near(yaw_kick_threshold, p.yaw_kick_threshold, eps)) return false;
     824                 :             : 
     825                 :             :         // v0.7.164 (Issue #322)
     826         [ -  + ]:          29 :         if (!is_near(unloaded_yaw_gain, p.unloaded_yaw_gain, eps)) return false;
     827         [ -  + ]:          29 :         if (!is_near(unloaded_yaw_threshold, p.unloaded_yaw_threshold, eps)) return false;
     828         [ -  + ]:          29 :         if (!is_near(unloaded_yaw_sens, p.unloaded_yaw_sens, eps)) return false;
     829         [ -  + ]:          29 :         if (!is_near(unloaded_yaw_gamma, p.unloaded_yaw_gamma, eps)) return false;
     830         [ -  + ]:          29 :         if (!is_near(unloaded_yaw_punch, p.unloaded_yaw_punch, eps)) return false;
     831         [ -  + ]:          29 :         if (!is_near(power_yaw_gain, p.power_yaw_gain, eps)) return false;
     832         [ -  + ]:          29 :         if (!is_near(power_yaw_threshold, p.power_yaw_threshold, eps)) return false;
     833         [ -  + ]:          29 :         if (!is_near(power_slip_threshold, p.power_slip_threshold, eps)) return false;
     834         [ -  + ]:          29 :         if (!is_near(power_yaw_gamma, p.power_yaw_gamma, eps)) return false;
     835         [ -  + ]:          29 :         if (!is_near(power_yaw_punch, p.power_yaw_punch, eps)) return false;
     836                 :             : 
     837         [ +  + ]:          29 :         if (!is_near(speed_gate_lower, p.speed_gate_lower, eps)) return false;
     838         [ +  + ]:          28 :         if (!is_near(speed_gate_upper, p.speed_gate_upper, eps)) return false;
     839                 :             : 
     840         [ +  + ]:          27 :         if (!is_near(road_fallback_scale, p.road_fallback_scale, eps)) return false;
     841         [ +  + ]:          26 :         if (understeer_affects_sop != p.understeer_affects_sop) return false;
     842                 :             : 
     843         [ +  + ]:          25 :         if (slope_detection_enabled != p.slope_detection_enabled) return false;
     844         [ +  + ]:          24 :         if (slope_sg_window != p.slope_sg_window) return false;
     845         [ -  + ]:          23 :         if (!is_near(slope_sensitivity, p.slope_sensitivity, eps)) return false;
     846                 :             : 
     847         [ +  + ]:          23 :         if (!is_near(slope_smoothing_tau, p.slope_smoothing_tau, eps)) return false;
     848         [ +  + ]:          22 :         if (!is_near(slope_alpha_threshold, p.slope_alpha_threshold, eps)) return false;
     849         [ +  + ]:          21 :         if (!is_near(slope_decay_rate, p.slope_decay_rate, eps)) return false;
     850         [ +  + ]:          20 :         if (slope_confidence_enabled != p.slope_confidence_enabled) return false;
     851         [ +  + ]:          19 :         if (!is_near(slope_min_threshold, p.slope_min_threshold, eps)) return false;
     852         [ +  + ]:          18 :         if (!is_near(slope_max_threshold, p.slope_max_threshold, eps)) return false;
     853                 :             : 
     854         [ +  + ]:          17 :         if (!is_near(slope_g_slew_limit, p.slope_g_slew_limit, eps)) return false;
     855         [ +  + ]:          16 :         if (slope_use_torque != p.slope_use_torque) return false;
     856         [ -  + ]:          15 :         if (!is_near(slope_torque_sensitivity, p.slope_torque_sensitivity, eps)) return false;
     857         [ +  + ]:          15 :         if (!is_near(slope_confidence_max_rate, p.slope_confidence_max_rate, eps)) return false;
     858                 :             : 
     859         [ -  + ]:          14 :         if (rest_api_enabled != p.rest_api_enabled) return false;
     860         [ -  + ]:          14 :         if (rest_api_port != p.rest_api_port) return false;
     861                 :             : 
     862                 :          14 :         return true;
     863                 :             :     }
     864                 :             : };
     865                 :             : 
     866                 :             : class Config {
     867                 :             : public:
     868                 :             :     static std::string m_config_path; // Default: "config.ini"
     869                 :             :     static void Save(const FFBEngine& engine, const std::string& filename = "");
     870                 :             :     static void Load(FFBEngine& engine, const std::string& filename = "");
     871                 :             :     
     872                 :             :     // Preset Management
     873                 :             :     static std::vector<Preset> presets;
     874                 :             :     static std::string m_last_preset_name; // NEW (v0.7.14)
     875                 :             :     static void LoadPresets(); // Populates presets vector
     876                 :             :     static void ApplyPreset(int index, FFBEngine& engine);
     877                 :             :     
     878                 :             :     // NEW: Add a user preset
     879                 :             :     static void AddUserPreset(const std::string& name, const FFBEngine& engine);
     880                 :             : 
     881                 :             :     // NEW: Delete and Duplicate (v0.7.14)
     882                 :             :     static void DeletePreset(int index, const FFBEngine& engine);
     883                 :             :     static void DuplicatePreset(int index, const FFBEngine& engine);
     884                 :             :     static bool IsEngineDirtyRelativeToPreset(int index, const FFBEngine& engine);
     885                 :             : 
     886                 :             :     // NEW: Import/Export (v0.7.12)
     887                 :             :     static void ExportPreset(int index, const std::string& filename);
     888                 :             :     static bool ImportPreset(const std::string& filename, const FFBEngine& engine);
     889                 :             : 
     890                 :             :     // NEW: Persist selected device
     891                 :             :     static std::string m_last_device_guid;
     892                 :             : 
     893                 :             :     // Global App Settings (not part of FFB Physics)
     894                 :             :     static bool m_always_on_top;      // NEW: Keep window on top
     895                 :             :     static bool m_auto_start_logging; // NEW: Auto-start logging
     896                 :             :     static std::string m_log_path;    // NEW: Path to save logs
     897                 :             : 
     898                 :             :     // Window Geometry Persistence (v0.5.5)
     899                 :             :     static int win_pos_x, win_pos_y;
     900                 :             :     static int win_w_small, win_h_small; // Dimensions for Config Only
     901                 :             :     static int win_w_large, win_h_large; // Dimensions for Config + Graphs
     902                 :             :     static bool show_graphs;             // Remember if graphs were open
     903                 :             : 
     904                 :             :     // Persistent storage for vehicle static loads (v0.7.70)
     905                 :             :     static std::map<std::string, double> m_saved_static_loads;
     906                 :             :     static std::recursive_mutex m_static_loads_mutex;
     907                 :             : 
     908                 :             :     // Flag to request a save from the main thread (avoids File I/O on FFB thread)
     909                 :             :     static std::atomic<bool> m_needs_save;
     910                 :             : 
     911                 :             :     // Thread-safe access to static loads map (v0.7.70)
     912                 :             :     static void SetSavedStaticLoad(const std::string& vehicleName, double value);
     913                 :             :     static bool GetSavedStaticLoad(const std::string& vehicleName, double& value);
     914                 :             : 
     915                 :             : private:
     916                 :             :     // Helper for parsing preset lines (v0.7.12)
     917                 :             :     static void ParsePresetLine(const std::string& line, Preset& p, std::string& version, bool& needs_save, bool& legacy_torque_hack, float& legacy_torque_val);
     918                 :             :     static bool ParseSystemLine(const std::string& key, const std::string& value, Preset& p, std::string& version, bool& needs_save, bool& legacy_torque_hack, float& legacy_torque_val);
     919                 :             :     static bool ParsePhysicsLine(const std::string& key, const std::string& value, Preset& p);
     920                 :             :     static bool ParseBrakingLine(const std::string& key, const std::string& value, Preset& p);
     921                 :             :     static bool ParseVibrationLine(const std::string& key, const std::string& value, Preset& p);
     922                 :             :     static bool ParseSafetyLine(const std::string& key, const std::string& value, Preset& p);
     923                 :             : 
     924                 :             :     static bool SyncSystemLine(const std::string& key, const std::string& value, FFBEngine& engine, std::string& version, bool& legacy_torque_hack, float& legacy_torque_val, bool& needs_save);
     925                 :             :     static bool SyncPhysicsLine(const std::string& key, const std::string& value, FFBEngine& engine, std::string& version, bool& needs_save);
     926                 :             :     static bool SyncBrakingLine(const std::string& key, const std::string& value, FFBEngine& engine);
     927                 :             :     static bool SyncVibrationLine(const std::string& key, const std::string& value, FFBEngine& engine);
     928                 :             :     static bool SyncSafetyLine(const std::string& key, const std::string& value, FFBEngine& engine);
     929                 :             : 
     930                 :             :     // Helper for writing preset fields (v0.7.12)
     931                 :             :     static void WritePresetFields(std::ofstream& file, const Preset& p);
     932                 :             : };
     933                 :             : 
     934                 :             : 
     935                 :             : 
     936                 :             : 
     937                 :             : #endif
     938                 :             : 
     939                 :             : 
     940                 :             : 
     941                 :             : 
     942                 :             : 
        

Generated by: LCOV version 2.0-1