LCOV - code coverage report
Current view: top level - core - Config.h (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 491 491
Test Date: 2026-03-20 13:12:51 Functions: 100.0 % 31 31
Branches: 80.9 % 236 191

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

Generated by: LCOV version 2.0-1