LCOV - code coverage report
Current view: top level - src - Config.h (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 374 374
Test Date: 2026-03-01 21:30:38 Functions: 100.0 % 28 28
Branches: 92.1 % 178 164

             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 sop = 1.666f;
      33                 :             :     float sop_scale = 1.0f;
      34                 :             :     float sop_smoothing = 1.0f;
      35                 :             :     float slip_smoothing = 0.002f;
      36                 :             :     float min_force = 0.0f;
      37                 :             :     float oversteer_boost = 2.52101f;
      38                 :             :     float dynamic_weight_gain = 0.0f; // NEW v0.7.46
      39                 :             :     float dynamic_weight_smoothing = 0.15f; // v0.7.47
      40                 :             :     float grip_smoothing_steady = 0.05f;    // v0.7.47
      41                 :             :     float grip_smoothing_fast = 0.005f;     // v0.7.47
      42                 :             :     float grip_smoothing_sensitivity = 0.1f; // v0.7.47
      43                 :             :     
      44                 :             :     bool lockup_enabled = true;
      45                 :             :     float lockup_gain = 0.37479f;
      46                 :             :     float lockup_start_pct = 1.0f;  // New v0.5.11
      47                 :             :     float lockup_full_pct = 5.0f;  // New v0.5.11
      48                 :             :     float lockup_rear_boost = 10.0f; // New v0.5.11
      49                 :             :     float lockup_gamma = 0.1f;           // New v0.6.0
      50                 :             :     float lockup_prediction_sens = 10.0f; // New v0.6.0
      51                 :             :     float lockup_bump_reject = 0.1f;     // New v0.6.0
      52                 :             :     float brake_load_cap = 2.0f;    // New v0.5.11
      53                 :             :     float texture_load_cap = 1.5f;  // NEW v0.6.25
      54                 :             :     
      55                 :             :     bool abs_pulse_enabled = false;       // New v0.6.0
      56                 :             :     float abs_gain = 2.0f;               // New v0.6.0
      57                 :             :     float abs_freq = 25.5f;              // New v0.6.20
      58                 :             :     
      59                 :             :     bool spin_enabled = true;
      60                 :             :     float spin_gain = 0.5f;
      61                 :             :     float spin_freq_scale = 1.0f;        // New v0.6.20
      62                 :             :     
      63                 :             :     bool slide_enabled = false;
      64                 :             :     float slide_gain = 0.226562f;
      65                 :             :     float slide_freq = 1.0f;
      66                 :             :     
      67                 :             :     bool road_enabled = true;
      68                 :             :     float road_gain = 0.0f;
      69                 :             :     float tactile_gain = 1.0f; // New v0.7.110 (Issue #206)
      70                 :             : 
      71                 :             :     bool dynamic_normalization_enabled = false;
      72                 :             :     bool auto_load_normalization_enabled = false;
      73                 :             : 
      74                 :             :     bool soft_lock_enabled = true;
      75                 :             :     float soft_lock_stiffness = 20.0f;
      76                 :             :     float soft_lock_damping = 0.5f;
      77                 :             :     
      78                 :             :     float wheelbase_max_nm = 15.0f; // Default DD
      79                 :             :     float target_rim_nm = 10.0f;    // Default target
      80                 :             :     
      81                 :             :     float lockup_freq_scale = 1.02f;      // New v0.6.20
      82                 :             :     int bottoming_method = 0;
      83                 :             :     float scrub_drag_gain = 0.0f;
      84                 :             :     
      85                 :             :     float rear_align_effect = 0.666f;
      86                 :             :     float sop_yaw_gain = 0.333f;
      87                 :             :     float gyro_gain = 0.0f;
      88                 :             :     
      89                 :             :     float steering_shaft_gain = 1.0f;
      90                 :             :     float ingame_ffb_gain = 1.0f; // New v0.7.71 (Issue #160)
      91                 :             :     int torque_source = 0;   // 0=Shaft, 1=Direct
      92                 :             :     bool torque_passthrough = false; // v0.7.63
      93                 :             :     
      94                 :             :     // NEW: Grip & Smoothing (v0.5.7)
      95                 :             :     float optimal_slip_angle = 0.1f;
      96                 :             :     float optimal_slip_ratio = 0.12f;
      97                 :             :     float steering_shaft_smoothing = 0.0f;
      98                 :             :     
      99                 :             :     // NEW: Advanced Smoothing (v0.5.8)
     100                 :             :     float gyro_smoothing = 0.0f;
     101                 :             :     float yaw_smoothing = 0.001f;
     102                 :             :     float chassis_smoothing = 0.0f;
     103                 :             : 
     104                 :             :     // v0.4.41: Signal Filtering
     105                 :             :     bool flatspot_suppression = false;
     106                 :             :     float notch_q = 2.0f;
     107                 :             :     float flatspot_strength = 1.0f;
     108                 :             :     
     109                 :             :     bool static_notch_enabled = false;
     110                 :             :     float static_notch_freq = 11.0f;
     111                 :             :     float static_notch_width = 2.0f; // New v0.6.10
     112                 :             :     float yaw_kick_threshold = 0.0f; // New v0.6.10
     113                 :             : 
     114                 :             :     // v0.6.23 New Settings with HIGHER DEFAULTS
     115                 :             :     float speed_gate_lower = 1.0f; // 3.6 km/h
     116                 :             :     float speed_gate_upper = 5.0f; // 18.0 km/h (Fixes idle shake)
     117                 :             :     
     118                 :             :     // Reserved for future implementation (v0.6.23+)
     119                 :             :     float road_fallback_scale = 0.05f;      // Planned: Road texture fallback scaling
     120                 :             :     bool understeer_affects_sop = false;     // Planned: Understeer modulation of SoP
     121                 :             : 
     122                 :             :     // ===== SLOPE DETECTION (v0.7.0 → v0.7.1 defaults) =====
     123                 :             :     bool slope_detection_enabled = false;
     124                 :             :     int slope_sg_window = 15;
     125                 :             :     float slope_sensitivity = 0.5f;          // Reduced from 1.0 (less aggressive)
     126                 :             : 
     127                 :             :     float slope_smoothing_tau = 0.04f;       // Changed from 0.02 (smoother transitions)
     128                 :             : 
     129                 :             :     // v0.7.3: Slope detection stability fixes
     130                 :             :     float slope_alpha_threshold = 0.02f;
     131                 :             :     float slope_decay_rate = 5.0f;
     132                 :             :     bool slope_confidence_enabled = true;
     133                 :             : 
     134                 :             :     // v0.7.11: Min/Max Threshold System
     135                 :             :     float slope_min_threshold = -0.3f;
     136                 :             :     float slope_max_threshold = -2.0f;
     137                 :             : 
     138                 :             :     // NEW v0.7.40: Advanced Slope Settings
     139                 :             :     float slope_g_slew_limit = 50.0f;
     140                 :             :     bool slope_use_torque = true;
     141                 :             :     float slope_torque_sensitivity = 0.5f;
     142                 :             :     float slope_confidence_max_rate = 0.10f;
     143                 :             : 
     144                 :             :     // 2. Constructors
     145         [ +  - ]:        1818 :     Preset(std::string n, bool builtin = false) : name(n), is_builtin(builtin), app_version(LMUFFB_VERSION) {}
     146   [ +  -  +  - ]:        5780 :     Preset() : name("Unnamed"), is_builtin(false), app_version(LMUFFB_VERSION) {} // Default constructor for file loading
     147                 :             : 
     148                 :             :     // 3. Fluent Setters (The "Python Dictionary" feel)
     149                 :         286 :     Preset& SetGain(float v) { gain = v; return *this; }
     150                 :         416 :     Preset& SetUndersteer(float v) { understeer = v; return *this; }
     151                 :         416 :     Preset& SetSoP(float v) { sop = v; return *this; }
     152                 :         156 :     Preset& SetSoPScale(float v) { sop_scale = v; return *this; }
     153                 :         416 :     Preset& SetSmoothing(float v) { sop_smoothing = v; return *this; }
     154                 :             :     Preset& SetMinForce(float v) { min_force = v; return *this; }
     155                 :         234 :     Preset& SetOversteer(float v) { oversteer_boost = v; return *this; }
     156                 :             :     Preset& SetDynamicWeight(float v) { dynamic_weight_gain = v; return *this; }
     157                 :             :     Preset& SetDynamicWeightSmoothing(float v) { dynamic_weight_smoothing = v; return *this; }
     158                 :             :     Preset& SetGripSmoothing(float steady, float fast, float sens) {
     159                 :             :         grip_smoothing_steady = steady;
     160                 :             :         grip_smoothing_fast = fast;
     161                 :             :         grip_smoothing_sensitivity = sens;
     162                 :             :         return *this;
     163                 :             :     }
     164                 :         416 :     Preset& SetSlipSmoothing(float v) { slip_smoothing = v; return *this; }
     165                 :             :     
     166                 :         260 :     Preset& SetLockup(bool enabled, float g, float start = 5.0f, float full = 15.0f, float boost = 1.5f) { 
     167                 :         260 :         lockup_enabled = enabled; 
     168                 :         260 :         lockup_gain = g; 
     169                 :         260 :         lockup_start_pct = start;
     170                 :         260 :         lockup_full_pct = full;
     171                 :         260 :         lockup_rear_boost = boost;
     172                 :         260 :         return *this; 
     173                 :             :     }
     174                 :             :     Preset& SetBrakeCap(float v) { brake_load_cap = v; return *this; }
     175                 :         260 :     Preset& SetSpin(bool enabled, float g, float scale = 1.0f) { 
     176                 :         260 :         spin_enabled = enabled; 
     177                 :         260 :         spin_gain = g; 
     178                 :         260 :         spin_freq_scale = scale;
     179                 :         260 :         return *this; 
     180                 :             :     }
     181                 :         416 :     Preset& SetSlide(bool enabled, float g, float f = 1.0f) { 
     182                 :         416 :         slide_enabled = enabled; 
     183                 :         416 :         slide_gain = g; 
     184                 :         416 :         slide_freq = f; 
     185                 :         416 :         return *this; 
     186                 :             :     }
     187                 :         260 :     Preset& SetRoad(bool enabled, float g) { road_enabled = enabled; road_gain = g; return *this; }
     188                 :             :     Preset& SetTactileGain(float v) { tactile_gain = v; return *this; }
     189                 :             :     Preset& SetDynamicNormalization(bool enabled) { dynamic_normalization_enabled = enabled; return *this; }
     190                 :             : 
     191                 :             :     Preset& SetSoftLock(bool enabled, float stiffness, float damping) {
     192                 :             :         soft_lock_enabled = enabled;
     193                 :             :         soft_lock_stiffness = stiffness;
     194                 :             :         soft_lock_damping = damping;
     195                 :             :         return *this;
     196                 :             :     }
     197                 :             :     
     198                 :             :     Preset& SetHardwareScaling(float wheelbase, float target) {
     199                 :             :         wheelbase_max_nm = wheelbase;
     200                 :             :         target_rim_nm = target;
     201                 :             :         return *this;
     202                 :             :     }
     203                 :             :     
     204                 :             :     Preset& SetBottoming(int method) { bottoming_method = method; return *this; }
     205                 :         234 :     Preset& SetScrub(float v) { scrub_drag_gain = v; return *this; }
     206                 :         416 :     Preset& SetRearAlign(float v) { rear_align_effect = v; return *this; }
     207                 :         234 :     Preset& SetSoPYaw(float v) { sop_yaw_gain = v; return *this; }
     208                 :         156 :     Preset& SetGyro(float v) { gyro_gain = v; return *this; }
     209                 :             :     
     210                 :             :     Preset& SetShaftGain(float v) { steering_shaft_gain = v; return *this; }
     211                 :             :     Preset& SetInGameGain(float v) { ingame_ffb_gain = v; return *this; }
     212                 :             :     Preset& SetTorqueSource(int v, bool passthrough = false) { torque_source = v; torque_passthrough = passthrough; return *this; }
     213                 :             :     Preset& SetFlatspot(bool enabled, float strength = 1.0f, float q = 2.0f) { 
     214                 :             :         flatspot_suppression = enabled; 
     215                 :             :         flatspot_strength = strength;
     216                 :             :         notch_q = q; 
     217                 :             :         return *this; 
     218                 :             :     }
     219                 :             :     
     220                 :             :     Preset& SetStaticNotch(bool enabled, float freq, float width = 2.0f) {
     221                 :             :         static_notch_enabled = enabled;
     222                 :             :         static_notch_freq = freq;
     223                 :             :         static_notch_width = width;
     224                 :             :         return *this;
     225                 :             :     }
     226                 :          26 :     Preset& SetYawKickThreshold(float v) { yaw_kick_threshold = v; return *this; }
     227                 :          26 :     Preset& SetSpeedGate(float lower, float upper) { speed_gate_lower = lower; speed_gate_upper = upper; return *this; }
     228                 :             : 
     229                 :          26 :     Preset& SetOptimalSlip(float angle, float ratio) {
     230                 :          26 :         optimal_slip_angle = angle;
     231                 :          26 :         optimal_slip_ratio = ratio;
     232                 :          26 :         return *this;
     233                 :             :     }
     234                 :             :     Preset& SetShaftSmoothing(float v) { steering_shaft_smoothing = v; return *this; }
     235                 :             :     
     236                 :             :     Preset& SetGyroSmoothing(float v) { gyro_smoothing = v; return *this; }
     237                 :          26 :     Preset& SetYawSmoothing(float v) { yaw_smoothing = v; return *this; }
     238                 :             :     Preset& SetChassisSmoothing(float v) { chassis_smoothing = v; return *this; }
     239                 :             :     
     240                 :             :     Preset& SetSlopeDetection(bool enabled, int window = 15, float min_thresh = -0.3f, float max_thresh = -2.0f, float tau = 0.04f) {
     241                 :             :         slope_detection_enabled = enabled;
     242                 :             :         slope_sg_window = window;
     243                 :             :         slope_min_threshold = min_thresh;
     244                 :             :         slope_max_threshold = max_thresh;
     245                 :             :         slope_smoothing_tau = tau;
     246                 :             :         return *this;
     247                 :             :     }
     248                 :             : 
     249                 :             :     Preset& SetSlopeStability(float alpha_thresh = 0.02f, float decay = 5.0f, bool conf = true) {
     250                 :             :         slope_alpha_threshold = alpha_thresh;
     251                 :             :         slope_decay_rate = decay;
     252                 :             :         slope_confidence_enabled = conf;
     253                 :             :         return *this;
     254                 :             :     }
     255                 :             : 
     256                 :             :     Preset& SetSlopeAdvanced(float slew = 50.0f, bool use_torque = true, float torque_sens = 0.5f) {
     257                 :             :         slope_g_slew_limit = slew;
     258                 :             :         slope_use_torque = use_torque;
     259                 :             :         slope_torque_sensitivity = torque_sens;
     260                 :             :         return *this;
     261                 :             :     }
     262                 :             : 
     263                 :             :     // Advanced Braking (v0.6.0)
     264                 :             :     // ⚠️ IMPORTANT: Default parameters (abs_f, lockup_f) must match Config.h defaults!
     265                 :             :     // When changing Config.h defaults, update these values to match.
     266                 :             :     // Current: abs_f=25.5, lockup_f=1.02 (GT3 DD 15 Nm defaults - v0.6.35)
     267                 :          52 :     Preset& SetAdvancedBraking(float gamma, float sens, float bump, bool abs, float abs_g, float abs_f = 25.5f, float lockup_f = 1.02f) {
     268                 :          52 :         lockup_gamma = gamma;
     269                 :          52 :         lockup_prediction_sens = sens;
     270                 :          52 :         lockup_bump_reject = bump;
     271                 :          52 :         abs_pulse_enabled = abs;
     272                 :          52 :         abs_gain = abs_g;
     273                 :          52 :         abs_freq = abs_f;
     274                 :          52 :         lockup_freq_scale = lockup_f;
     275                 :          52 :         return *this;
     276                 :             :     }
     277                 :             : 
     278                 :             :     // 4. Static method to apply defaults to FFBEngine (Single Source of Truth)
     279                 :             :     // This is called by FFBEngine constructor to initialize with T300 defaults
     280                 :         578 :     static void ApplyDefaultsToEngine(FFBEngine& engine) {
     281         [ +  - ]:         578 :         Preset defaults; // Uses default member initializers (T300 values)
     282         [ +  - ]:         578 :         defaults.Apply(engine);
     283                 :         578 :     }
     284                 :             : 
     285                 :             :     // Apply this preset to an engine instance
     286                 :             :     // v0.7.16: Added comprehensive safety clamping to prevent crashes/NaN from invalid config values
     287                 :         600 :     void Apply(FFBEngine& engine) const {
     288                 :         600 :         engine.m_dynamic_normalization_enabled = dynamic_normalization_enabled;
     289                 :         600 :         engine.m_auto_load_normalization_enabled = auto_load_normalization_enabled;
     290                 :         600 :         engine.m_gain = (std::max)(0.0f, gain);
     291                 :         600 :         engine.m_understeer_effect = (std::max)(0.0f, (std::min)(2.0f, understeer));
     292                 :         600 :         engine.m_sop_effect = (std::max)(0.0f, (std::min)(2.0f, sop));
     293                 :         600 :         engine.m_sop_scale = (std::max)(0.01f, sop_scale);
     294                 :         600 :         engine.m_sop_smoothing_factor = (std::max)(0.0f, (std::min)(1.0f, sop_smoothing));
     295                 :         600 :         engine.m_slip_angle_smoothing = (std::max)(0.0001f, slip_smoothing);
     296                 :         600 :         engine.m_min_force = (std::max)(0.0f, min_force);
     297                 :         600 :         engine.m_oversteer_boost = (std::max)(0.0f, oversteer_boost);
     298                 :         600 :         engine.m_dynamic_weight_gain = (std::max)(0.0f, (std::min)(2.0f, dynamic_weight_gain));
     299                 :         600 :         engine.m_dynamic_weight_smoothing = (std::max)(0.0f, dynamic_weight_smoothing);
     300                 :         600 :         engine.m_grip_smoothing_steady = (std::max)(0.0f, grip_smoothing_steady);
     301                 :         600 :         engine.m_grip_smoothing_fast = (std::max)(0.0f, grip_smoothing_fast);
     302                 :         600 :         engine.m_grip_smoothing_sensitivity = (std::max)(0.001f, grip_smoothing_sensitivity);
     303                 :             : 
     304                 :         600 :         engine.m_lockup_enabled = lockup_enabled;
     305                 :         600 :         engine.m_lockup_gain = (std::max)(0.0f, lockup_gain);
     306                 :         600 :         engine.m_lockup_start_pct = (std::max)(0.1f, lockup_start_pct);
     307                 :         600 :         engine.m_lockup_full_pct = (std::max)(0.2f, lockup_full_pct);
     308                 :         600 :         engine.m_lockup_rear_boost = (std::max)(0.0f, lockup_rear_boost);
     309                 :         600 :         engine.m_lockup_gamma = (std::max)(0.1f, lockup_gamma); // Critical: prevent pow(0, negative) crash
     310                 :         600 :         engine.m_lockup_prediction_sens = (std::max)(1.0f, lockup_prediction_sens);
     311                 :         600 :         engine.m_lockup_bump_reject = (std::max)(0.01f, lockup_bump_reject);
     312                 :         600 :         engine.m_brake_load_cap = (std::max)(1.0f, brake_load_cap);
     313                 :         600 :         engine.m_texture_load_cap = (std::max)(1.0f, texture_load_cap);
     314                 :             : 
     315                 :         600 :         engine.m_abs_pulse_enabled = abs_pulse_enabled;
     316                 :         600 :         engine.m_abs_gain = (std::max)(0.0f, abs_gain);
     317                 :             : 
     318                 :         600 :         engine.m_spin_enabled = spin_enabled;
     319                 :         600 :         engine.m_spin_gain = (std::max)(0.0f, spin_gain);
     320                 :         600 :         engine.m_slide_texture_enabled = slide_enabled;
     321                 :         600 :         engine.m_slide_texture_gain = (std::max)(0.0f, slide_gain);
     322                 :         600 :         engine.m_slide_freq_scale = (std::max)(0.1f, slide_freq);
     323                 :         600 :         engine.m_road_texture_enabled = road_enabled;
     324                 :         600 :         engine.m_road_texture_gain = (std::max)(0.0f, road_gain);
     325                 :         600 :         engine.m_tactile_gain = (std::max)(0.0f, (std::min)(2.0f, tactile_gain));
     326                 :             : 
     327                 :         600 :         engine.m_soft_lock_enabled = soft_lock_enabled;
     328                 :         600 :         engine.m_soft_lock_stiffness = (std::max)(0.0f, soft_lock_stiffness);
     329                 :         600 :         engine.m_soft_lock_damping = (std::max)(0.0f, soft_lock_damping);
     330                 :             : 
     331                 :         600 :         engine.m_wheelbase_max_nm = (std::max)(1.0f, wheelbase_max_nm);
     332                 :         600 :         engine.m_target_rim_nm = (std::max)(1.0f, target_rim_nm);
     333                 :         600 :         engine.m_abs_freq_hz = (std::max)(1.0f, abs_freq);
     334                 :         600 :         engine.m_lockup_freq_scale = (std::max)(0.1f, lockup_freq_scale);
     335                 :         600 :         engine.m_spin_freq_scale = (std::max)(0.1f, spin_freq_scale);
     336                 :         600 :         engine.m_bottoming_method = bottoming_method;
     337                 :         600 :         engine.m_scrub_drag_gain = (std::max)(0.0f, scrub_drag_gain);
     338                 :         600 :         engine.m_rear_align_effect = (std::max)(0.0f, rear_align_effect);
     339                 :         600 :         engine.m_sop_yaw_gain = (std::max)(0.0f, sop_yaw_gain);
     340                 :         600 :         engine.m_gyro_gain = (std::max)(0.0f, gyro_gain);
     341                 :         600 :         engine.m_steering_shaft_gain = (std::max)(0.0f, steering_shaft_gain);
     342                 :         600 :         engine.m_ingame_ffb_gain = (std::max)(0.0f, ingame_ffb_gain);
     343                 :         600 :         engine.m_torque_source = torque_source;
     344                 :         600 :         engine.m_torque_passthrough = torque_passthrough;
     345                 :         600 :         engine.m_flatspot_suppression = flatspot_suppression;
     346                 :         600 :         engine.m_notch_q = (std::max)(0.1f, notch_q); // Critical for biquad division
     347                 :         600 :         engine.m_flatspot_strength = (std::max)(0.0f, (std::min)(1.0f, flatspot_strength));
     348                 :         600 :         engine.m_static_notch_enabled = static_notch_enabled;
     349                 :         600 :         engine.m_static_notch_freq = (std::max)(1.0f, static_notch_freq);
     350                 :         600 :         engine.m_static_notch_width = (std::max)(0.1f, static_notch_width);
     351                 :         600 :         engine.m_yaw_kick_threshold = (std::max)(0.0f, yaw_kick_threshold);
     352                 :         600 :         engine.m_speed_gate_lower = (std::max)(0.0f, speed_gate_lower);
     353                 :         600 :         engine.m_speed_gate_upper = (std::max)(0.1f, speed_gate_upper);
     354                 :             :         
     355                 :             :         // NEW: Grip & Smoothing (v0.5.7/v0.5.8)
     356                 :         600 :         engine.m_optimal_slip_angle = (std::max)(0.01f, optimal_slip_angle); // Critical for grip division
     357                 :         600 :         engine.m_optimal_slip_ratio = (std::max)(0.01f, optimal_slip_ratio); // Critical for grip division
     358                 :         600 :         engine.m_steering_shaft_smoothing = (std::max)(0.0f, steering_shaft_smoothing);
     359                 :         600 :         engine.m_gyro_smoothing = (std::max)(0.0f, gyro_smoothing);
     360                 :         600 :         engine.m_yaw_accel_smoothing = (std::max)(0.0f, yaw_smoothing);
     361                 :         600 :         engine.m_chassis_inertia_smoothing = (std::max)(0.0f, chassis_smoothing);
     362                 :         600 :         engine.m_road_fallback_scale = (std::max)(0.0f, road_fallback_scale);
     363                 :         600 :         engine.m_understeer_affects_sop = understeer_affects_sop;
     364                 :             :         
     365                 :             :         // Slope Detection (v0.7.0)
     366                 :         600 :         engine.m_slope_detection_enabled = slope_detection_enabled;
     367                 :         600 :         engine.m_slope_sg_window = (std::max)(5, (std::min)(41, slope_sg_window));
     368         [ -  + ]:         600 :         if (engine.m_slope_sg_window % 2 == 0) engine.m_slope_sg_window++; // Must be odd for SG
     369                 :         600 :         engine.m_slope_sensitivity = (std::max)(0.1f, slope_sensitivity);
     370                 :             : 
     371                 :         600 :         engine.m_slope_smoothing_tau = (std::max)(0.001f, slope_smoothing_tau);
     372                 :             : 
     373                 :             :         // v0.7.3: Slope stability fixes
     374                 :         600 :         engine.m_slope_alpha_threshold = (std::max)(0.001f, slope_alpha_threshold); // Critical for slope division
     375                 :         600 :         engine.m_slope_decay_rate = (std::max)(0.1f, slope_decay_rate);
     376                 :         600 :         engine.m_slope_confidence_enabled = slope_confidence_enabled;
     377                 :         600 :         engine.m_slope_confidence_max_rate = (std::max)(engine.m_slope_alpha_threshold + 0.01f, slope_confidence_max_rate);
     378                 :             : 
     379                 :             :         // v0.7.11: Min/Max thresholds
     380                 :         600 :         engine.m_slope_min_threshold = slope_min_threshold;
     381                 :         600 :         engine.m_slope_max_threshold = slope_max_threshold;
     382                 :             : 
     383                 :             :         // NEW v0.7.40: Advanced Slope Settings
     384                 :         600 :         engine.m_slope_g_slew_limit = (std::max)(1.0f, slope_g_slew_limit);
     385                 :         600 :         engine.m_slope_use_torque = slope_use_torque;
     386                 :         600 :         engine.m_slope_torque_sensitivity = (std::max)(0.01f, slope_torque_sensitivity);
     387                 :             : 
     388                 :             :         // Stage 1 & 2 Normalization (Issue #152 & #153)
     389                 :             :         // Initialize session peak from target rim torque to provide a sane starting point.
     390                 :         600 :         engine.m_session_peak_torque = (std::max)(1.0, (double)target_rim_nm);
     391                 :         600 :         engine.m_smoothed_structural_mult = 1.0 / engine.m_session_peak_torque;
     392                 :         600 :     }
     393                 :             : 
     394                 :             :     // NEW: Ensure values are within safe ranges (v0.7.16)
     395                 :          24 :     void Validate() {
     396                 :          24 :         gain = (std::max)(0.0f, gain);
     397                 :          24 :         understeer = (std::max)(0.0f, (std::min)(2.0f, understeer));
     398                 :          24 :         sop = (std::max)(0.0f, (std::min)(2.0f, sop));
     399                 :          24 :         sop_scale = (std::max)(0.01f, sop_scale);
     400                 :          24 :         sop_smoothing = (std::max)(0.0f, (std::min)(1.0f, sop_smoothing));
     401                 :          24 :         slip_smoothing = (std::max)(0.0001f, slip_smoothing);
     402                 :          24 :         min_force = (std::max)(0.0f, min_force);
     403                 :          24 :         oversteer_boost = (std::max)(0.0f, oversteer_boost);
     404                 :          24 :         dynamic_weight_gain = (std::max)(0.0f, (std::min)(2.0f, dynamic_weight_gain));
     405                 :          24 :         dynamic_weight_smoothing = (std::max)(0.0f, dynamic_weight_smoothing);
     406                 :          24 :         grip_smoothing_steady = (std::max)(0.0f, grip_smoothing_steady);
     407                 :          24 :         grip_smoothing_fast = (std::max)(0.0f, grip_smoothing_fast);
     408                 :          24 :         grip_smoothing_sensitivity = (std::max)(0.001f, grip_smoothing_sensitivity);
     409                 :          24 :         lockup_gain = (std::max)(0.0f, lockup_gain);
     410                 :          24 :         lockup_start_pct = (std::max)(0.1f, lockup_start_pct);
     411                 :          24 :         lockup_full_pct = (std::max)(0.2f, lockup_full_pct);
     412                 :          24 :         lockup_rear_boost = (std::max)(0.0f, lockup_rear_boost);
     413                 :          24 :         lockup_gamma = (std::max)(0.1f, lockup_gamma);
     414                 :          24 :         lockup_prediction_sens = (std::max)(1.0f, lockup_prediction_sens);
     415                 :          24 :         lockup_bump_reject = (std::max)(0.01f, lockup_bump_reject);
     416                 :          24 :         brake_load_cap = (std::max)(1.0f, brake_load_cap);
     417                 :          24 :         texture_load_cap = (std::max)(1.0f, texture_load_cap);
     418                 :          24 :         abs_gain = (std::max)(0.0f, abs_gain);
     419                 :          24 :         spin_gain = (std::max)(0.0f, spin_gain);
     420                 :          24 :         slide_gain = (std::max)(0.0f, slide_gain);
     421                 :          24 :         slide_freq = (std::max)(0.1f, slide_freq);
     422                 :          24 :         road_gain = (std::max)(0.0f, road_gain);
     423                 :          24 :         tactile_gain = (std::max)(0.0f, (std::min)(2.0f, tactile_gain));
     424                 :          24 :         soft_lock_stiffness = (std::max)(0.0f, soft_lock_stiffness);
     425                 :          24 :         soft_lock_damping = (std::max)(0.0f, soft_lock_damping);
     426                 :          24 :         wheelbase_max_nm = (std::max)(1.0f, wheelbase_max_nm);
     427                 :          24 :         target_rim_nm = (std::max)(1.0f, target_rim_nm);
     428                 :          24 :         abs_freq = (std::max)(1.0f, abs_freq);
     429                 :          24 :         lockup_freq_scale = (std::max)(0.1f, lockup_freq_scale);
     430                 :          24 :         spin_freq_scale = (std::max)(0.1f, spin_freq_scale);
     431                 :          24 :         scrub_drag_gain = (std::max)(0.0f, scrub_drag_gain);
     432                 :          24 :         rear_align_effect = (std::max)(0.0f, rear_align_effect);
     433                 :          24 :         sop_yaw_gain = (std::max)(0.0f, sop_yaw_gain);
     434                 :          24 :         gyro_gain = (std::max)(0.0f, gyro_gain);
     435                 :          24 :         steering_shaft_gain = (std::max)(0.0f, steering_shaft_gain);
     436                 :          24 :         ingame_ffb_gain = (std::max)(0.0f, ingame_ffb_gain);
     437                 :          24 :         torque_source = (std::max)(0, (std::min)(1, torque_source));
     438                 :             :         // torque_passthrough is bool, no clamp needed
     439                 :          24 :         notch_q = (std::max)(0.1f, notch_q);
     440                 :          24 :         flatspot_strength = (std::max)(0.0f, (std::min)(1.0f, flatspot_strength));
     441                 :          24 :         static_notch_freq = (std::max)(1.0f, static_notch_freq);
     442                 :          24 :         static_notch_width = (std::max)(0.1f, static_notch_width);
     443                 :          24 :         speed_gate_upper = (std::max)(0.1f, speed_gate_upper);
     444                 :          24 :         optimal_slip_angle = (std::max)(0.01f, optimal_slip_angle);
     445                 :          24 :         optimal_slip_ratio = (std::max)(0.01f, optimal_slip_ratio);
     446                 :          24 :         steering_shaft_smoothing = (std::max)(0.0f, steering_shaft_smoothing);
     447                 :          24 :         gyro_smoothing = (std::max)(0.0f, gyro_smoothing);
     448                 :          24 :         yaw_smoothing = (std::max)(0.0f, yaw_smoothing);
     449                 :          24 :         chassis_smoothing = (std::max)(0.0f, chassis_smoothing);
     450                 :          24 :         road_fallback_scale = (std::max)(0.0f, road_fallback_scale);
     451                 :          24 :         slope_sg_window = (std::max)(5, (std::min)(41, slope_sg_window));
     452         [ -  + ]:          24 :         if (slope_sg_window % 2 == 0) slope_sg_window++;
     453                 :          24 :         slope_sensitivity = (std::max)(0.1f, slope_sensitivity);
     454                 :          24 :         slope_smoothing_tau = (std::max)(0.001f, slope_smoothing_tau);
     455                 :          24 :         slope_alpha_threshold = (std::max)(0.001f, slope_alpha_threshold);
     456                 :          24 :         slope_decay_rate = (std::max)(0.1f, slope_decay_rate);
     457                 :          24 :         slope_g_slew_limit = (std::max)(1.0f, slope_g_slew_limit);
     458                 :          24 :         slope_torque_sensitivity = (std::max)(0.01f, slope_torque_sensitivity);
     459                 :          24 :         slope_confidence_max_rate = (std::max)(slope_alpha_threshold + 0.01f, slope_confidence_max_rate);
     460                 :          24 :     }
     461                 :             : 
     462                 :             :     // NEW: Capture current engine state into this preset
     463                 :         393 :     void UpdateFromEngine(const FFBEngine& engine) {
     464                 :         393 :         dynamic_normalization_enabled = engine.m_dynamic_normalization_enabled;
     465                 :         393 :         auto_load_normalization_enabled = engine.m_auto_load_normalization_enabled;
     466                 :         393 :         gain = engine.m_gain;
     467                 :         393 :         understeer = engine.m_understeer_effect;
     468                 :         393 :         sop = engine.m_sop_effect;
     469                 :         393 :         sop_scale = engine.m_sop_scale;
     470                 :         393 :         sop_smoothing = engine.m_sop_smoothing_factor;
     471                 :         393 :         slip_smoothing = engine.m_slip_angle_smoothing;
     472                 :         393 :         min_force = engine.m_min_force;
     473                 :         393 :         oversteer_boost = engine.m_oversteer_boost;
     474                 :         393 :         dynamic_weight_gain = engine.m_dynamic_weight_gain;
     475                 :         393 :         dynamic_weight_smoothing = engine.m_dynamic_weight_smoothing;
     476                 :         393 :         grip_smoothing_steady = engine.m_grip_smoothing_steady;
     477                 :         393 :         grip_smoothing_fast = engine.m_grip_smoothing_fast;
     478                 :         393 :         grip_smoothing_sensitivity = engine.m_grip_smoothing_sensitivity;
     479                 :         393 :         lockup_enabled = engine.m_lockup_enabled;
     480                 :         393 :         lockup_gain = engine.m_lockup_gain;
     481                 :         393 :         lockup_start_pct = engine.m_lockup_start_pct;
     482                 :         393 :         lockup_full_pct = engine.m_lockup_full_pct;
     483                 :         393 :         lockup_rear_boost = engine.m_lockup_rear_boost;
     484                 :         393 :         lockup_gamma = engine.m_lockup_gamma;
     485                 :         393 :         lockup_prediction_sens = engine.m_lockup_prediction_sens;
     486                 :         393 :         lockup_bump_reject = engine.m_lockup_bump_reject;
     487                 :         393 :         brake_load_cap = engine.m_brake_load_cap;
     488                 :         393 :         texture_load_cap = engine.m_texture_load_cap;  // NEW v0.6.25
     489                 :         393 :         abs_pulse_enabled = engine.m_abs_pulse_enabled;
     490                 :         393 :         abs_gain = engine.m_abs_gain;
     491                 :             :         
     492                 :         393 :         spin_enabled = engine.m_spin_enabled;
     493                 :         393 :         spin_gain = engine.m_spin_gain;
     494                 :         393 :         slide_enabled = engine.m_slide_texture_enabled;
     495                 :         393 :         slide_gain = engine.m_slide_texture_gain;
     496                 :         393 :         slide_freq = engine.m_slide_freq_scale;
     497                 :         393 :         road_enabled = engine.m_road_texture_enabled;
     498                 :         393 :         road_gain = engine.m_road_texture_gain;
     499                 :         393 :         tactile_gain = engine.m_tactile_gain;
     500                 :             : 
     501                 :         393 :         soft_lock_enabled = engine.m_soft_lock_enabled;
     502                 :         393 :         soft_lock_stiffness = engine.m_soft_lock_stiffness;
     503                 :         393 :         soft_lock_damping = engine.m_soft_lock_damping;
     504                 :             : 
     505                 :         393 :         wheelbase_max_nm = engine.m_wheelbase_max_nm;
     506                 :         393 :         target_rim_nm = engine.m_target_rim_nm;
     507                 :         393 :         abs_freq = engine.m_abs_freq_hz;
     508                 :         393 :         lockup_freq_scale = engine.m_lockup_freq_scale;
     509                 :         393 :         spin_freq_scale = engine.m_spin_freq_scale;
     510                 :         393 :         bottoming_method = engine.m_bottoming_method;
     511                 :         393 :         scrub_drag_gain = engine.m_scrub_drag_gain;
     512                 :         393 :         rear_align_effect = engine.m_rear_align_effect;
     513                 :         393 :         sop_yaw_gain = engine.m_sop_yaw_gain;
     514                 :         393 :         gyro_gain = engine.m_gyro_gain;
     515                 :         393 :         steering_shaft_gain = engine.m_steering_shaft_gain;
     516                 :         393 :         ingame_ffb_gain = engine.m_ingame_ffb_gain;
     517                 :         393 :         torque_source = engine.m_torque_source;
     518                 :         393 :         torque_passthrough = engine.m_torque_passthrough;
     519                 :         393 :         flatspot_suppression = engine.m_flatspot_suppression;
     520                 :         393 :         notch_q = engine.m_notch_q;
     521                 :         393 :         flatspot_strength = engine.m_flatspot_strength;
     522                 :         393 :         static_notch_enabled = engine.m_static_notch_enabled;
     523                 :         393 :         static_notch_freq = engine.m_static_notch_freq;
     524                 :         393 :         static_notch_width = engine.m_static_notch_width;
     525                 :         393 :         yaw_kick_threshold = engine.m_yaw_kick_threshold;
     526                 :         393 :         speed_gate_lower = engine.m_speed_gate_lower;
     527                 :         393 :         speed_gate_upper = engine.m_speed_gate_upper;
     528                 :             : 
     529                 :             :         // NEW: Grip & Smoothing (v0.5.7/v0.5.8)
     530                 :         393 :         optimal_slip_angle = engine.m_optimal_slip_angle;
     531                 :         393 :         optimal_slip_ratio = engine.m_optimal_slip_ratio;
     532                 :         393 :         steering_shaft_smoothing = engine.m_steering_shaft_smoothing;
     533                 :         393 :         gyro_smoothing = engine.m_gyro_smoothing;
     534                 :         393 :         yaw_smoothing = engine.m_yaw_accel_smoothing;
     535                 :         393 :         chassis_smoothing = engine.m_chassis_inertia_smoothing;
     536                 :         393 :         road_fallback_scale = engine.m_road_fallback_scale;
     537                 :         393 :         understeer_affects_sop = engine.m_understeer_affects_sop;
     538                 :             : 
     539                 :             :         // Slope Detection (v0.7.0)
     540                 :         393 :         slope_detection_enabled = engine.m_slope_detection_enabled;
     541                 :         393 :         slope_sg_window = engine.m_slope_sg_window;
     542                 :         393 :         slope_sensitivity = engine.m_slope_sensitivity;
     543                 :             : 
     544                 :         393 :         slope_smoothing_tau = engine.m_slope_smoothing_tau;
     545                 :             : 
     546                 :             :         // v0.7.3: Slope stability fixes
     547                 :         393 :         slope_alpha_threshold = engine.m_slope_alpha_threshold;
     548                 :         393 :         slope_decay_rate = engine.m_slope_decay_rate;
     549                 :         393 :         slope_confidence_enabled = engine.m_slope_confidence_enabled;
     550                 :         393 :         slope_confidence_max_rate = engine.m_slope_confidence_max_rate;
     551                 :             : 
     552                 :             :         // v0.7.11: Min/Max thresholds
     553                 :         393 :         slope_min_threshold = engine.m_slope_min_threshold;
     554                 :         393 :         slope_max_threshold = engine.m_slope_max_threshold;
     555                 :             : 
     556                 :             :         // NEW v0.7.40: Advanced Slope Settings
     557                 :         393 :         slope_g_slew_limit = engine.m_slope_g_slew_limit;
     558                 :         393 :         slope_use_torque = engine.m_slope_use_torque;
     559                 :         393 :         slope_torque_sensitivity = engine.m_slope_torque_sensitivity;
     560                 :         393 :         app_version = LMUFFB_VERSION;
     561                 :         393 :     }
     562                 :             : 
     563                 :         459 :     bool Equals(const Preset& p) const {
     564                 :         459 :         const float eps = 0.0001f;
     565                 :       11405 :         auto is_near = [](float a, float b, float epsilon) { return std::abs(a - b) < epsilon; };
     566                 :             : 
     567         [ +  + ]:         459 :         if (!is_near(gain, p.gain, eps)) return false;
     568         [ +  + ]:         457 :         if (!is_near(understeer, p.understeer, eps)) return false;
     569         [ +  + ]:         456 :         if (!is_near(sop, p.sop, eps)) return false;
     570         [ +  + ]:         451 :         if (!is_near(sop_scale, p.sop_scale, eps)) return false;
     571         [ +  + ]:         450 :         if (!is_near(sop_smoothing, p.sop_smoothing, eps)) return false;
     572         [ +  + ]:         449 :         if (!is_near(slip_smoothing, p.slip_smoothing, eps)) return false;
     573         [ +  + ]:         448 :         if (!is_near(min_force, p.min_force, eps)) return false;
     574         [ +  + ]:         447 :         if (!is_near(oversteer_boost, p.oversteer_boost, eps)) return false;
     575         [ +  + ]:         440 :         if (!is_near(dynamic_weight_gain, p.dynamic_weight_gain, eps)) return false;
     576         [ +  + ]:         439 :         if (!is_near(dynamic_weight_smoothing, p.dynamic_weight_smoothing, eps)) return false;
     577         [ +  + ]:         438 :         if (!is_near(grip_smoothing_steady, p.grip_smoothing_steady, eps)) return false;
     578         [ +  + ]:         437 :         if (!is_near(grip_smoothing_fast, p.grip_smoothing_fast, eps)) return false;
     579         [ +  + ]:         436 :         if (!is_near(grip_smoothing_sensitivity, p.grip_smoothing_sensitivity, eps)) return false;
     580                 :             : 
     581         [ +  + ]:         435 :         if (lockup_enabled != p.lockup_enabled) return false;
     582         [ +  + ]:         434 :         if (!is_near(lockup_gain, p.lockup_gain, eps)) return false;
     583         [ +  + ]:         433 :         if (!is_near(lockup_start_pct, p.lockup_start_pct, eps)) return false;
     584         [ +  + ]:         432 :         if (!is_near(lockup_full_pct, p.lockup_full_pct, eps)) return false;
     585         [ +  + ]:         431 :         if (!is_near(lockup_rear_boost, p.lockup_rear_boost, eps)) return false;
     586         [ +  + ]:         430 :         if (!is_near(lockup_gamma, p.lockup_gamma, eps)) return false;
     587         [ +  + ]:         429 :         if (!is_near(lockup_prediction_sens, p.lockup_prediction_sens, eps)) return false;
     588         [ +  + ]:         428 :         if (!is_near(lockup_bump_reject, p.lockup_bump_reject, eps)) return false;
     589         [ +  + ]:         427 :         if (!is_near(brake_load_cap, p.brake_load_cap, eps)) return false;
     590         [ +  + ]:         426 :         if (!is_near(texture_load_cap, p.texture_load_cap, eps)) return false;
     591                 :             : 
     592         [ +  + ]:         425 :         if (abs_pulse_enabled != p.abs_pulse_enabled) return false;
     593         [ +  + ]:          68 :         if (!is_near(abs_gain, p.abs_gain, eps)) return false;
     594         [ +  + ]:          67 :         if (!is_near(abs_freq, p.abs_freq, eps)) return false;
     595                 :             : 
     596         [ +  + ]:          66 :         if (spin_enabled != p.spin_enabled) return false;
     597         [ -  + ]:          65 :         if (!is_near(spin_gain, p.spin_gain, eps)) return false;
     598         [ +  + ]:          65 :         if (!is_near(spin_freq_scale, p.spin_freq_scale, eps)) return false;
     599                 :             : 
     600         [ +  + ]:          64 :         if (slide_enabled != p.slide_enabled) return false;
     601         [ +  + ]:          63 :         if (!is_near(slide_gain, p.slide_gain, eps)) return false;
     602         [ +  + ]:          62 :         if (!is_near(slide_freq, p.slide_freq, eps)) return false;
     603                 :             : 
     604         [ +  + ]:          61 :         if (road_enabled != p.road_enabled) return false;
     605         [ +  + ]:          60 :         if (!is_near(road_gain, p.road_gain, eps)) return false;
     606         [ -  + ]:          59 :         if (!is_near(tactile_gain, p.tactile_gain, eps)) return false;
     607                 :             : 
     608         [ -  + ]:          59 :         if (dynamic_normalization_enabled != p.dynamic_normalization_enabled) return false;
     609         [ -  + ]:          59 :         if (auto_load_normalization_enabled != p.auto_load_normalization_enabled) return false;
     610                 :             : 
     611         [ +  + ]:          59 :         if (soft_lock_enabled != p.soft_lock_enabled) return false;
     612         [ +  + ]:          58 :         if (!is_near(soft_lock_stiffness, p.soft_lock_stiffness, eps)) return false;
     613         [ -  + ]:          57 :         if (!is_near(soft_lock_damping, p.soft_lock_damping, eps)) return false;
     614                 :             : 
     615         [ +  + ]:          57 :         if (!is_near(wheelbase_max_nm, p.wheelbase_max_nm, eps)) return false;
     616         [ +  + ]:          56 :         if (!is_near(target_rim_nm, p.target_rim_nm, eps)) return false;
     617         [ +  + ]:          55 :         if (!is_near(lockup_freq_scale, p.lockup_freq_scale, eps)) return false;
     618         [ +  + ]:          54 :         if (bottoming_method != p.bottoming_method) return false;
     619         [ +  + ]:          53 :         if (!is_near(scrub_drag_gain, p.scrub_drag_gain, eps)) return false;
     620         [ +  + ]:          52 :         if (!is_near(rear_align_effect, p.rear_align_effect, eps)) return false;
     621         [ +  + ]:          51 :         if (!is_near(sop_yaw_gain, p.sop_yaw_gain, eps)) return false;
     622         [ +  + ]:          50 :         if (!is_near(gyro_gain, p.gyro_gain, eps)) return false;
     623         [ +  + ]:          49 :         if (!is_near(steering_shaft_gain, p.steering_shaft_gain, eps)) return false;
     624         [ +  + ]:          48 :         if (!is_near(ingame_ffb_gain, p.ingame_ffb_gain, eps)) return false;
     625         [ +  + ]:          47 :         if (torque_source != p.torque_source) return false;
     626         [ +  + ]:          42 :         if (torque_passthrough != p.torque_passthrough) return false;
     627                 :             : 
     628         [ +  + ]:          41 :         if (!is_near(optimal_slip_angle, p.optimal_slip_angle, eps)) return false;
     629         [ +  + ]:          40 :         if (!is_near(optimal_slip_ratio, p.optimal_slip_ratio, eps)) return false;
     630         [ +  + ]:          39 :         if (!is_near(steering_shaft_smoothing, p.steering_shaft_smoothing, eps)) return false;
     631         [ +  + ]:          38 :         if (!is_near(gyro_smoothing, p.gyro_smoothing, eps)) return false;
     632         [ +  + ]:          37 :         if (!is_near(yaw_smoothing, p.yaw_smoothing, eps)) return false;
     633         [ +  + ]:          36 :         if (!is_near(chassis_smoothing, p.chassis_smoothing, eps)) return false;
     634                 :             : 
     635         [ +  + ]:          35 :         if (flatspot_suppression != p.flatspot_suppression) return false;
     636         [ +  + ]:          34 :         if (!is_near(notch_q, p.notch_q, eps)) return false;
     637         [ +  + ]:          33 :         if (!is_near(flatspot_strength, p.flatspot_strength, eps)) return false;
     638                 :             : 
     639         [ +  + ]:          32 :         if (static_notch_enabled != p.static_notch_enabled) return false;
     640         [ +  + ]:          31 :         if (!is_near(static_notch_freq, p.static_notch_freq, eps)) return false;
     641         [ +  + ]:          30 :         if (!is_near(static_notch_width, p.static_notch_width, eps)) return false;
     642         [ +  + ]:          29 :         if (!is_near(yaw_kick_threshold, p.yaw_kick_threshold, eps)) return false;
     643                 :             : 
     644         [ +  + ]:          28 :         if (!is_near(speed_gate_lower, p.speed_gate_lower, eps)) return false;
     645         [ +  + ]:          27 :         if (!is_near(speed_gate_upper, p.speed_gate_upper, eps)) return false;
     646                 :             : 
     647         [ +  + ]:          26 :         if (!is_near(road_fallback_scale, p.road_fallback_scale, eps)) return false;
     648         [ +  + ]:          25 :         if (understeer_affects_sop != p.understeer_affects_sop) return false;
     649                 :             : 
     650         [ +  + ]:          24 :         if (slope_detection_enabled != p.slope_detection_enabled) return false;
     651         [ +  + ]:          23 :         if (slope_sg_window != p.slope_sg_window) return false;
     652         [ -  + ]:          22 :         if (!is_near(slope_sensitivity, p.slope_sensitivity, eps)) return false;
     653                 :             : 
     654         [ +  + ]:          22 :         if (!is_near(slope_smoothing_tau, p.slope_smoothing_tau, eps)) return false;
     655         [ +  + ]:          21 :         if (!is_near(slope_alpha_threshold, p.slope_alpha_threshold, eps)) return false;
     656         [ +  + ]:          20 :         if (!is_near(slope_decay_rate, p.slope_decay_rate, eps)) return false;
     657         [ +  + ]:          19 :         if (slope_confidence_enabled != p.slope_confidence_enabled) return false;
     658         [ +  + ]:          18 :         if (!is_near(slope_min_threshold, p.slope_min_threshold, eps)) return false;
     659         [ +  + ]:          17 :         if (!is_near(slope_max_threshold, p.slope_max_threshold, eps)) return false;
     660                 :             : 
     661         [ +  + ]:          16 :         if (!is_near(slope_g_slew_limit, p.slope_g_slew_limit, eps)) return false;
     662         [ +  + ]:          15 :         if (slope_use_torque != p.slope_use_torque) return false;
     663         [ -  + ]:          14 :         if (!is_near(slope_torque_sensitivity, p.slope_torque_sensitivity, eps)) return false;
     664         [ +  + ]:          14 :         if (!is_near(slope_confidence_max_rate, p.slope_confidence_max_rate, eps)) return false;
     665                 :             : 
     666                 :          13 :         return true;
     667                 :             :     }
     668                 :             : };
     669                 :             : 
     670                 :             : class Config {
     671                 :             : public:
     672                 :             :     static std::string m_config_path; // Default: "config.ini"
     673                 :             :     static void Save(const FFBEngine& engine, const std::string& filename = "");
     674                 :             :     static void Load(FFBEngine& engine, const std::string& filename = "");
     675                 :             :     
     676                 :             :     // Preset Management
     677                 :             :     static std::vector<Preset> presets;
     678                 :             :     static std::string m_last_preset_name; // NEW (v0.7.14)
     679                 :             :     static void LoadPresets(); // Populates presets vector
     680                 :             :     static void ApplyPreset(int index, FFBEngine& engine);
     681                 :             :     
     682                 :             :     // NEW: Add a user preset
     683                 :             :     static void AddUserPreset(const std::string& name, const FFBEngine& engine);
     684                 :             : 
     685                 :             :     // NEW: Delete and Duplicate (v0.7.14)
     686                 :             :     static void DeletePreset(int index, const FFBEngine& engine);
     687                 :             :     static void DuplicatePreset(int index, const FFBEngine& engine);
     688                 :             :     static bool IsEngineDirtyRelativeToPreset(int index, const FFBEngine& engine);
     689                 :             : 
     690                 :             :     // NEW: Import/Export (v0.7.12)
     691                 :             :     static void ExportPreset(int index, const std::string& filename);
     692                 :             :     static bool ImportPreset(const std::string& filename, const FFBEngine& engine);
     693                 :             : 
     694                 :             :     // NEW: Persist selected device
     695                 :             :     static std::string m_last_device_guid;
     696                 :             : 
     697                 :             :     // Global App Settings (not part of FFB Physics)
     698                 :             :     static bool m_always_on_top;      // NEW: Keep window on top
     699                 :             :     static bool m_auto_start_logging; // NEW: Auto-start logging
     700                 :             :     static std::string m_log_path;    // NEW: Path to save logs
     701                 :             : 
     702                 :             :     // Window Geometry Persistence (v0.5.5)
     703                 :             :     static int win_pos_x, win_pos_y;
     704                 :             :     static int win_w_small, win_h_small; // Dimensions for Config Only
     705                 :             :     static int win_w_large, win_h_large; // Dimensions for Config + Graphs
     706                 :             :     static bool show_graphs;             // Remember if graphs were open
     707                 :             : 
     708                 :             :     // Persistent storage for vehicle static loads (v0.7.70)
     709                 :             :     static std::map<std::string, double> m_saved_static_loads;
     710                 :             :     static std::recursive_mutex m_static_loads_mutex;
     711                 :             : 
     712                 :             :     // Flag to request a save from the main thread (avoids File I/O on FFB thread)
     713                 :             :     static std::atomic<bool> m_needs_save;
     714                 :             : 
     715                 :             :     // Thread-safe access to static loads map (v0.7.70)
     716                 :             :     static void SetSavedStaticLoad(const std::string& vehicleName, double value);
     717                 :             :     static bool GetSavedStaticLoad(const std::string& vehicleName, double& value);
     718                 :             : 
     719                 :             : private:
     720                 :             :     // Helper for parsing preset lines (v0.7.12)
     721                 :             :     static void ParsePresetLine(const std::string& line, Preset& p, std::string& version, bool& needs_save, bool& legacy_torque_hack, float& legacy_torque_val);
     722                 :             :     // Helper for writing preset fields (v0.7.12)
     723                 :             :     static void WritePresetFields(std::ofstream& file, const Preset& p);
     724                 :             : };
     725                 :             : 
     726                 :             : 
     727                 :             : 
     728                 :             : 
     729                 :             : #endif
     730                 :             : 
     731                 :             : 
     732                 :             : 
     733                 :             : 
     734                 :             : 
        

Generated by: LCOV version 2.0-1