LCOV - code coverage report
Current view: top level - core - Config.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 97.8 % 1586 1551
Test Date: 2026-03-20 13:12:51 Functions: 100.0 % 26 26
Branches: 64.3 % 2468 1587

             Branch data     Line data    Source code
       1                 :             : #include "Config.h"
       2                 :             : #include "Version.h"
       3                 :             : #include "Logger.h"
       4                 :             : #include "Logger.h"
       5                 :             : #include <fstream>
       6                 :             : #include <sstream>
       7                 :             : #include <iostream>
       8                 :             : #include <algorithm>
       9                 :             : #include <mutex>
      10                 :             : 
      11                 :             : extern std::recursive_mutex g_engine_mutex;
      12                 :             : 
      13                 :             : bool Config::m_always_on_top = true;
      14                 :             : std::string Config::m_last_device_guid = "";
      15                 :             : std::string Config::m_last_preset_name = "Default";
      16                 :             : std::string Config::m_config_path = "config.ini";
      17                 :             : bool Config::m_auto_start_logging = false;
      18                 :             : std::string Config::m_log_path = "logs/";
      19                 :             : 
      20                 :             : // Window Geometry Defaults (v0.5.5)
      21                 :             : int Config::win_pos_x = 100;
      22                 :             : int Config::win_pos_y = 100;
      23                 :             : int Config::win_w_small = 500;   // Narrow (Config Only)
      24                 :             : int Config::win_h_small = 800;
      25                 :             : int Config::win_w_large = 1400;  // Wide (Config + Graphs)
      26                 :             : int Config::win_h_large = 800;
      27                 :             : bool Config::show_graphs = false;
      28                 :             : 
      29                 :             : std::map<std::string, double> Config::m_saved_static_loads;
      30                 :             : std::recursive_mutex Config::m_static_loads_mutex;
      31                 :             : std::atomic<bool> Config::m_needs_save{ false };
      32                 :             : 
      33                 :             : std::vector<Preset> Config::presets;
      34                 :             : 
      35                 :             : // Helper to compare semantic version strings (e.g., "0.7.66" <= "0.7.66")
      36                 :         127 : static bool IsVersionLessEqual(const std::string& v1, const std::string& v2) {
      37         [ +  + ]:         127 :     if (v1.empty()) return true; // Empty version treated as legacy
      38         [ -  + ]:         122 :     if (v2.empty()) return false;
      39                 :             : 
      40   [ +  -  +  - ]:         122 :     std::stringstream ss1(v1), ss2(v2);
      41                 :         122 :     std::string segment1, segment2;
      42                 :             : 
      43                 :             :     while (true) {
      44   [ +  -  +  - ]:         364 :         bool has1 = (bool)std::getline(ss1, segment1, '.');
      45   [ +  -  +  - ]:         364 :         bool has2 = (bool)std::getline(ss2, segment2, '.');
      46                 :             : 
      47   [ +  +  +  - ]:         364 :         if (!has1 && !has2) return true; // Exactly equal
      48                 :             : 
      49                 :         359 :         int val1 = 0;
      50   [ +  -  +  -  :         359 :         try { if (has1) val1 = std::stoi(segment1); } catch (...) {}
                   -  - ]
      51                 :         359 :         int val2 = 0;
      52   [ +  -  +  -  :         359 :         try { if (has2) val2 = std::stoi(segment2); } catch (...) {}
                   -  - ]
      53                 :             : 
      54         [ +  + ]:         359 :         if (val1 < val2) return true;
      55         [ +  + ]:         354 :         if (val1 > val2) return false;
      56                 :             : 
      57   [ +  -  +  - ]:         242 :         if (!has1 || !has2) break;
      58                 :         242 :     }
      59                 :           0 :     return true;
      60                 :         122 : }
      61                 :             : 
      62                 :          93 : static void FinalizePreset(std::vector<Preset>& presets, Preset& p, const std::string& name, const std::string& version, bool legacy_torque_hack, float legacy_torque_val, bool& needs_save, bool handle_collisions) {
      63         [ -  + ]:          93 :     if (name.empty()) return;
      64                 :             : 
      65                 :          93 :     p.name = name;
      66                 :          93 :     p.is_builtin = false;
      67                 :             : 
      68                 :             :     // Issue #211: Legacy 100Nm hack scaling
      69   [ +  +  +  -  :          95 :     if (legacy_torque_hack && IsVersionLessEqual(version, "0.7.66")) {
          +  -  +  -  +  
          +  +  +  +  +  
             -  -  -  - ]
      70                 :           1 :         p.general.gain *= (15.0f / legacy_torque_val);
      71                 :           1 :         Logger::Get().LogFile("[Config] Migrated legacy 100Nm hack for preset '%s'. Scaling gain.", name.c_str());
      72                 :           1 :         needs_save = true;
      73                 :             :     }
      74                 :             : 
      75                 :             :     // MIGRATION: If version is missing or old, update it
      76   [ +  +  +  -  :         261 :     if (version.empty() || IsVersionLessEqual(version, "0.7.146")) {
          +  -  +  +  +  
          +  +  +  +  +  
             -  -  -  - ]
      77                 :          12 :         p.sop_smoothing = 0.0f; // Issue #37: Reset to Raw for migration
      78                 :          12 :         needs_save = true;
      79         [ +  + ]:          12 :         if (version.empty()) {
      80                 :           9 :             p.app_version = LMUFFB_VERSION;
      81                 :           9 :             Logger::Get().LogFile("[Config] Migrated legacy preset '%s' to version %s", name.c_str(), LMUFFB_VERSION);
      82                 :             :         } else {
      83                 :           3 :             Logger::Get().LogFile("[Config] Reset SoP Smoothing for migrated preset '%s' (v%s)", name.c_str(), version.c_str());
      84                 :           3 :             p.app_version = LMUFFB_VERSION;
      85                 :             :         }
      86                 :             :     } else {
      87                 :          81 :         p.app_version = version;
      88                 :             :     }
      89                 :             : 
      90         [ +  + ]:          93 :     if (handle_collisions) {
      91         [ +  - ]:           3 :         std::string base_name = p.name;
      92                 :           3 :         int counter = 1;
      93                 :           3 :         bool exists = true;
      94         [ +  + ]:           6 :         while (exists) {
      95                 :           3 :             exists = false;
      96         [ +  + ]:          33 :             for (const auto& existing : presets) {
      97         [ -  + ]:          30 :                 if (existing.name == p.name) {
      98   [ #  #  #  #  :           0 :                     p.name = base_name + " (" + std::to_string(counter++) + ")";
                   #  # ]
      99                 :           0 :                     exists = true;
     100                 :           0 :                     break;
     101                 :             :                 }
     102                 :             :             }
     103                 :             :         }
     104                 :           3 :     }
     105                 :             : 
     106                 :          93 :     p.Validate();
     107                 :          93 :     presets.push_back(p);
     108                 :             : }
     109                 :             : 
     110                 :        9025 : bool Config::ParseSystemLine(const std::string& key, const std::string& value, Preset& current_preset, std::string& current_preset_version, bool& needs_save, bool& legacy_torque_hack, float& legacy_torque_val) {
     111         [ +  + ]:        9025 :     if (key == "app_version") { current_preset_version = value; return true; }
     112         [ +  + ]:        8941 :     if (key == "gain") { current_preset.general.gain = std::stof(value); return true; }
     113         [ +  + ]:        8853 :     if (key == "wheelbase_max_nm") { current_preset.general.wheelbase_max_nm = std::stof(value); return true; }
     114         [ +  + ]:        8773 :     if (key == "target_rim_nm") { current_preset.general.target_rim_nm = std::stof(value); return true; }
     115         [ +  + ]:        8693 :     if (key == "min_force") { current_preset.general.min_force = std::stof(value); return true; }
     116         [ +  + ]:        8613 :     if (key == "max_torque_ref") {
     117                 :           1 :         float old_val = std::stof(value);
     118         [ +  - ]:           1 :         if (old_val > 40.0f) {
     119                 :           1 :             current_preset.general.wheelbase_max_nm = 15.0f;
     120                 :           1 :             current_preset.general.target_rim_nm = 10.0f;
     121                 :           1 :             legacy_torque_hack = true;
     122                 :           1 :             legacy_torque_val = old_val;
     123                 :             :         } else {
     124                 :           0 :             current_preset.general.wheelbase_max_nm = old_val;
     125                 :           0 :             current_preset.general.target_rim_nm = old_val;
     126                 :             :         }
     127                 :           1 :         needs_save = true;
     128                 :           1 :         return true;
     129                 :             :     }
     130   [ +  +  +  -  :        8612 :     if (key == "rest_api_fallback_enabled") { current_preset.rest_api_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     131         [ +  + ]:        8533 :     if (key == "rest_api_port") { current_preset.rest_api_port = std::stoi(value); return true; }
     132                 :        8454 :     return false;
     133                 :             : }
     134                 :             : 
     135                 :        8454 : bool Config::ParsePhysicsLine(const std::string& key, const std::string& value, Preset& current_preset) {
     136         [ +  + ]:        8454 :     if (key == "understeer") {
     137         [ +  - ]:          80 :         float val = std::stof(value);
     138         [ -  + ]:          80 :         if (val > 2.0f) val /= 100.0f;
     139                 :          80 :         current_preset.understeer = (std::min)(2.0f, (std::max)(0.0f, val));
     140                 :          80 :         return true;
     141                 :             :     }
     142         [ +  + ]:        8374 :     if (key == "understeer_gamma") { current_preset.understeer_gamma = std::stof(value); return true; }
     143         [ +  + ]:        8295 :     if (key == "steering_shaft_gain") { current_preset.steering_shaft_gain = std::stof(value); return true; }
     144         [ +  + ]:        8215 :     if (key == "ingame_ffb_gain") { current_preset.ingame_ffb_gain = std::stof(value); return true; }
     145         [ +  + ]:        8135 :     if (key == "steering_shaft_smoothing") { current_preset.steering_shaft_smoothing = std::stof(value); return true; }
     146         [ +  + ]:        8055 :     if (key == "torque_source") { current_preset.torque_source = std::stoi(value); return true; }
     147         [ +  + ]:        7975 :     if (key == "steering_100hz_reconstruction") { current_preset.steering_100hz_reconstruction = std::stoi(value); return true; }
     148   [ +  +  +  +  :        7896 :     if (key == "torque_passthrough") { current_preset.torque_passthrough = (value == "1" || value == "true"); return true; }
                   -  + ]
     149         [ +  + ]:        7816 :     if (key == "sop") { current_preset.sop = std::stof(value); return true; }
     150         [ +  + ]:        7735 :     if (key == "lateral_load_effect") { current_preset.lateral_load = std::stof(value); return true; }
     151   [ +  +  +  -  :        7656 :     if (key == "lat_load_transform") { current_preset.lat_load_transform = std::clamp(std::stoi(value), 0, 3); return true; }
                   +  - ]
     152         [ +  + ]:        7577 :     if (key == "sop_scale") { current_preset.sop_scale = std::stof(value); return true; }
     153         [ +  + ]:        7497 :     if (key == "sop_smoothing_factor") { current_preset.sop_smoothing = std::stof(value); return true; }
     154         [ +  + ]:        7416 :     if (key == "oversteer_boost") { current_preset.oversteer_boost = std::stof(value); return true; }
     155   [ +  +  -  +  :        7336 :     if (key == "long_load_effect" || key == "dynamic_weight_gain") { current_preset.long_load_effect = std::stof(value); return true; }
                   +  + ]
     156   [ +  +  -  +  :        7257 :     if (key == "long_load_smoothing" || key == "dynamic_weight_smoothing") { current_preset.long_load_smoothing = std::stof(value); return true; }
                   +  + ]
     157   [ +  +  +  -  :        7178 :     if (key == "long_load_transform") { current_preset.long_load_transform = std::clamp(std::stoi(value), 0, 3); return true; }
                   +  - ]
     158         [ +  + ]:        7099 :     if (key == "grip_smoothing_steady") { current_preset.grip_smoothing_steady = std::stof(value); return true; }
     159         [ +  + ]:        7020 :     if (key == "grip_smoothing_fast") { current_preset.grip_smoothing_fast = std::stof(value); return true; }
     160         [ +  + ]:        6941 :     if (key == "grip_smoothing_sensitivity") { current_preset.grip_smoothing_sensitivity = std::stof(value); return true; }
     161         [ +  + ]:        6862 :     if (key == "rear_align_effect") { current_preset.rear_align_effect = std::stof(value); return true; }
     162         [ +  + ]:        6782 :     if (key == "kerb_strike_rejection") { current_preset.kerb_strike_rejection = std::stof(value); return true; }
     163         [ +  + ]:        6703 :     if (key == "sop_yaw_gain") { current_preset.sop_yaw_gain = std::stof(value); return true; }
     164         [ +  + ]:        6623 :     if (key == "yaw_kick_threshold") { current_preset.yaw_kick_threshold = std::stof(value); return true; }
     165         [ +  + ]:        6543 :     if (key == "unloaded_yaw_gain") { current_preset.unloaded_yaw_gain = std::stof(value); return true; }
     166         [ +  + ]:        6464 :     if (key == "unloaded_yaw_threshold") { current_preset.unloaded_yaw_threshold = std::stof(value); return true; }
     167         [ +  + ]:        6385 :     if (key == "unloaded_yaw_sens") { current_preset.unloaded_yaw_sens = std::stof(value); return true; }
     168         [ +  + ]:        6306 :     if (key == "unloaded_yaw_gamma") { current_preset.unloaded_yaw_gamma = std::stof(value); return true; }
     169         [ +  + ]:        6227 :     if (key == "unloaded_yaw_punch") { current_preset.unloaded_yaw_punch = std::stof(value); return true; }
     170         [ +  + ]:        6148 :     if (key == "power_yaw_gain") { current_preset.power_yaw_gain = std::stof(value); return true; }
     171         [ +  + ]:        6069 :     if (key == "power_yaw_threshold") { current_preset.power_yaw_threshold = std::stof(value); return true; }
     172         [ +  + ]:        5990 :     if (key == "power_slip_threshold") { current_preset.power_slip_threshold = std::stof(value); return true; }
     173         [ +  + ]:        5911 :     if (key == "power_yaw_gamma") { current_preset.power_yaw_gamma = std::stof(value); return true; }
     174         [ +  + ]:        5832 :     if (key == "power_yaw_punch") { current_preset.power_yaw_punch = std::stof(value); return true; }
     175         [ +  + ]:        5753 :     if (key == "yaw_accel_smoothing") { current_preset.yaw_smoothing = std::stof(value); return true; }
     176   [ +  +  +  - ]:        5673 :     if (key == "gyro_gain") { current_preset.gyro_gain = (std::min)(1.0f, std::stof(value)); return true; }
     177   [ +  +  +  - ]:        5593 :     if (key == "stationary_damping") { current_preset.stationary_damping = (std::min)(1.0f, std::stof(value)); return true; }
     178         [ +  + ]:        5514 :     if (key == "gyro_smoothing_factor") { current_preset.gyro_smoothing = std::stof(value); return true; }
     179         [ +  + ]:        5434 :     if (key == "optimal_slip_angle") { current_preset.optimal_slip_angle = std::stof(value); return true; }
     180         [ +  + ]:        5354 :     if (key == "optimal_slip_ratio") { current_preset.optimal_slip_ratio = std::stof(value); return true; }
     181         [ +  + ]:        5274 :     if (key == "slope_detection_enabled") { current_preset.slope_detection_enabled = (value == "1"); return true; }
     182         [ +  + ]:        5193 :     if (key == "slope_sg_window") { current_preset.slope_sg_window = std::stoi(value); return true; }
     183         [ +  + ]:        5113 :     if (key == "slope_sensitivity") { current_preset.slope_sensitivity = std::stof(value); return true; }
     184   [ +  +  +  +  :        5033 :     if (key == "slope_negative_threshold" || key == "slope_min_threshold") { current_preset.slope_min_threshold = std::stof(value); return true; }
                   +  + ]
     185         [ +  + ]:        4951 :     if (key == "slope_smoothing_tau") { current_preset.slope_smoothing_tau = std::stof(value); return true; }
     186         [ +  + ]:        4871 :     if (key == "slope_max_threshold") { current_preset.slope_max_threshold = std::stof(value); return true; }
     187         [ +  + ]:        4791 :     if (key == "slope_alpha_threshold") { current_preset.slope_alpha_threshold = std::stof(value); return true; }
     188         [ +  + ]:        4711 :     if (key == "slope_decay_rate") { current_preset.slope_decay_rate = std::stof(value); return true; }
     189         [ +  + ]:        4631 :     if (key == "slope_confidence_enabled") { current_preset.slope_confidence_enabled = (value == "1"); return true; }
     190         [ +  + ]:        4551 :     if (key == "slope_g_slew_limit") { current_preset.slope_g_slew_limit = std::stof(value); return true; }
     191         [ +  + ]:        4471 :     if (key == "slope_use_torque") { current_preset.slope_use_torque = (value == "1"); return true; }
     192         [ +  + ]:        4391 :     if (key == "slope_torque_sensitivity") { current_preset.slope_torque_sensitivity = std::stof(value); return true; }
     193         [ +  + ]:        4311 :     if (key == "slope_confidence_max_rate") { current_preset.slope_confidence_max_rate = std::stof(value); return true; }
     194         [ +  + ]:        4231 :     if (key == "slip_angle_smoothing") { current_preset.slip_smoothing = std::stof(value); return true; }
     195         [ +  + ]:        4151 :     if (key == "chassis_inertia_smoothing") { current_preset.chassis_smoothing = std::stof(value); return true; }
     196   [ +  +  -  +  :        4071 :     if (key == "load_sensitivity_enabled") { current_preset.load_sensitivity_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     197                 :        3992 :     return false;
     198                 :             : }
     199                 :             : 
     200                 :        3992 : bool Config::ParseBrakingLine(const std::string& key, const std::string& value, Preset& current_preset) {
     201   [ +  +  -  +  :        3992 :     if (key == "lockup_enabled") { current_preset.lockup_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     202         [ +  + ]:        3912 :     if (key == "lockup_gain") { current_preset.lockup_gain = std::stof(value); return true; }
     203         [ +  + ]:        3831 :     if (key == "lockup_start_pct") { current_preset.lockup_start_pct = std::stof(value); return true; }
     204         [ +  + ]:        3751 :     if (key == "lockup_full_pct") { current_preset.lockup_full_pct = std::stof(value); return true; }
     205         [ +  + ]:        3671 :     if (key == "lockup_rear_boost") { current_preset.lockup_rear_boost = std::stof(value); return true; }
     206         [ +  + ]:        3591 :     if (key == "lockup_gamma") { current_preset.lockup_gamma = std::stof(value); return true; }
     207         [ +  + ]:        3511 :     if (key == "lockup_prediction_sens") { current_preset.lockup_prediction_sens = std::stof(value); return true; }
     208         [ +  + ]:        3431 :     if (key == "lockup_bump_reject") { current_preset.lockup_bump_reject = std::stof(value); return true; }
     209   [ +  +  +  - ]:        3351 :     if (key == "brake_load_cap") { current_preset.brake_load_cap = (std::min)(10.0f, std::stof(value)); return true; }
     210         [ +  + ]:        3270 :     if (key == "abs_pulse_enabled") { current_preset.abs_pulse_enabled = std::stoi(value); return true; }
     211         [ +  + ]:        3190 :     if (key == "abs_gain") { current_preset.abs_gain = std::stof(value); return true; }
     212         [ +  + ]:        3110 :     if (key == "abs_freq") { current_preset.abs_freq = std::stof(value); return true; }
     213         [ +  + ]:        3030 :     if (key == "lockup_freq_scale") { current_preset.lockup_freq_scale = std::stof(value); return true; }
     214                 :        2950 :     return false;
     215                 :             : }
     216                 :             : 
     217                 :        2950 : bool Config::ParseVibrationLine(const std::string& key, const std::string& value, Preset& current_preset) {
     218   [ +  +  -  +  :        2950 :     if (key == "texture_load_cap" || key == "max_load_factor") { current_preset.texture_load_cap = std::stof(value); return true; }
                   +  + ]
     219   [ +  +  -  +  :        2870 :     if (key == "spin_enabled") { current_preset.spin_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     220         [ +  + ]:        2790 :     if (key == "spin_gain") { current_preset.spin_gain = std::stof(value); return true; }
     221         [ +  + ]:        2710 :     if (key == "spin_freq_scale") { current_preset.spin_freq_scale = std::stof(value); return true; }
     222   [ +  +  +  +  :        2630 :     if (key == "slide_enabled") { current_preset.slide_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     223         [ +  + ]:        2550 :     if (key == "slide_gain") { current_preset.slide_gain = std::stof(value); return true; }
     224         [ +  + ]:        2470 :     if (key == "slide_freq") { current_preset.slide_freq = std::stof(value); return true; }
     225   [ +  +  -  +  :        2390 :     if (key == "road_enabled") { current_preset.road_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     226         [ +  + ]:        2310 :     if (key == "road_gain") { current_preset.road_gain = std::stof(value); return true; }
     227   [ +  +  -  +  :        2230 :     if (key == "vibration_gain" || key == "tactile_gain") { current_preset.vibration_gain = std::stof(value); return true; }
                   +  + ]
     228         [ +  + ]:        2151 :     if (key == "scrub_drag_gain") { current_preset.scrub_drag_gain = std::stof(value); return true; }
     229   [ +  +  -  +  :        2071 :     if (key == "bottoming_enabled") { current_preset.bottoming_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     230         [ +  + ]:        1992 :     if (key == "bottoming_gain") { current_preset.bottoming_gain = std::stof(value); return true; }
     231         [ +  + ]:        1913 :     if (key == "bottoming_method") { current_preset.bottoming_method = std::stoi(value); return true; }
     232   [ +  +  +  -  :        1833 :     if (key == "dynamic_normalization_enabled") { current_preset.general.dynamic_normalization_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     233   [ +  +  +  -  :        1754 :     if (key == "auto_load_normalization_enabled") { current_preset.general.auto_load_normalization_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     234   [ +  +  -  +  :        1675 :     if (key == "soft_lock_enabled") { current_preset.soft_lock_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     235         [ +  + ]:        1595 :     if (key == "soft_lock_stiffness") { current_preset.soft_lock_stiffness = std::stof(value); return true; }
     236         [ +  + ]:        1515 :     if (key == "soft_lock_damping") { current_preset.soft_lock_damping = std::stof(value); return true; }
     237   [ +  +  +  +  :        1435 :     if (key == "flatspot_suppression") { current_preset.flatspot_suppression = (value == "1" || value == "true"); return true; }
                   -  + ]
     238         [ +  + ]:        1355 :     if (key == "notch_q") { current_preset.notch_q = std::stof(value); return true; }
     239         [ +  + ]:        1275 :     if (key == "flatspot_strength") { current_preset.flatspot_strength = std::stof(value); return true; }
     240   [ +  +  +  +  :        1195 :     if (key == "static_notch_enabled") { current_preset.static_notch_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     241         [ +  + ]:        1115 :     if (key == "static_notch_freq") { current_preset.static_notch_freq = std::stof(value); return true; }
     242         [ +  + ]:        1035 :     if (key == "static_notch_width") { current_preset.static_notch_width = std::stof(value); return true; }
     243         [ +  + ]:         955 :     if (key == "speed_gate_lower") { current_preset.speed_gate_lower = std::stof(value); return true; }
     244         [ +  + ]:         875 :     if (key == "speed_gate_upper") { current_preset.speed_gate_upper = std::stof(value); return true; }
     245         [ +  + ]:         795 :     if (key == "road_fallback_scale") { current_preset.road_fallback_scale = std::stof(value); return true; }
     246         [ +  + ]:         715 :     if (key == "understeer_affects_sop") { current_preset.understeer_affects_sop = std::stoi(value); return true; }
     247                 :         635 :     return false;
     248                 :             : }
     249                 :             : 
     250                 :         635 : bool Config::ParseSafetyLine(const std::string& key, const std::string& value, Preset& current_preset) {
     251         [ +  + ]:         635 :     if (key == "safety_window_duration") { current_preset.safety_window_duration = std::stof(value); return true; }
     252         [ +  + ]:         556 :     if (key == "safety_gain_reduction") { current_preset.safety_gain_reduction = std::stof(value); return true; }
     253         [ +  + ]:         477 :     if (key == "safety_smoothing_tau") { current_preset.safety_smoothing_tau = std::stof(value); return true; }
     254         [ +  + ]:         398 :     if (key == "spike_detection_threshold") { current_preset.spike_detection_threshold = std::stof(value); return true; }
     255         [ +  + ]:         319 :     if (key == "immediate_spike_threshold") { current_preset.immediate_spike_threshold = std::stof(value); return true; }
     256         [ +  + ]:         240 :     if (key == "safety_slew_full_scale_time_s") { current_preset.safety_slew_full_scale_time_s = std::stof(value); return true; }
     257   [ +  +  +  -  :         161 :     if (key == "stutter_safety_enabled") { current_preset.stutter_safety_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     258         [ +  + ]:          82 :     if (key == "stutter_threshold") { current_preset.stutter_threshold = std::stof(value); return true; }
     259                 :           3 :     return false;
     260                 :             : }
     261                 :             : 
     262                 :        4078 : bool Config::SyncSystemLine(const std::string& key, const std::string& value, FFBEngine& engine, std::string& config_version, bool& legacy_torque_hack, float& legacy_torque_val, bool& needs_save) {
     263   [ +  +  -  +  :        4078 :     if (key == "always_on_top") { m_always_on_top = (value == "1" || value == "true"); return true; }
                   -  - ]
     264         [ +  + ]:        4046 :     if (key == "last_device_guid") { m_last_device_guid = value; return true; }
     265         [ +  + ]:        4027 :     if (key == "last_preset_name") { m_last_preset_name = value; return true; }
     266         [ +  + ]:        3994 :     if (key == "win_pos_x") { win_pos_x = std::stoi(value); return true; }
     267         [ +  + ]:        3964 :     if (key == "win_pos_y") { win_pos_y = std::stoi(value); return true; }
     268         [ +  + ]:        3934 :     if (key == "win_w_small") { win_w_small = std::stoi(value); return true; }
     269         [ +  + ]:        3904 :     if (key == "win_h_small") { win_h_small = std::stoi(value); return true; }
     270         [ +  + ]:        3874 :     if (key == "win_w_large") { win_w_large = std::stoi(value); return true; }
     271         [ +  + ]:        3844 :     if (key == "win_h_large") { win_h_large = std::stoi(value); return true; }
     272   [ +  +  +  +  :        3814 :     if (key == "show_graphs") { show_graphs = (value == "1" || value == "true"); return true; }
                   -  + ]
     273   [ +  +  +  +  :        3783 :     if (key == "auto_start_logging") { m_auto_start_logging = (value == "1" || value == "true"); return true; }
                   -  + ]
     274         [ +  + ]:        3752 :     if (key == "log_path") { m_log_path = value; return true; }
     275   [ +  +  +  +  :        3721 :     if (key == "invert_force") { engine.m_invert_force = (value == "1" || value == "true"); return true; }
                   -  + ]
     276         [ +  + ]:        3689 :     if (key == "gain") { engine.m_general.gain = std::stof(value); return true; }
     277         [ +  + ]:        3653 :     if (key == "wheelbase_max_nm") { engine.m_general.wheelbase_max_nm = std::stof(value); return true; }
     278         [ +  + ]:        3621 :     if (key == "target_rim_nm") { engine.m_general.target_rim_nm = std::stof(value); return true; }
     279         [ +  + ]:        3589 :     if (key == "min_force") { engine.m_general.min_force = std::stof(value); return true; }
     280         [ +  + ]:        3557 :     if (key == "max_torque_ref") {
     281                 :          12 :         float old_val = std::stof(value);
     282         [ +  + ]:          12 :         if (old_val > 40.0f) {
     283                 :           6 :             engine.m_general.wheelbase_max_nm = 15.0f;
     284                 :           6 :             engine.m_general.target_rim_nm = 10.0f;
     285                 :           6 :             legacy_torque_hack = true;
     286                 :           6 :             legacy_torque_val = old_val;
     287                 :             :         } else {
     288                 :           6 :             engine.m_general.wheelbase_max_nm = old_val;
     289                 :           6 :             engine.m_general.target_rim_nm = old_val;
     290                 :             :         }
     291                 :          12 :         needs_save = true;
     292                 :          12 :         return true;
     293                 :             :     }
     294   [ +  +  +  -  :        3545 :     if (key == "rest_api_fallback_enabled") { engine.m_rest_api_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     295         [ +  + ]:        3517 :     if (key == "rest_api_port") { engine.m_rest_api_port = std::stoi(value); return true; }
     296                 :        3489 :     return false;
     297                 :             : }
     298                 :             : 
     299                 :        3489 : bool Config::SyncPhysicsLine(const std::string& key, const std::string& value, FFBEngine& engine, std::string& config_version, bool& needs_save) {
     300         [ +  + ]:        3489 :     if (key == "understeer") {
     301         [ +  + ]:          37 :         float val = std::stof(value);
     302         [ +  + ]:          36 :         if (val > 2.0f) val /= 100.0f;
     303                 :          36 :         engine.m_understeer_effect = (std::min)(2.0f, (std::max)(0.0f, val));
     304                 :          36 :         return true;
     305                 :             :     }
     306         [ +  + ]:        3452 :     if (key == "understeer_gamma") { engine.m_understeer_gamma = std::stof(value); return true; }
     307         [ +  + ]:        3424 :     if (key == "steering_shaft_gain") { engine.m_steering_shaft_gain = std::stof(value); return true; }
     308         [ +  + ]:        3392 :     if (key == "ingame_ffb_gain") { engine.m_ingame_ffb_gain = std::stof(value); return true; }
     309         [ +  + ]:        3360 :     if (key == "steering_shaft_smoothing") { engine.m_steering_shaft_smoothing = std::stof(value); return true; }
     310         [ +  + ]:        3328 :     if (key == "torque_source") { engine.m_torque_source = std::stoi(value); return true; }
     311         [ +  + ]:        3296 :     if (key == "steering_100hz_reconstruction") { engine.m_steering_100hz_reconstruction = std::stoi(value); return true; }
     312   [ +  +  +  +  :        3268 :     if (key == "torque_passthrough") { engine.m_torque_passthrough = (value == "1" || value == "true"); return true; }
                   +  + ]
     313         [ +  + ]:        3236 :     if (key == "sop") { engine.m_sop_effect = std::stof(value); return true; }
     314         [ +  + ]:        3203 :     if (key == "lateral_load_effect") { engine.m_lat_load_effect = std::stof(value); return true; }
     315   [ +  +  +  -  :        3175 :     if (key == "lat_load_transform") { engine.m_lat_load_transform = static_cast<LoadTransform>(std::clamp(std::stoi(value), 0, 3)); return true; }
                   +  - ]
     316         [ +  + ]:        3147 :     if (key == "sop_scale") { engine.m_sop_scale = std::stof(value); return true; }
     317   [ +  +  +  +  :        3115 :     if (key == "sop_smoothing_factor" || key == "smoothing") {
                   +  + ]
     318                 :          36 :         float val = std::stof(value);
     319   [ +  -  +  -  :          72 :         if (IsVersionLessEqual(config_version, "0.7.146")) {
                   +  + ]
     320                 :           5 :             val = 0.0f;
     321                 :           5 :             needs_save = true;
     322                 :             :         }
     323                 :          36 :         engine.m_sop_smoothing_factor = val;
     324                 :          36 :         return true;
     325                 :             :     }
     326         [ +  + ]:        3079 :     if (key == "oversteer_boost") { engine.m_oversteer_boost = std::stof(value); return true; }
     327   [ +  +  -  +  :        3047 :     if (key == "long_load_effect" || key == "dynamic_weight_gain") { engine.m_long_load_effect = std::stof(value); return true; }
                   +  + ]
     328   [ +  +  -  +  :        3016 :     if (key == "long_load_smoothing" || key == "dynamic_weight_smoothing") { engine.m_long_load_smoothing = std::stof(value); return true; }
                   +  + ]
     329   [ +  +  +  -  :        2985 :     if (key == "long_load_transform") { engine.m_long_load_transform = static_cast<LoadTransform>(std::clamp(std::stoi(value), 0, 3)); return true; }
                   +  - ]
     330         [ +  + ]:        2957 :     if (key == "grip_smoothing_steady") { engine.m_grip_smoothing_steady = std::stof(value); return true; }
     331         [ +  + ]:        2926 :     if (key == "grip_smoothing_fast") { engine.m_grip_smoothing_fast = std::stof(value); return true; }
     332         [ +  + ]:        2895 :     if (key == "grip_smoothing_sensitivity") { engine.m_grip_smoothing_sensitivity = std::stof(value); return true; }
     333         [ +  + ]:        2864 :     if (key == "rear_align_effect") { engine.m_rear_align_effect = std::stof(value); return true; }
     334         [ +  + ]:        2831 :     if (key == "kerb_strike_rejection") { engine.m_kerb_strike_rejection = std::stof(value); return true; }
     335         [ +  + ]:        2803 :     if (key == "sop_yaw_gain") { engine.m_sop_yaw_gain = std::stof(value); return true; }
     336         [ +  + ]:        2770 :     if (key == "yaw_kick_threshold") { engine.m_yaw_kick_threshold = std::stof(value); return true; }
     337         [ +  + ]:        2738 :     if (key == "unloaded_yaw_gain") { engine.m_unloaded_yaw_gain = std::stof(value); return true; }
     338         [ +  + ]:        2710 :     if (key == "unloaded_yaw_threshold") { engine.m_unloaded_yaw_threshold = std::stof(value); return true; }
     339         [ +  + ]:        2682 :     if (key == "unloaded_yaw_sens") { engine.m_unloaded_yaw_sens = std::stof(value); return true; }
     340         [ +  + ]:        2654 :     if (key == "unloaded_yaw_gamma") { engine.m_unloaded_yaw_gamma = std::stof(value); return true; }
     341         [ +  + ]:        2626 :     if (key == "unloaded_yaw_punch") { engine.m_unloaded_yaw_punch = std::stof(value); return true; }
     342         [ +  + ]:        2598 :     if (key == "power_yaw_gain") { engine.m_power_yaw_gain = std::stof(value); return true; }
     343         [ +  + ]:        2570 :     if (key == "power_yaw_threshold") { engine.m_power_yaw_threshold = std::stof(value); return true; }
     344         [ +  + ]:        2542 :     if (key == "power_slip_threshold") { engine.m_power_slip_threshold = std::stof(value); return true; }
     345         [ +  + ]:        2514 :     if (key == "power_yaw_gamma") { engine.m_power_yaw_gamma = std::stof(value); return true; }
     346         [ +  + ]:        2486 :     if (key == "power_yaw_punch") { engine.m_power_yaw_punch = std::stof(value); return true; }
     347         [ +  + ]:        2458 :     if (key == "yaw_accel_smoothing") { engine.m_yaw_accel_smoothing = std::stof(value); return true; }
     348   [ +  +  +  - ]:        2426 :     if (key == "gyro_gain") { engine.m_gyro_gain = (std::min)(1.0f, std::stof(value)); return true; }
     349   [ +  +  +  - ]:        2393 :     if (key == "stationary_damping") { engine.m_stationary_damping = (std::min)(1.0f, std::stof(value)); return true; }
     350         [ +  + ]:        2365 :     if (key == "gyro_smoothing_factor") { engine.m_gyro_smoothing = std::stof(value); return true; }
     351         [ +  + ]:        2333 :     if (key == "optimal_slip_angle") { engine.m_optimal_slip_angle = std::stof(value); return true; }
     352         [ +  + ]:        2300 :     if (key == "optimal_slip_ratio") { engine.m_optimal_slip_ratio = std::stof(value); return true; }
     353         [ +  + ]:        2269 :     if (key == "slope_detection_enabled") { engine.m_slope_detection_enabled = (value == "1"); return true; }
     354         [ +  + ]:        2236 :     if (key == "slope_sg_window") { engine.m_slope_sg_window = std::stoi(value); return true; }
     355         [ +  + ]:        2201 :     if (key == "slope_sensitivity") { engine.m_slope_sensitivity = std::stof(value); return true; }
     356   [ +  +  +  +  :        2167 :     if (key == "slope_negative_threshold" || key == "slope_min_threshold") { engine.m_slope_min_threshold = std::stof(value); return true; }
                   +  + ]
     357         [ +  + ]:        2130 :     if (key == "slope_smoothing_tau") { engine.m_slope_smoothing_tau = std::stof(value); return true; }
     358         [ +  + ]:        2099 :     if (key == "slope_max_threshold") { engine.m_slope_max_threshold = std::stof(value); return true; }
     359         [ +  + ]:        2065 :     if (key == "slope_alpha_threshold") { engine.m_slope_alpha_threshold = std::stof(value); return true; }
     360         [ +  + ]:        2033 :     if (key == "slope_decay_rate") { engine.m_slope_decay_rate = std::stof(value); return true; }
     361         [ +  + ]:        2001 :     if (key == "slope_confidence_enabled") { engine.m_slope_confidence_enabled = (value == "1"); return true; }
     362         [ +  + ]:        1969 :     if (key == "slope_g_slew_limit") { engine.m_slope_g_slew_limit = std::stof(value); return true; }
     363         [ +  + ]:        1937 :     if (key == "slope_use_torque") { engine.m_slope_use_torque = (value == "1"); return true; }
     364         [ +  + ]:        1905 :     if (key == "slope_torque_sensitivity") { engine.m_slope_torque_sensitivity = std::stof(value); return true; }
     365         [ +  + ]:        1873 :     if (key == "slope_confidence_max_rate") { engine.m_slope_confidence_max_rate = std::stof(value); return true; }
     366         [ +  + ]:        1841 :     if (key == "slip_angle_smoothing") { engine.m_slip_angle_smoothing = std::stof(value); return true; }
     367         [ +  + ]:        1811 :     if (key == "chassis_inertia_smoothing") { engine.m_chassis_inertia_smoothing = std::stof(value); return true; }
     368   [ +  +  -  +  :        1779 :     if (key == "load_sensitivity_enabled") { engine.m_load_sensitivity_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     369         [ +  + ]:        1751 :     if (key == "speed_gate_lower") { engine.m_speed_gate_lower = std::stof(value); return true; }
     370         [ +  + ]:        1719 :     if (key == "speed_gate_upper") { engine.m_speed_gate_upper = std::stof(value); return true; }
     371         [ +  + ]:        1687 :     if (key == "road_fallback_scale") { engine.m_road_fallback_scale = std::stof(value); return true; }
     372         [ +  + ]:        1655 :     if (key == "understeer_affects_sop") { engine.m_understeer_affects_sop = std::stoi(value); return true; }
     373                 :        1623 :     return false;
     374                 :             : }
     375                 :             : 
     376                 :        1623 : bool Config::SyncBrakingLine(const std::string& key, const std::string& value, FFBEngine& engine) {
     377   [ +  +  +  +  :        1623 :     if (key == "lockup_enabled") { engine.m_lockup_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     378         [ +  + ]:        1591 :     if (key == "lockup_gain") { engine.m_lockup_gain = std::stof(value); return true; }
     379         [ +  + ]:        1556 :     if (key == "lockup_start_pct") { engine.m_lockup_start_pct = std::stof(value); return true; }
     380         [ +  + ]:        1524 :     if (key == "lockup_full_pct") { engine.m_lockup_full_pct = std::stof(value); return true; }
     381         [ +  + ]:        1492 :     if (key == "lockup_rear_boost") { engine.m_lockup_rear_boost = std::stof(value); return true; }
     382         [ +  + ]:        1460 :     if (key == "lockup_gamma") { engine.m_lockup_gamma = std::stof(value); return true; }
     383         [ +  + ]:        1427 :     if (key == "lockup_prediction_sens") { engine.m_lockup_prediction_sens = std::stof(value); return true; }
     384         [ +  + ]:        1395 :     if (key == "lockup_bump_reject") { engine.m_lockup_bump_reject = std::stof(value); return true; }
     385         [ +  + ]:        1363 :     if (key == "brake_load_cap") { engine.m_brake_load_cap = std::stof(value); return true; }
     386   [ +  +  +  +  :        1329 :     if (key == "abs_pulse_enabled") { engine.m_abs_pulse_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     387         [ +  + ]:        1297 :     if (key == "abs_gain") { engine.m_abs_gain = std::stof(value); return true; }
     388         [ +  + ]:        1265 :     if (key == "abs_freq") { engine.m_abs_freq_hz = std::stof(value); return true; }
     389         [ +  + ]:        1233 :     if (key == "lockup_freq_scale") { engine.m_lockup_freq_scale = std::stof(value); return true; }
     390                 :        1202 :     return false;
     391                 :             : }
     392                 :             : 
     393                 :        1202 : bool Config::SyncVibrationLine(const std::string& key, const std::string& value, FFBEngine& engine) {
     394   [ +  +  +  +  :        1202 :     if (key == "texture_load_cap" || key == "max_load_factor") { engine.m_texture_load_cap = std::stof(value); return true; }
                   +  + ]
     395   [ +  +  +  +  :        1166 :     if (key == "spin_enabled") { engine.m_spin_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     396         [ +  + ]:        1134 :     if (key == "spin_gain") { engine.m_spin_gain = std::stof(value); return true; }
     397         [ +  + ]:        1101 :     if (key == "spin_freq_scale") { engine.m_spin_freq_scale = std::stof(value); return true; }
     398   [ +  +  +  +  :        1069 :     if (key == "slide_enabled") { engine.m_slide_texture_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     399         [ +  + ]:        1037 :     if (key == "slide_gain") { engine.m_slide_texture_gain = std::stof(value); return true; }
     400         [ +  + ]:        1004 :     if (key == "slide_freq") { engine.m_slide_freq_scale = std::stof(value); return true; }
     401   [ +  +  +  +  :         972 :     if (key == "road_enabled") { engine.m_road_texture_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     402         [ +  + ]:         940 :     if (key == "road_gain") { engine.m_road_texture_gain = std::stof(value); return true; }
     403   [ +  +  -  +  :         907 :     if (key == "vibration_gain" || key == "tactile_gain") { engine.m_vibration_gain = std::stof(value); return true; }
                   +  + ]
     404   [ +  +  +  +  :         879 :     if (key == "bottoming_enabled") { engine.m_bottoming_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     405         [ +  + ]:         851 :     if (key == "bottoming_gain") { engine.m_bottoming_gain = std::stof(value); return true; }
     406   [ +  +  +  +  :         823 :     if (key == "dynamic_normalization_enabled") { engine.m_general.dynamic_normalization_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     407   [ +  +  +  -  :         795 :     if (key == "auto_load_normalization_enabled") { engine.m_general.auto_load_normalization_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     408   [ +  +  -  +  :         767 :     if (key == "soft_lock_enabled") { engine.m_soft_lock_enabled = (value == "1" || value == "true"); return true; }
                   -  - ]
     409         [ +  + ]:         735 :     if (key == "soft_lock_stiffness") { engine.m_soft_lock_stiffness = std::stof(value); return true; }
     410         [ +  + ]:         703 :     if (key == "soft_lock_damping") { engine.m_soft_lock_damping = std::stof(value); return true; }
     411   [ +  +  +  +  :         671 :     if (key == "flatspot_suppression") { engine.m_flatspot_suppression = (value == "1" || value == "true"); return true; }
                   -  + ]
     412         [ +  + ]:         639 :     if (key == "notch_q") { engine.m_notch_q = std::stof(value); return true; }
     413         [ +  + ]:         607 :     if (key == "flatspot_strength") { engine.m_flatspot_strength = std::stof(value); return true; }
     414   [ +  +  +  +  :         575 :     if (key == "static_notch_enabled") { engine.m_static_notch_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     415         [ +  + ]:         543 :     if (key == "static_notch_freq") { engine.m_static_notch_freq = std::stof(value); return true; }
     416         [ +  + ]:         511 :     if (key == "static_notch_width") { engine.m_static_notch_width = std::stof(value); return true; }
     417         [ +  + ]:         479 :     if (key == "scrub_drag_gain") { engine.m_scrub_drag_gain = std::stof(value); return true; }
     418         [ +  + ]:         446 :     if (key == "bottoming_method") { engine.m_bottoming_method = std::stoi(value); return true; }
     419                 :         414 :     return false;
     420                 :             : }
     421                 :             : 
     422                 :         414 : bool Config::SyncSafetyLine(const std::string& key, const std::string& value, FFBEngine& engine) {
     423         [ +  + ]:         414 :     if (key == "safety_window_duration") { engine.m_safety.m_safety_window_duration = std::stof(value); return true; }
     424         [ +  + ]:         386 :     if (key == "safety_gain_reduction") { engine.m_safety.m_safety_gain_reduction = std::stof(value); return true; }
     425         [ +  + ]:         358 :     if (key == "safety_smoothing_tau") { engine.m_safety.m_safety_smoothing_tau = std::stof(value); return true; }
     426         [ +  + ]:         330 :     if (key == "spike_detection_threshold") { engine.m_safety.m_spike_detection_threshold = std::stof(value); return true; }
     427         [ +  + ]:         302 :     if (key == "immediate_spike_threshold") { engine.m_safety.m_immediate_spike_threshold = std::stof(value); return true; }
     428         [ +  + ]:         274 :     if (key == "safety_slew_full_scale_time_s") { engine.m_safety.m_safety_slew_full_scale_time_s = std::stof(value); return true; }
     429   [ +  +  +  -  :         246 :     if (key == "stutter_safety_enabled") { engine.m_safety.m_stutter_safety_enabled = (value == "1" || value == "true"); return true; }
                   -  + ]
     430         [ +  + ]:         218 :     if (key == "stutter_threshold") { engine.m_safety.m_stutter_threshold = std::stof(value); return true; }
     431                 :         190 :     return false;
     432                 :             : }
     433                 :             : 
     434                 :        9025 : void Config::ParsePresetLine(const std::string& line, Preset& current_preset, std::string& current_preset_version, bool& needs_save, bool& legacy_torque_hack, float& legacy_torque_val) {
     435         [ +  - ]:        9025 :     std::istringstream is_line(line);
     436                 :        9025 :     std::string key;
     437   [ +  -  +  -  :        9025 :     if (std::getline(is_line, key, '=')) {
                   +  - ]
     438                 :        9025 :         std::string value;
     439   [ +  -  +  -  :        9025 :         if (std::getline(is_line, value)) {
                   +  - ]
     440                 :             :             try {
     441   [ +  -  +  + ]:        9025 :                 if (ParseSystemLine(key, value, current_preset, current_preset_version, needs_save, legacy_torque_hack, legacy_torque_val)) return;
     442   [ +  -  +  + ]:        8454 :                 if (ParsePhysicsLine(key, value, current_preset)) return;
     443   [ +  -  +  + ]:        3992 :                 if (ParseBrakingLine(key, value, current_preset)) return;
     444   [ +  -  +  + ]:        2950 :                 if (ParseVibrationLine(key, value, current_preset)) return;
     445   [ +  -  +  + ]:         635 :                 if (ParseSafetyLine(key, value, current_preset)) return;
     446   [ -  -  -  -  :           0 :             } catch (...) { Logger::Get().Log("[Config] ParsePresetLine Error."); }
                   -  - ]
     447                 :             :         }
     448         [ +  + ]:        9025 :     }
     449   [ +  +  +  + ]:       18047 : }
     450                 :             : 
     451                 :          97 : void Config::LoadPresets() {
     452                 :          97 :     presets.clear();
     453                 :             :     
     454                 :             :     // 1. Default - Uses Preset struct defaults from Config.h (Single Source of Truth)
     455   [ +  -  +  -  :         194 :     presets.push_back(Preset("Default", true));
                   +  - ]
     456                 :             :     
     457                 :             :     // 2. T300 (Custom optimized)
     458                 :             :     {
     459   [ +  -  +  - ]:          97 :         Preset p("T300", true);
     460                 :          97 :         p.general.gain = 1.0f;
     461                 :          97 :         p.general.wheelbase_max_nm = 4.0f;
     462                 :          97 :         p.general.target_rim_nm = 4.0f;
     463                 :          97 :         p.general.min_force = 0.01f;
     464                 :          97 :         p.steering_shaft_gain = 1.0f;
     465                 :          97 :         p.steering_shaft_smoothing = 0.0f;
     466                 :          97 :         p.understeer = 0.5f;
     467                 :          97 :         p.flatspot_suppression = false;
     468                 :          97 :         p.notch_q = 2.0f;
     469                 :          97 :         p.flatspot_strength = 1.0f;
     470                 :          97 :         p.static_notch_enabled = false;
     471                 :          97 :         p.static_notch_freq = 11.0f;
     472                 :          97 :         p.static_notch_width = 2.0f;
     473                 :          97 :         p.oversteer_boost = 2.40336f;
     474                 :          97 :         p.sop = 0.425003f;
     475                 :          97 :         p.rear_align_effect = 0.966383f;
     476                 :          97 :         p.sop_yaw_gain = 0.386555f;
     477                 :          97 :         p.yaw_kick_threshold = 1.68f;
     478                 :          97 :         p.yaw_smoothing = 0.005f;
     479                 :          97 :         p.gyro_gain = 0.0336134f;
     480                 :          97 :         p.gyro_smoothing = 0.0f;
     481                 :          97 :         p.sop_smoothing = 0.0f;
     482                 :          97 :         p.sop_scale = 1.0f;
     483                 :          97 :         p.understeer_affects_sop = false;
     484                 :          97 :         p.slip_smoothing = 0.0f;
     485                 :          97 :         p.chassis_smoothing = 0.0f;
     486                 :          97 :         p.optimal_slip_angle = 0.10f;   // CHANGED from 0.06f
     487                 :          97 :         p.optimal_slip_ratio = 0.12f;
     488                 :          97 :         p.lockup_enabled = true;
     489                 :          97 :         p.lockup_gain = 2.0f;
     490                 :          97 :         p.brake_load_cap = 10.0f;
     491                 :          97 :         p.lockup_freq_scale = 1.02f;
     492                 :          97 :         p.lockup_gamma = 0.1f;
     493                 :          97 :         p.lockup_start_pct = 1.0f;
     494                 :          97 :         p.lockup_full_pct = 5.0f;
     495                 :          97 :         p.lockup_prediction_sens = 10.0f;
     496                 :          97 :         p.lockup_bump_reject = 0.1f;
     497                 :          97 :         p.lockup_rear_boost = 10.0f;
     498                 :          97 :         p.abs_pulse_enabled = true;
     499                 :          97 :         p.abs_gain = 2.0f;
     500                 :          97 :         p.abs_freq = 20.0f;
     501                 :          97 :         p.texture_load_cap = 1.96f;
     502                 :          97 :         p.slide_enabled = true;
     503                 :          97 :         p.slide_gain = 0.235294f;
     504                 :          97 :         p.slide_freq = 1.0f;
     505                 :          97 :         p.road_enabled = true;
     506                 :          97 :         p.road_gain = 2.0f;
     507                 :          97 :         p.road_fallback_scale = 0.05f;
     508                 :          97 :         p.spin_enabled = true;
     509                 :          97 :         p.spin_gain = 0.5f;
     510                 :          97 :         p.spin_freq_scale = 1.0f;
     511                 :          97 :         p.scrub_drag_gain = 0.0462185f;
     512                 :          97 :         p.bottoming_enabled = true;
     513                 :          97 :         p.bottoming_gain = 1.0f;
     514                 :          97 :         p.bottoming_method = 0;
     515                 :          97 :         p.speed_gate_lower = 0.0f;
     516                 :          97 :         p.speed_gate_upper = 0.277778f;
     517         [ +  - ]:          97 :         presets.push_back(p);
     518                 :          97 :     }
     519                 :             :     
     520                 :             :     // 3. T300 v0.7.164 (Issue #322)
     521                 :             :     {
     522   [ +  -  +  - ]:          97 :         Preset p("T300 v0.7.164", true);
     523         [ +  - ]:          97 :         p.app_version = "0.7.165";
     524                 :          97 :         p.general.gain = 0.559471f;
     525                 :          97 :         p.general.wheelbase_max_nm = 25.1f;
     526                 :          97 :         p.general.target_rim_nm = 24.5f;
     527                 :          97 :         p.general.min_force = 0.02f;
     528                 :          97 :         p.steering_shaft_gain = 0.955947f;
     529                 :          97 :         p.ingame_ffb_gain = 1.0f;
     530                 :          97 :         p.steering_shaft_smoothing = 0.0f;
     531                 :          97 :         p.understeer = 1.0f;
     532                 :          97 :         p.torque_source = 0;
     533                 :          97 :         p.torque_passthrough = false;
     534                 :          97 :         p.flatspot_suppression = false;
     535                 :          97 :         p.notch_q = 2.0f;
     536                 :          97 :         p.flatspot_strength = 1.0f;
     537                 :          97 :         p.static_notch_enabled = false;
     538                 :          97 :         p.static_notch_freq = 11.0f;
     539                 :          97 :         p.static_notch_width = 2.0f;
     540                 :          97 :         p.oversteer_boost = 0.0f;
     541                 :          97 :         p.long_load_effect = 2.68722f;
     542                 :          97 :         p.long_load_smoothing = 0.15f;
     543                 :          97 :         p.long_load_transform = 0;
     544                 :          97 :         p.grip_smoothing_steady = 0.05f;
     545                 :          97 :         p.grip_smoothing_fast = 0.005f;
     546                 :          97 :         p.grip_smoothing_sensitivity = 0.1f;
     547                 :          97 :         p.sop = 0.0f;
     548                 :          97 :         p.lateral_load = 2.81938f;
     549                 :          97 :         p.lat_load_transform = 2;
     550                 :          97 :         p.rear_align_effect = 0.828194f;
     551                 :          97 :         p.sop_yaw_gain = 0.418502f;
     552                 :          97 :         p.yaw_kick_threshold = 1.01f;
     553                 :          97 :         p.unloaded_yaw_gain = 1.0f;
     554                 :          97 :         p.unloaded_yaw_threshold = 0.0f;
     555                 :          97 :         p.unloaded_yaw_sens = 0.1f;
     556                 :          97 :         p.unloaded_yaw_gamma = 0.1f;
     557                 :          97 :         p.unloaded_yaw_punch = 0.0f;
     558                 :          97 :         p.power_yaw_gain = 1.0f;
     559                 :          97 :         p.power_yaw_threshold = 0.0f;
     560                 :          97 :         p.power_slip_threshold = 0.0618062f;
     561                 :          97 :         p.power_yaw_gamma = 0.2f;
     562                 :          97 :         p.power_yaw_punch = 0.0f;
     563                 :          97 :         p.yaw_smoothing = 0.001f;
     564                 :          97 :         p.gyro_gain = 0.0f;
     565                 :          97 :         p.gyro_smoothing = 0.0f;
     566                 :          97 :         p.sop_smoothing = 0.0f;
     567                 :          97 :         p.sop_scale = 1.0f;
     568                 :          97 :         p.understeer_affects_sop = false;
     569                 :          97 :         p.slope_detection_enabled = false;
     570                 :          97 :         p.slope_sg_window = 15;
     571                 :          97 :         p.slope_sensitivity = 0.5f;
     572                 :          97 :         p.slope_smoothing_tau = 0.04f;
     573                 :          97 :         p.slope_min_threshold = -0.3f;
     574                 :          97 :         p.slope_max_threshold = -2.0f;
     575                 :          97 :         p.slope_alpha_threshold = 0.02f;
     576                 :          97 :         p.slope_decay_rate = 5.0f;
     577                 :          97 :         p.slope_confidence_enabled = true;
     578                 :          97 :         p.slope_g_slew_limit = 50.0f;
     579                 :          97 :         p.slope_use_torque = true;
     580                 :          97 :         p.slope_torque_sensitivity = 0.5f;
     581                 :          97 :         p.slope_confidence_max_rate = 0.1f;
     582                 :          97 :         p.slip_smoothing = 0.002f;
     583                 :          97 :         p.chassis_smoothing = 0.0f;
     584                 :          97 :         p.optimal_slip_angle = 0.1f;
     585                 :          97 :         p.optimal_slip_ratio = 0.12f;
     586                 :          97 :         p.lockup_enabled = true;
     587                 :          97 :         p.lockup_gain = 3.0f;
     588                 :          97 :         p.brake_load_cap = 10.0f;
     589                 :          97 :         p.lockup_freq_scale = 1.02f;
     590                 :          97 :         p.lockup_gamma = 0.1f;
     591                 :          97 :         p.lockup_start_pct = 1.0f;
     592                 :          97 :         p.lockup_full_pct = 5.0f;
     593                 :          97 :         p.lockup_prediction_sens = 10.0f;
     594                 :          97 :         p.lockup_bump_reject = 0.1f;
     595                 :          97 :         p.lockup_rear_boost = 10.0f;
     596                 :          97 :         p.abs_pulse_enabled = false;
     597                 :          97 :         p.abs_gain = 2.0f;
     598                 :          97 :         p.abs_freq = 25.5f;
     599                 :          97 :         p.texture_load_cap = 1.5f;
     600                 :          97 :         p.slide_enabled = false;
     601                 :          97 :         p.slide_gain = 0.226562f;
     602                 :          97 :         p.slide_freq = 1.0f;
     603                 :          97 :         p.road_enabled = false;
     604                 :          97 :         p.road_gain = 0.0f;
     605                 :          97 :         p.vibration_gain = 1.0f;
     606                 :          97 :         p.road_fallback_scale = 0.05f;
     607                 :          97 :         p.general.dynamic_normalization_enabled = false;
     608                 :          97 :         p.general.auto_load_normalization_enabled = false;
     609                 :          97 :         p.soft_lock_enabled = false;
     610                 :          97 :         p.soft_lock_stiffness = 20.0f;
     611                 :          97 :         p.soft_lock_damping = 0.5f;
     612                 :          97 :         p.spin_enabled = false;
     613                 :          97 :         p.spin_gain = 0.5f;
     614                 :          97 :         p.spin_freq_scale = 1.0f;
     615                 :          97 :         p.scrub_drag_gain = 0.0f;
     616                 :          97 :         p.bottoming_enabled = true;
     617                 :          97 :         p.bottoming_gain = 1.0f;
     618                 :          97 :         p.bottoming_method = 0;
     619                 :          97 :         p.rest_api_enabled = true;
     620                 :          97 :         p.rest_api_port = 6397;
     621                 :          97 :         p.safety_window_duration = 0.0f;
     622                 :          97 :         p.safety_gain_reduction = 0.3f;
     623                 :          97 :         p.safety_smoothing_tau = 0.2f;
     624                 :          97 :         p.spike_detection_threshold = 500.0f;
     625                 :          97 :         p.immediate_spike_threshold = 1500.0f;
     626                 :          97 :         p.safety_slew_full_scale_time_s = 1.0f;
     627                 :          97 :         p.stutter_safety_enabled = false;
     628                 :          97 :         p.stutter_threshold = 1.5f;
     629                 :          97 :         p.speed_gate_lower = 1.0f;
     630                 :          97 :         p.speed_gate_upper = 5.0f;
     631         [ +  - ]:          97 :         presets.push_back(p);
     632                 :          97 :     }
     633                 :             : 
     634                 :             :     // 4. GT3 DD 15 Nm (Simagic Alpha)
     635                 :             :     {
     636   [ +  -  +  - ]:          97 :         Preset p("GT3 DD 15 Nm (Simagic Alpha)", true);
     637                 :          97 :         p.general.gain = 1.0f;
     638                 :          97 :         p.general.wheelbase_max_nm = 15.0f;
     639                 :          97 :         p.general.target_rim_nm = 10.0f;
     640                 :          97 :         p.general.min_force = 0.0f;
     641                 :          97 :         p.steering_shaft_gain = 1.0f;
     642                 :          97 :         p.steering_shaft_smoothing = 0.0f;
     643                 :          97 :         p.understeer = 1.0f;
     644                 :          97 :         p.flatspot_suppression = false;
     645                 :          97 :         p.notch_q = 2.0f;
     646                 :          97 :         p.flatspot_strength = 1.0f;
     647                 :          97 :         p.static_notch_enabled = false;
     648                 :          97 :         p.static_notch_freq = 11.0f;
     649                 :          97 :         p.static_notch_width = 2.0f;
     650                 :          97 :         p.oversteer_boost = 2.52101f;
     651                 :          97 :         p.sop = 1.666f;
     652                 :          97 :         p.rear_align_effect = 0.666f;
     653                 :          97 :         p.sop_yaw_gain = 0.333f;
     654                 :          97 :         p.yaw_kick_threshold = 0.0f;
     655                 :          97 :         p.yaw_smoothing = 0.001f;
     656                 :          97 :         p.gyro_gain = 0.0f;
     657                 :          97 :         p.gyro_smoothing = 0.0f;
     658                 :          97 :         p.sop_smoothing = 0.0f;
     659                 :          97 :         p.sop_scale = 1.98f;
     660                 :          97 :         p.understeer_affects_sop = false;
     661                 :          97 :         p.slip_smoothing = 0.002f;
     662                 :          97 :         p.chassis_smoothing = 0.012f;
     663                 :          97 :         p.optimal_slip_angle = 0.1f;
     664                 :          97 :         p.optimal_slip_ratio = 0.12f;
     665                 :          97 :         p.lockup_enabled = true;
     666                 :          97 :         p.lockup_gain = 0.37479f;
     667                 :          97 :         p.brake_load_cap = 2.0f;
     668                 :          97 :         p.lockup_freq_scale = 1.0f;
     669                 :          97 :         p.lockup_gamma = 1.0f;
     670                 :          97 :         p.lockup_start_pct = 1.0f;
     671                 :          97 :         p.lockup_full_pct = 7.5f;
     672                 :          97 :         p.lockup_prediction_sens = 10.0f;
     673                 :          97 :         p.lockup_bump_reject = 0.1f;
     674                 :          97 :         p.lockup_rear_boost = 1.0f;
     675                 :          97 :         p.abs_pulse_enabled = false;
     676                 :          97 :         p.abs_gain = 2.1f;
     677                 :          97 :         p.abs_freq = 25.5f;
     678                 :          97 :         p.texture_load_cap = 1.5f;
     679                 :          97 :         p.slide_enabled = false;
     680                 :          97 :         p.slide_gain = 0.226562f;
     681                 :          97 :         p.slide_freq = 1.47f;
     682                 :          97 :         p.road_enabled = true;
     683                 :          97 :         p.road_gain = 0.0f;
     684                 :          97 :         p.road_fallback_scale = 0.05f;
     685                 :          97 :         p.spin_enabled = true;
     686                 :          97 :         p.spin_gain = 0.462185f;
     687                 :          97 :         p.spin_freq_scale = 1.8f;
     688                 :          97 :         p.scrub_drag_gain = 0.333f;
     689                 :          97 :         p.bottoming_enabled = true;
     690                 :          97 :         p.bottoming_gain = 1.0f;
     691                 :          97 :         p.bottoming_method = 1;
     692                 :          97 :         p.speed_gate_lower = 1.0f;
     693                 :          97 :         p.speed_gate_upper = 5.0f;
     694         [ +  - ]:          97 :         presets.push_back(p);
     695                 :          97 :     }
     696                 :             :     
     697                 :             :     // 5. LMPx/HY DD 15 Nm (Simagic Alpha)
     698                 :             :     {
     699   [ +  -  +  - ]:          97 :         Preset p("LMPx/HY DD 15 Nm (Simagic Alpha)", true);
     700                 :          97 :         p.general.gain = 1.0f;
     701                 :          97 :         p.general.wheelbase_max_nm = 15.0f;
     702                 :          97 :         p.general.target_rim_nm = 10.0f;
     703                 :          97 :         p.general.min_force = 0.0f;
     704                 :          97 :         p.steering_shaft_gain = 1.0f;
     705                 :          97 :         p.steering_shaft_smoothing = 0.0f;
     706                 :          97 :         p.understeer = 1.0f;
     707                 :          97 :         p.flatspot_suppression = false;
     708                 :          97 :         p.notch_q = 2.0f;
     709                 :          97 :         p.flatspot_strength = 1.0f;
     710                 :          97 :         p.static_notch_enabled = false;
     711                 :          97 :         p.static_notch_freq = 11.0f;
     712                 :          97 :         p.static_notch_width = 2.0f;
     713                 :          97 :         p.oversteer_boost = 2.52101f;
     714                 :          97 :         p.sop = 1.666f;
     715                 :          97 :         p.rear_align_effect = 0.666f;
     716                 :          97 :         p.sop_yaw_gain = 0.333f;
     717                 :          97 :         p.yaw_kick_threshold = 0.0f;
     718                 :          97 :         p.yaw_smoothing = 0.003f;
     719                 :          97 :         p.gyro_gain = 0.0f;
     720                 :          97 :         p.gyro_smoothing = 0.003f;
     721                 :          97 :         p.sop_smoothing = 0.0f;
     722                 :          97 :         p.sop_scale = 1.59f;
     723                 :          97 :         p.understeer_affects_sop = false;
     724                 :          97 :         p.slip_smoothing = 0.003f;
     725                 :          97 :         p.chassis_smoothing = 0.019f;
     726                 :          97 :         p.optimal_slip_angle = 0.12f;
     727                 :          97 :         p.optimal_slip_ratio = 0.12f;
     728                 :          97 :         p.lockup_enabled = true;
     729                 :          97 :         p.lockup_gain = 0.37479f;
     730                 :          97 :         p.brake_load_cap = 2.0f;
     731                 :          97 :         p.lockup_freq_scale = 1.0f;
     732                 :          97 :         p.lockup_gamma = 1.0f;
     733                 :          97 :         p.lockup_start_pct = 1.0f;
     734                 :          97 :         p.lockup_full_pct = 7.5f;
     735                 :          97 :         p.lockup_prediction_sens = 10.0f;
     736                 :          97 :         p.lockup_bump_reject = 0.1f;
     737                 :          97 :         p.lockup_rear_boost = 1.0f;
     738                 :          97 :         p.abs_pulse_enabled = false;
     739                 :          97 :         p.abs_gain = 2.1f;
     740                 :          97 :         p.abs_freq = 25.5f;
     741                 :          97 :         p.texture_load_cap = 1.5f;
     742                 :          97 :         p.slide_enabled = false;
     743                 :          97 :         p.slide_gain = 0.226562f;
     744                 :          97 :         p.slide_freq = 1.47f;
     745                 :          97 :         p.road_enabled = true;
     746                 :          97 :         p.road_gain = 0.0f;
     747                 :          97 :         p.road_fallback_scale = 0.05f;
     748                 :          97 :         p.spin_enabled = true;
     749                 :          97 :         p.spin_gain = 0.462185f;
     750                 :          97 :         p.spin_freq_scale = 1.8f;
     751                 :          97 :         p.scrub_drag_gain = 0.333f;
     752                 :          97 :         p.bottoming_enabled = true;
     753                 :          97 :         p.bottoming_gain = 1.0f;
     754                 :          97 :         p.bottoming_method = 1;
     755                 :          97 :         p.speed_gate_lower = 1.0f;
     756                 :          97 :         p.speed_gate_upper = 5.0f;
     757         [ +  - ]:          97 :         presets.push_back(p);
     758                 :          97 :     }
     759                 :             :     
     760                 :             :     // 6. GM DD 21 Nm (Moza R21 Ultra)
     761                 :             :     {
     762   [ +  -  +  - ]:          97 :         Preset p("GM DD 21 Nm (Moza R21 Ultra)", true);
     763                 :          97 :         p.general.gain = 1.454f;
     764                 :          97 :         p.general.wheelbase_max_nm = 21.0f;
     765                 :          97 :         p.general.target_rim_nm = 12.0f;
     766                 :          97 :         p.general.min_force = 0.0f;
     767                 :          97 :         p.steering_shaft_gain = 1.989f;
     768                 :          97 :         p.steering_shaft_smoothing = 0.0f;
     769                 :          97 :         p.understeer = 0.638f;
     770                 :          97 :         p.flatspot_suppression = true;
     771                 :          97 :         p.notch_q = 0.57f;
     772                 :          97 :         p.flatspot_strength = 1.0f;
     773                 :          97 :         p.static_notch_enabled = false;
     774                 :          97 :         p.static_notch_freq = 11.0f;
     775                 :          97 :         p.static_notch_width = 2.0f;
     776                 :          97 :         p.oversteer_boost = 0.0f;
     777                 :          97 :         p.sop = 0.0f;
     778                 :          97 :         p.rear_align_effect = 0.29f;
     779                 :          97 :         p.sop_yaw_gain = 0.0f;
     780                 :          97 :         p.yaw_kick_threshold = 0.0f;
     781                 :          97 :         p.yaw_smoothing = 0.015f;
     782                 :          97 :         p.gyro_gain = 0.0f;
     783                 :          97 :         p.gyro_smoothing = 0.0f;
     784                 :          97 :         p.sop_smoothing = 0.0f;
     785                 :          97 :         p.sop_scale = 0.89f;
     786                 :          97 :         p.understeer_affects_sop = false;
     787                 :          97 :         p.slip_smoothing = 0.002f;
     788                 :          97 :         p.chassis_smoothing = 0.0f;
     789                 :          97 :         p.optimal_slip_angle = 0.1f;
     790                 :          97 :         p.optimal_slip_ratio = 0.12f;
     791                 :          97 :         p.lockup_enabled = true;
     792                 :          97 :         p.lockup_gain = 0.977f;
     793                 :          97 :         p.brake_load_cap = 81.0f;
     794                 :          97 :         p.lockup_freq_scale = 1.0f;
     795                 :          97 :         p.lockup_gamma = 1.0f;
     796                 :          97 :         p.lockup_start_pct = 1.0f;
     797                 :          97 :         p.lockup_full_pct = 7.5f;
     798                 :          97 :         p.lockup_prediction_sens = 10.0f;
     799                 :          97 :         p.lockup_bump_reject = 0.1f;
     800                 :          97 :         p.lockup_rear_boost = 1.0f;
     801                 :          97 :         p.abs_pulse_enabled = false;
     802                 :          97 :         p.abs_gain = 2.1f;
     803                 :          97 :         p.abs_freq = 25.5f;
     804                 :          97 :         p.texture_load_cap = 1.5f;
     805                 :          97 :         p.slide_enabled = false;
     806                 :          97 :         p.slide_gain = 0.0f;
     807                 :          97 :         p.slide_freq = 1.47f;
     808                 :          97 :         p.road_enabled = true;
     809                 :          97 :         p.road_gain = 0.0f;
     810                 :          97 :         p.road_fallback_scale = 0.05f;
     811                 :          97 :         p.spin_enabled = true;
     812                 :          97 :         p.spin_gain = 0.462185f;
     813                 :          97 :         p.spin_freq_scale = 1.8f;
     814                 :          97 :         p.scrub_drag_gain = 0.333f;
     815                 :          97 :         p.bottoming_enabled = true;
     816                 :          97 :         p.bottoming_gain = 1.0f;
     817                 :          97 :         p.bottoming_method = 1;
     818                 :          97 :         p.speed_gate_lower = 1.0f;
     819                 :          97 :         p.speed_gate_upper = 5.0f;
     820         [ +  - ]:          97 :         presets.push_back(p);
     821                 :          97 :     }
     822                 :             :     
     823                 :             :     // 7. GM + Yaw Kick DD 21 Nm (Moza R21 Ultra)
     824                 :             :     {
     825                 :             :         // Copy GM preset and add yaw kick
     826   [ +  -  +  - ]:          97 :         Preset p("GM + Yaw Kick DD 21 Nm (Moza R21 Ultra)", true);
     827                 :          97 :         p.general.gain = 1.454f;
     828                 :          97 :         p.general.wheelbase_max_nm = 21.0f;
     829                 :          97 :         p.general.target_rim_nm = 12.0f;
     830                 :          97 :         p.general.min_force = 0.0f;
     831                 :          97 :         p.steering_shaft_gain = 1.989f;
     832                 :          97 :         p.steering_shaft_smoothing = 0.0f;
     833                 :          97 :         p.understeer = 0.638f;
     834                 :          97 :         p.flatspot_suppression = true;
     835                 :          97 :         p.notch_q = 0.57f;
     836                 :          97 :         p.flatspot_strength = 1.0f;
     837                 :          97 :         p.static_notch_enabled = false;
     838                 :          97 :         p.static_notch_freq = 11.0f;
     839                 :          97 :         p.static_notch_width = 2.0f;
     840                 :          97 :         p.oversteer_boost = 0.0f;
     841                 :          97 :         p.sop = 0.0f;
     842                 :          97 :         p.rear_align_effect = 0.29f;
     843                 :          97 :         p.sop_yaw_gain = 0.333f;  // ONLY DIFFERENCE: Added yaw kick
     844                 :          97 :         p.yaw_kick_threshold = 0.0f;
     845                 :          97 :         p.yaw_smoothing = 0.003f;
     846                 :          97 :         p.gyro_gain = 0.0f;
     847                 :          97 :         p.gyro_smoothing = 0.0f;
     848                 :          97 :         p.sop_smoothing = 0.0f;
     849                 :          97 :         p.sop_scale = 0.89f;
     850                 :          97 :         p.understeer_affects_sop = false;
     851                 :          97 :         p.slip_smoothing = 0.002f;
     852                 :          97 :         p.chassis_smoothing = 0.0f;
     853                 :          97 :         p.optimal_slip_angle = 0.1f;
     854                 :          97 :         p.optimal_slip_ratio = 0.12f;
     855                 :          97 :         p.lockup_enabled = true;
     856                 :          97 :         p.lockup_gain = 0.977f;
     857                 :          97 :         p.brake_load_cap = 81.0f;
     858                 :          97 :         p.lockup_freq_scale = 1.0f;
     859                 :          97 :         p.lockup_gamma = 1.0f;
     860                 :          97 :         p.lockup_start_pct = 1.0f;
     861                 :          97 :         p.lockup_full_pct = 7.5f;
     862                 :          97 :         p.lockup_prediction_sens = 10.0f;
     863                 :          97 :         p.lockup_bump_reject = 0.1f;
     864                 :          97 :         p.lockup_rear_boost = 1.0f;
     865                 :          97 :         p.abs_pulse_enabled = false;
     866                 :          97 :         p.abs_gain = 2.1f;
     867                 :          97 :         p.abs_freq = 25.5f;
     868                 :          97 :         p.texture_load_cap = 1.5f;
     869                 :          97 :         p.slide_enabled = false;
     870                 :          97 :         p.slide_gain = 0.0f;
     871                 :          97 :         p.slide_freq = 1.47f;
     872                 :          97 :         p.road_enabled = true;
     873                 :          97 :         p.road_gain = 0.0f;
     874                 :          97 :         p.road_fallback_scale = 0.05f;
     875                 :          97 :         p.spin_enabled = true;
     876                 :          97 :         p.spin_gain = 0.462185f;
     877                 :          97 :         p.spin_freq_scale = 1.8f;
     878                 :          97 :         p.scrub_drag_gain = 0.333f;
     879                 :          97 :         p.bottoming_enabled = true;
     880                 :          97 :         p.bottoming_gain = 1.0f;
     881                 :          97 :         p.bottoming_method = 1;
     882                 :          97 :         p.speed_gate_lower = 1.0f;
     883                 :          97 :         p.speed_gate_upper = 5.0f;
     884         [ +  - ]:          97 :         presets.push_back(p);
     885                 :          97 :     }
     886                 :             :     
     887                 :             :     // 8. Test: Game Base FFB Only
     888   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: Game Base FFB Only", true)
                   +  - ]
     889                 :          97 :         .SetUndersteer(0.0f)
     890                 :          97 :         .SetSoP(0.0f)
     891                 :          97 :         .SetSoPScale(1.0f)
     892                 :          97 :         .SetSmoothing(0.0f)
     893                 :          97 :         .SetSlipSmoothing(0.015f)
     894                 :          97 :         .SetSlide(false, 0.0f)
     895                 :          97 :         .SetRearAlign(0.0f)
     896                 :          97 :         .SetBottoming(false, 0.0f, 0)
     897                 :             :     );
     898                 :             : 
     899                 :             :     // 9. Test: SoP Only
     900   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: SoP Only", true)
                   +  - ]
     901                 :          97 :         .SetUndersteer(0.0f)
     902                 :          97 :         .SetSoP(0.08f)
     903                 :          97 :         .SetSoPScale(1.0f)
     904                 :          97 :         .SetSmoothing(0.0f)
     905                 :          97 :         .SetSlipSmoothing(0.015f)
     906                 :          97 :         .SetSlide(false, 0.0f)
     907                 :          97 :         .SetRearAlign(0.0f)
     908                 :          97 :         .SetSoPYaw(0.0f)
     909                 :          97 :         .SetBottoming(false, 0.0f, 0)
     910                 :             :     );
     911                 :             : 
     912                 :             :     // 10. Test: Understeer Only (Updated v0.6.31 for proper effect isolation)
     913   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: Understeer Only", true)
                   +  - ]
     914                 :             :         // PRIMARY EFFECT
     915                 :          97 :         .SetUndersteer(0.61f)
     916                 :             :         
     917                 :             :         // DISABLE ALL OTHER EFFECTS
     918                 :          97 :         .SetSoP(0.0f)
     919                 :          97 :         .SetSoPScale(1.0f)
     920                 :          97 :         .SetOversteer(0.0f)          // Disable oversteer boost
     921                 :          97 :         .SetRearAlign(0.0f)
     922                 :          97 :         .SetSoPYaw(0.0f)             // Disable yaw kick
     923                 :          97 :         .SetGyro(0.0f)               // Disable gyro damping
     924                 :             :         
     925                 :             :         // DISABLE ALL TEXTURES
     926                 :          97 :         .SetSlide(false, 0.0f)
     927                 :          97 :         .SetRoad(false, 0.0f)        // Disable road texture
     928                 :          97 :         .SetSpin(false, 0.0f)        // Disable spin
     929                 :          97 :         .SetLockup(false, 0.0f)      // Disable lockup vibration
     930                 :          97 :         .SetAdvancedBraking(0.5f, 20.0f, 0.1f, false, 0.0f)  // Disable ABS pulse
     931                 :          97 :         .SetScrub(0.0f)
     932                 :          97 :         .SetBottoming(false, 0.0f, 0)
     933                 :             :         
     934                 :             :         // SMOOTHING
     935                 :          97 :         .SetSmoothing(0.0f)         // SoP smoothing (doesn't affect test since SoP=0)
     936                 :          97 :         .SetSlipSmoothing(0.015f)    // Slip angle smoothing (important for grip calculation)
     937                 :             :         
     938                 :             :         // PHYSICS PARAMETERS (Explicit for clarity and future-proofing)
     939                 :          97 :         .SetOptimalSlip(0.10f, 0.12f)  // Explicit optimal slip thresholds
     940                 :          97 :         .SetSpeedGate(0.0f, 0.0f)      // Disable speed gate (0 = no gating)
     941                 :             :     );
     942                 :             : 
     943                 :             :     // 11. Test: Yaw Kick Only
     944   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: Yaw Kick Only", true)
                   +  - ]
     945                 :             :         // PRIMARY EFFECT
     946                 :          97 :         .SetSoPYaw(0.386555f)        // Yaw kick at T300 level
     947                 :          97 :         .SetYawKickThreshold(1.68f)  // T300 threshold
     948                 :          97 :         .SetYawSmoothing(0.005f)     // T300 smoothing
     949                 :             :         
     950                 :             :         // DISABLE ALL OTHER EFFECTS
     951                 :          97 :         .SetUndersteer(0.0f)
     952                 :          97 :         .SetSoP(0.0f)
     953                 :          97 :         .SetSoPScale(1.0f)
     954                 :          97 :         .SetOversteer(0.0f)
     955                 :          97 :         .SetRearAlign(0.0f)
     956                 :          97 :         .SetGyro(0.0f)
     957                 :             :         
     958                 :             :         // DISABLE ALL TEXTURES
     959                 :          97 :         .SetSlide(false, 0.0f)
     960                 :          97 :         .SetRoad(false, 0.0f)
     961                 :          97 :         .SetSpin(false, 0.0f)
     962                 :          97 :         .SetLockup(false, 0.0f)
     963                 :          97 :         .SetAdvancedBraking(0.5f, 20.0f, 0.1f, false, 0.0f)
     964                 :          97 :         .SetScrub(0.0f)
     965                 :          97 :         .SetBottoming(false, 0.0f, 0)
     966                 :             :         
     967                 :             :         // SMOOTHING
     968                 :          97 :         .SetSmoothing(0.0f)
     969                 :          97 :         .SetSlipSmoothing(0.015f)
     970                 :             :     );
     971                 :             : 
     972                 :             :     // 12. Test: Textures Only
     973   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: Textures Only", true)
                   +  - ]
     974                 :          97 :         .SetUndersteer(0.0f)
     975                 :          97 :         .SetSoP(0.0f)
     976                 :          97 :         .SetSoPScale(0.0f)
     977                 :          97 :         .SetSmoothing(0.0f)
     978                 :          97 :         .SetSlipSmoothing(0.015f)
     979                 :          97 :         .SetLockup(true, 1.0f)
     980                 :          97 :         .SetSpin(true, 1.0f)
     981                 :          97 :         .SetSlide(true, 0.39f)
     982                 :          97 :         .SetRoad(true, 1.0f)
     983                 :          97 :         .SetRearAlign(0.0f)
     984                 :          97 :         .SetBottoming(true, 1.0f, 0)
     985                 :             :     );
     986                 :             : 
     987                 :             :     // 13. Test: Rear Align Torque Only
     988   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: Rear Align Torque Only", true)
                   +  - ]
     989                 :          97 :         .SetGain(1.0f)
     990                 :          97 :         .SetUndersteer(0.0f)
     991                 :          97 :         .SetSoP(0.0f)
     992                 :          97 :         .SetSmoothing(0.0f)
     993                 :          97 :         .SetSlipSmoothing(0.015f)
     994                 :          97 :         .SetSlide(false, 0.0f)
     995                 :          97 :         .SetRearAlign(0.90f)
     996                 :          97 :         .SetSoPYaw(0.0f)
     997                 :          97 :         .SetBottoming(false, 0.0f, 0)
     998                 :             :     );
     999                 :             : 
    1000                 :             :     // 14. Test: SoP Base Only
    1001   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: SoP Base Only", true)
                   +  - ]
    1002                 :          97 :         .SetGain(1.0f)
    1003                 :          97 :         .SetUndersteer(0.0f)
    1004                 :          97 :         .SetSoP(0.08f)
    1005                 :          97 :         .SetSmoothing(0.0f)
    1006                 :          97 :         .SetSlipSmoothing(0.015f)
    1007                 :          97 :         .SetSlide(false, 0.0f)
    1008                 :          97 :         .SetRearAlign(0.0f)
    1009                 :          97 :         .SetSoPYaw(0.0f)
    1010                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1011                 :             :     );
    1012                 :             : 
    1013                 :             :     // 15. Test: Slide Texture Only
    1014   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: Slide Texture Only", true)
                   +  - ]
    1015                 :          97 :         .SetGain(1.0f)
    1016                 :          97 :         .SetUndersteer(0.0f)
    1017                 :          97 :         .SetSoP(0.0f)
    1018                 :          97 :         .SetSmoothing(0.0f)
    1019                 :          97 :         .SetSlipSmoothing(0.015f)
    1020                 :          97 :         .SetSlide(true, 0.39f, 1.0f)
    1021                 :          97 :         .SetRearAlign(0.0f)
    1022                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1023                 :             :     );
    1024                 :             : 
    1025                 :             :     // 16. Test: No Effects
    1026   [ +  -  +  -  :         291 :     presets.push_back(Preset("Test: No Effects", true)
                   +  - ]
    1027                 :          97 :         .SetGain(1.0f)
    1028                 :          97 :         .SetUndersteer(0.0f)
    1029                 :          97 :         .SetSoP(0.0f)
    1030                 :          97 :         .SetSmoothing(0.0f)
    1031                 :          97 :         .SetSlipSmoothing(0.015f)
    1032                 :          97 :         .SetSlide(false, 0.0f)
    1033                 :          97 :         .SetRearAlign(0.0f)
    1034                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1035                 :             :     );
    1036                 :             : 
    1037                 :             :     // --- NEW GUIDE PRESETS (v0.4.24) ---
    1038                 :             : 
    1039                 :             :     // 17. Guide: Understeer (Front Grip Loss)
    1040   [ +  -  +  -  :         291 :     presets.push_back(Preset("Guide: Understeer (Front Grip)", true)
                   +  - ]
    1041                 :          97 :         .SetGain(1.0f)
    1042                 :          97 :         .SetUndersteer(0.61f)
    1043                 :          97 :         .SetSoP(0.0f)
    1044                 :          97 :         .SetOversteer(0.0f)
    1045                 :          97 :         .SetRearAlign(0.0f)
    1046                 :          97 :         .SetSoPYaw(0.0f)
    1047                 :          97 :         .SetGyro(0.0f)
    1048                 :          97 :         .SetLockup(false, 0.0f)
    1049                 :          97 :         .SetSpin(false, 0.0f)
    1050                 :          97 :         .SetSlide(false, 0.0f)
    1051                 :          97 :         .SetRoad(false, 0.0f)
    1052                 :          97 :         .SetScrub(0.0f)
    1053                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1054                 :          97 :         .SetSmoothing(0.0f)
    1055                 :          97 :         .SetSlipSmoothing(0.015f)
    1056                 :             :     );
    1057                 :             : 
    1058                 :             :     // 18. Guide: Oversteer (Rear Grip Loss)
    1059   [ +  -  +  -  :         291 :     presets.push_back(Preset("Guide: Oversteer (Rear Grip)", true)
                   +  - ]
    1060                 :          97 :         .SetGain(1.0f)
    1061                 :          97 :         .SetUndersteer(0.0f)
    1062                 :          97 :         .SetSoP(0.08f)
    1063                 :          97 :         .SetSoPScale(1.0f)
    1064                 :          97 :         .SetRearAlign(0.90f)
    1065                 :          97 :         .SetOversteer(0.65f)
    1066                 :          97 :         .SetSoPYaw(0.0f)
    1067                 :          97 :         .SetGyro(0.0f)
    1068                 :          97 :         .SetLockup(false, 0.0f)
    1069                 :          97 :         .SetSpin(false, 0.0f)
    1070                 :          97 :         .SetSlide(false, 0.0f)
    1071                 :          97 :         .SetRoad(false, 0.0f)
    1072                 :          97 :         .SetScrub(0.0f)
    1073                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1074                 :          97 :         .SetSmoothing(0.0f)
    1075                 :          97 :         .SetSlipSmoothing(0.015f)
    1076                 :             :     );
    1077                 :             : 
    1078                 :             :     // 19. Guide: Slide Texture (Scrubbing)
    1079   [ +  -  +  -  :         291 :     presets.push_back(Preset("Guide: Slide Texture (Scrub)", true)
                   +  - ]
    1080                 :          97 :         .SetGain(1.0f)
    1081                 :          97 :         .SetUndersteer(0.0f)
    1082                 :          97 :         .SetSoP(0.0f)
    1083                 :          97 :         .SetOversteer(0.0f)
    1084                 :          97 :         .SetRearAlign(0.0f)
    1085                 :          97 :         .SetSlide(true, 0.39f, 1.0f) // Gain 0.39, Freq 1.0 (Rumble)
    1086                 :          97 :         .SetScrub(1.0f)
    1087                 :          97 :         .SetLockup(false, 0.0f)
    1088                 :          97 :         .SetSpin(false, 0.0f)
    1089                 :          97 :         .SetRoad(false, 0.0f)
    1090                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1091                 :          97 :         .SetSmoothing(0.0f)
    1092                 :          97 :         .SetSlipSmoothing(0.015f)
    1093                 :             :     );
    1094                 :             : 
    1095                 :             :     // 20. Guide: Braking Lockup
    1096   [ +  -  +  -  :         291 :     presets.push_back(Preset("Guide: Braking Lockup", true)
                   +  - ]
    1097                 :          97 :         .SetGain(1.0f)
    1098                 :          97 :         .SetUndersteer(0.0f)
    1099                 :          97 :         .SetSoP(0.0f)
    1100                 :          97 :         .SetOversteer(0.0f)
    1101                 :          97 :         .SetRearAlign(0.0f)
    1102                 :          97 :         .SetLockup(true, 1.0f)
    1103                 :          97 :         .SetSpin(false, 0.0f)
    1104                 :          97 :         .SetSlide(false, 0.0f)
    1105                 :          97 :         .SetRoad(false, 0.0f)
    1106                 :          97 :         .SetScrub(0.0f)
    1107                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1108                 :          97 :         .SetSmoothing(0.0f)
    1109                 :          97 :         .SetSlipSmoothing(0.015f)
    1110                 :             :     );
    1111                 :             : 
    1112                 :             :     // 21. Guide: Traction Loss (Wheel Spin)
    1113   [ +  -  +  -  :         291 :     presets.push_back(Preset("Guide: Traction Loss (Spin)", true)
                   +  - ]
    1114                 :          97 :         .SetGain(1.0f)
    1115                 :          97 :         .SetUndersteer(0.0f)
    1116                 :          97 :         .SetSoP(0.0f)
    1117                 :          97 :         .SetOversteer(0.0f)
    1118                 :          97 :         .SetRearAlign(0.0f)
    1119                 :          97 :         .SetSpin(true, 1.0f)
    1120                 :          97 :         .SetLockup(false, 0.0f)
    1121                 :          97 :         .SetSlide(false, 0.0f)
    1122                 :          97 :         .SetRoad(false, 0.0f)
    1123                 :          97 :         .SetScrub(0.0f)
    1124                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1125                 :          97 :         .SetSmoothing(0.0f)
    1126                 :          97 :         .SetSlipSmoothing(0.015f)
    1127                 :             :     );
    1128                 :             : 
    1129                 :             :     // 22. Guide: SoP Yaw (Kick)
    1130   [ +  -  +  -  :         291 :     presets.push_back(Preset("Guide: SoP Yaw (Kick)", true)
                   +  - ]
    1131                 :          97 :         .SetGain(1.0f)
    1132                 :          97 :         .SetUndersteer(0.0f)
    1133                 :          97 :         .SetSoP(0.0f)
    1134                 :          97 :         .SetOversteer(0.0f)
    1135                 :          97 :         .SetRearAlign(0.0f)
    1136                 :          97 :         .SetSoPYaw(5.0f) // Standard T300 level
    1137                 :          97 :         .SetGyro(0.0f)
    1138                 :          97 :         .SetLockup(false, 0.0f)
    1139                 :          97 :         .SetSpin(false, 0.0f)
    1140                 :          97 :         .SetSlide(false, 0.0f)
    1141                 :          97 :         .SetRoad(false, 0.0f)
    1142                 :          97 :         .SetScrub(0.0f)
    1143                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1144                 :          97 :         .SetSmoothing(0.0f)
    1145                 :          97 :         .SetSlipSmoothing(0.015f)
    1146                 :             :     );
    1147                 :             : 
    1148                 :             :     // 23. Guide: Gyroscopic Damping
    1149   [ +  -  +  -  :         194 :     presets.push_back(Preset("Guide: Gyroscopic Damping", true)
                   +  - ]
    1150                 :          97 :         .SetGain(1.0f)
    1151                 :          97 :         .SetUndersteer(0.0f)
    1152                 :          97 :         .SetSoP(0.0f)
    1153                 :          97 :         .SetOversteer(0.0f)
    1154                 :          97 :         .SetRearAlign(0.0f)
    1155                 :          97 :         .SetSoPYaw(0.0f)
    1156                 :          97 :         .SetGyro(1.0f) // Max damping
    1157                 :          97 :         .SetLockup(false, 0.0f)
    1158                 :          97 :         .SetSpin(false, 0.0f)
    1159                 :          97 :         .SetSlide(false, 0.0f)
    1160                 :          97 :         .SetRoad(false, 0.0f)
    1161                 :          97 :         .SetScrub(0.0f)
    1162                 :          97 :         .SetBottoming(false, 0.0f, 0)
    1163                 :          97 :         .SetSmoothing(0.0f)
    1164                 :          97 :         .SetSlipSmoothing(0.015f)
    1165                 :             :     );
    1166                 :             : 
    1167                 :             :     // --- Parse User Presets from config.ini ---
    1168                 :             :     // (Keep the existing parsing logic below, it works fine for file I/O)
    1169         [ +  - ]:          97 :     std::ifstream file(m_config_path);
    1170         [ +  + ]:          97 :     if (!file.is_open()) return;
    1171                 :             : 
    1172                 :          78 :     std::string line;
    1173                 :          78 :     bool in_presets = false;
    1174                 :          78 :     bool needs_save = false;
    1175                 :             :     
    1176         [ +  - ]:          78 :     std::string current_preset_name = "";
    1177         [ +  - ]:          78 :     Preset current_preset; // Uses default constructor with default values
    1178         [ +  - ]:          78 :     std::string current_preset_version = "";
    1179                 :          78 :     bool preset_pending = false;
    1180                 :          78 :     bool legacy_torque_hack = false;
    1181                 :          78 :     float legacy_torque_val = 100.0f;
    1182                 :             : 
    1183   [ +  -  +  -  :       19471 :     while (std::getline(file, line)) {
                   +  + ]
    1184                 :             :         // Strip whitespace
    1185         [ +  - ]:       19393 :         line.erase(0, line.find_first_not_of(" \t\r\n"));
    1186         [ +  - ]:       19393 :         line.erase(line.find_last_not_of(" \t\r\n") + 1);
    1187                 :             :         
    1188   [ +  +  +  -  :       19393 :         if (line.empty() || line[0] == ';') continue;
             +  +  +  + ]
    1189                 :             : 
    1190   [ +  -  +  + ]:       18154 :         if (line[0] == '[') {
    1191         [ +  + ]:         233 :             if (preset_pending) {
    1192         [ +  - ]:          42 :                 FinalizePreset(presets, current_preset, current_preset_name, current_preset_version, legacy_torque_hack, legacy_torque_val, needs_save, false);
    1193                 :          42 :                 preset_pending = false;
    1194                 :             :             }
    1195                 :             :             
    1196   [ +  -  +  + ]:         233 :             if (line == "[Presets]") {
    1197                 :          75 :                 in_presets = true;
    1198   [ +  -  +  + ]:         158 :             } else if (line.rfind("[Preset:", 0) == 0) { 
    1199                 :          90 :                 in_presets = false; 
    1200                 :          90 :                 size_t end_pos = line.find(']');
    1201         [ +  - ]:          90 :                 if (end_pos != std::string::npos) {
    1202         [ +  - ]:          90 :                     current_preset_name = line.substr(8, end_pos - 8);
    1203   [ +  -  +  - ]:          90 :                     current_preset = Preset(current_preset_name, false); // Reset to defaults, not builtin
    1204                 :          90 :                     preset_pending = true;
    1205         [ +  - ]:          90 :                     current_preset_version = "";
    1206                 :          90 :                     legacy_torque_hack = false;
    1207                 :          90 :                     legacy_torque_val = 100.0f;
    1208                 :             :                 }
    1209                 :             :             } else {
    1210                 :          68 :                 in_presets = false;
    1211                 :             :             }
    1212                 :         233 :             continue;
    1213                 :         233 :         }
    1214                 :             : 
    1215         [ +  + ]:       17921 :         if (preset_pending) {
    1216         [ +  - ]:        8946 :             ParsePresetLine(line, current_preset, current_preset_version, needs_save, legacy_torque_hack, legacy_torque_val);
    1217                 :             :         }
    1218                 :             :     }
    1219                 :             :     
    1220         [ +  + ]:          78 :     if (preset_pending) {
    1221         [ +  - ]:          48 :         FinalizePreset(presets, current_preset, current_preset_name, current_preset_version, legacy_torque_hack, legacy_torque_val, needs_save, false);
    1222                 :             :     }
    1223                 :             : 
    1224                 :             :     // Auto-save if migration occurred
    1225         [ +  + ]:          78 :     if (needs_save) {
    1226                 :           8 :         m_needs_save = true;
    1227                 :             :     }
    1228         [ +  + ]:          97 : }
    1229                 :             : 
    1230                 :          18 : void Config::ApplyPreset(int index, FFBEngine& engine) {
    1231   [ +  +  +  +  :          18 :     if (index >= 0 && index < presets.size()) {
                   +  + ]
    1232         [ +  - ]:          14 :         std::lock_guard<std::recursive_mutex> lock(g_engine_mutex);
    1233         [ +  - ]:          14 :         presets[index].Apply(engine);
    1234         [ +  - ]:          14 :         m_last_preset_name = presets[index].name;
    1235   [ +  -  +  - ]:          14 :         Logger::Get().LogFile("[Config] Applied preset: %s", presets[index].name.c_str());
    1236   [ +  -  +  - ]:          14 :         Save(engine); // Integrated Auto-Save (v0.6.27)
    1237                 :          14 :     }
    1238                 :          18 : }
    1239                 :             : 
    1240                 :          55 : void Config::WritePresetFields(std::ofstream& file, const Preset& p) {
    1241                 :          55 :     file << "app_version=" << p.app_version << "\n";
    1242                 :          55 :     file << "gain=" << p.general.gain << "\n";
    1243                 :          55 :     file << "wheelbase_max_nm=" << p.general.wheelbase_max_nm << "\n";
    1244                 :          55 :     file << "target_rim_nm=" << p.general.target_rim_nm << "\n";
    1245                 :          55 :     file << "min_force=" << p.general.min_force << "\n";
    1246                 :             : 
    1247                 :          55 :     file << "steering_shaft_gain=" << p.steering_shaft_gain << "\n";
    1248                 :          55 :     file << "ingame_ffb_gain=" << p.ingame_ffb_gain << "\n";
    1249                 :          55 :     file << "steering_shaft_smoothing=" << p.steering_shaft_smoothing << "\n";
    1250                 :          55 :     file << "understeer=" << p.understeer << "\n";
    1251                 :          55 :     file << "understeer_gamma=" << p.understeer_gamma << "\n";
    1252                 :          55 :     file << "torque_source=" << p.torque_source << "\n";
    1253                 :          55 :     file << "steering_100hz_reconstruction=" << p.steering_100hz_reconstruction << "\n";
    1254                 :          55 :     file << "torque_passthrough=" << p.torque_passthrough << "\n";
    1255                 :          55 :     file << "flatspot_suppression=" << p.flatspot_suppression << "\n";
    1256                 :          55 :     file << "notch_q=" << p.notch_q << "\n";
    1257                 :          55 :     file << "flatspot_strength=" << p.flatspot_strength << "\n";
    1258                 :          55 :     file << "static_notch_enabled=" << p.static_notch_enabled << "\n";
    1259                 :          55 :     file << "static_notch_freq=" << p.static_notch_freq << "\n";
    1260                 :          55 :     file << "static_notch_width=" << p.static_notch_width << "\n";
    1261                 :             : 
    1262                 :          55 :     file << "oversteer_boost=" << p.oversteer_boost << "\n";
    1263                 :          55 :     file << "long_load_effect=" << p.long_load_effect << "\n";
    1264                 :          55 :     file << "long_load_smoothing=" << p.long_load_smoothing << "\n";
    1265                 :          55 :     file << "long_load_transform=" << p.long_load_transform << "\n";
    1266                 :          55 :     file << "grip_smoothing_steady=" << p.grip_smoothing_steady << "\n";
    1267                 :          55 :     file << "grip_smoothing_fast=" << p.grip_smoothing_fast << "\n";
    1268                 :          55 :     file << "grip_smoothing_sensitivity=" << p.grip_smoothing_sensitivity << "\n";
    1269                 :          55 :     file << "sop=" << p.sop << "\n";
    1270                 :          55 :     file << "lateral_load_effect=" << p.lateral_load << "\n";
    1271                 :          55 :     file << "lat_load_transform=" << p.lat_load_transform << "\n";
    1272                 :          55 :     file << "rear_align_effect=" << p.rear_align_effect << "\n";
    1273                 :          55 :     file << "kerb_strike_rejection=" << p.kerb_strike_rejection << "\n";
    1274                 :          55 :     file << "sop_yaw_gain=" << p.sop_yaw_gain << "\n";
    1275                 :          55 :     file << "yaw_kick_threshold=" << p.yaw_kick_threshold << "\n";
    1276                 :          55 :     file << "unloaded_yaw_gain=" << p.unloaded_yaw_gain << "\n";
    1277                 :          55 :     file << "unloaded_yaw_threshold=" << p.unloaded_yaw_threshold << "\n";
    1278                 :          55 :     file << "unloaded_yaw_sens=" << p.unloaded_yaw_sens << "\n";
    1279                 :          55 :     file << "unloaded_yaw_gamma=" << p.unloaded_yaw_gamma << "\n";
    1280                 :          55 :     file << "unloaded_yaw_punch=" << p.unloaded_yaw_punch << "\n";
    1281                 :          55 :     file << "power_yaw_gain=" << p.power_yaw_gain << "\n";
    1282                 :          55 :     file << "power_yaw_threshold=" << p.power_yaw_threshold << "\n";
    1283                 :          55 :     file << "power_slip_threshold=" << p.power_slip_threshold << "\n";
    1284                 :          55 :     file << "power_yaw_gamma=" << p.power_yaw_gamma << "\n";
    1285                 :          55 :     file << "power_yaw_punch=" << p.power_yaw_punch << "\n";
    1286                 :          55 :     file << "yaw_accel_smoothing=" << p.yaw_smoothing << "\n";
    1287                 :          55 :     file << "gyro_gain=" << p.gyro_gain << "\n";
    1288                 :          55 :     file << "stationary_damping=" << p.stationary_damping << "\n";
    1289                 :          55 :     file << "gyro_smoothing_factor=" << p.gyro_smoothing << "\n";
    1290                 :          55 :     file << "sop_smoothing_factor=" << p.sop_smoothing << "\n";
    1291                 :          55 :     file << "sop_scale=" << p.sop_scale << "\n";
    1292                 :          55 :     file << "understeer_affects_sop=" << p.understeer_affects_sop << "\n";
    1293                 :          55 :     file << "slope_detection_enabled=" << p.slope_detection_enabled << "\n";
    1294                 :          55 :     file << "slope_sg_window=" << p.slope_sg_window << "\n";
    1295                 :          55 :     file << "slope_sensitivity=" << p.slope_sensitivity << "\n";
    1296                 :             : 
    1297                 :          55 :     file << "slope_smoothing_tau=" << p.slope_smoothing_tau << "\n";
    1298                 :          55 :     file << "slope_min_threshold=" << p.slope_min_threshold << "\n";
    1299                 :          55 :     file << "slope_max_threshold=" << p.slope_max_threshold << "\n";
    1300                 :          55 :     file << "slope_alpha_threshold=" << p.slope_alpha_threshold << "\n";
    1301                 :          55 :     file << "slope_decay_rate=" << p.slope_decay_rate << "\n";
    1302                 :          55 :     file << "slope_confidence_enabled=" << p.slope_confidence_enabled << "\n";
    1303                 :          55 :     file << "slope_g_slew_limit=" << p.slope_g_slew_limit << "\n";
    1304         [ +  - ]:          55 :     file << "slope_use_torque=" << (p.slope_use_torque ? "1" : "0") << "\n";
    1305                 :          55 :     file << "slope_torque_sensitivity=" << p.slope_torque_sensitivity << "\n";
    1306                 :          55 :     file << "slope_confidence_max_rate=" << p.slope_confidence_max_rate << "\n";
    1307                 :             : 
    1308                 :          55 :     file << "slip_angle_smoothing=" << p.slip_smoothing << "\n";
    1309                 :          55 :     file << "chassis_inertia_smoothing=" << p.chassis_smoothing << "\n";
    1310         [ +  - ]:          55 :     file << "load_sensitivity_enabled=" << (p.load_sensitivity_enabled ? "1" : "0") << "\n";
    1311                 :          55 :     file << "optimal_slip_angle=" << p.optimal_slip_angle << "\n";
    1312                 :          55 :     file << "optimal_slip_ratio=" << p.optimal_slip_ratio << "\n";
    1313                 :             : 
    1314         [ +  - ]:          55 :     file << "lockup_enabled=" << (p.lockup_enabled ? "1" : "0") << "\n";
    1315                 :          55 :     file << "lockup_gain=" << p.lockup_gain << "\n";
    1316                 :          55 :     file << "brake_load_cap=" << p.brake_load_cap << "\n";
    1317                 :          55 :     file << "lockup_freq_scale=" << p.lockup_freq_scale << "\n";
    1318                 :          55 :     file << "lockup_gamma=" << p.lockup_gamma << "\n";
    1319                 :          55 :     file << "lockup_start_pct=" << p.lockup_start_pct << "\n";
    1320                 :          55 :     file << "lockup_full_pct=" << p.lockup_full_pct << "\n";
    1321                 :          55 :     file << "lockup_prediction_sens=" << p.lockup_prediction_sens << "\n";
    1322                 :          55 :     file << "lockup_bump_reject=" << p.lockup_bump_reject << "\n";
    1323                 :          55 :     file << "lockup_rear_boost=" << p.lockup_rear_boost << "\n";
    1324         [ +  + ]:          55 :     file << "abs_pulse_enabled=" << (p.abs_pulse_enabled ? "1" : "0") << "\n";
    1325                 :          55 :     file << "abs_gain=" << p.abs_gain << "\n";
    1326                 :          55 :     file << "abs_freq=" << p.abs_freq << "\n";
    1327                 :             : 
    1328                 :          55 :     file << "texture_load_cap=" << p.texture_load_cap << "\n";
    1329         [ +  + ]:          55 :     file << "slide_enabled=" << (p.slide_enabled ? "1" : "0") << "\n";
    1330                 :          55 :     file << "slide_gain=" << p.slide_gain << "\n";
    1331                 :          55 :     file << "slide_freq=" << p.slide_freq << "\n";
    1332         [ +  - ]:          55 :     file << "road_enabled=" << (p.road_enabled ? "1" : "0") << "\n";
    1333                 :          55 :     file << "road_gain=" << p.road_gain << "\n";
    1334                 :          55 :     file << "vibration_gain=" << p.vibration_gain << "\n";
    1335                 :          55 :     file << "road_fallback_scale=" << p.road_fallback_scale << "\n";
    1336         [ -  + ]:          55 :     file << "dynamic_normalization_enabled=" << (p.general.dynamic_normalization_enabled ? "1" : "0") << "\n";
    1337         [ -  + ]:          55 :     file << "auto_load_normalization_enabled=" << (p.general.auto_load_normalization_enabled ? "1" : "0") << "\n";
    1338         [ +  - ]:          55 :     file << "soft_lock_enabled=" << (p.soft_lock_enabled ? "1" : "0") << "\n";
    1339                 :          55 :     file << "soft_lock_stiffness=" << p.soft_lock_stiffness << "\n";
    1340                 :          55 :     file << "soft_lock_damping=" << p.soft_lock_damping << "\n";
    1341         [ +  - ]:          55 :     file << "spin_enabled=" << (p.spin_enabled ? "1" : "0") << "\n";
    1342                 :          55 :     file << "spin_gain=" << p.spin_gain << "\n";
    1343                 :          55 :     file << "spin_freq_scale=" << p.spin_freq_scale << "\n";
    1344                 :          55 :     file << "scrub_drag_gain=" << p.scrub_drag_gain << "\n";
    1345         [ +  - ]:          55 :     file << "bottoming_enabled=" << (p.bottoming_enabled ? "1" : "0") << "\n";
    1346                 :          55 :     file << "bottoming_gain=" << p.bottoming_gain << "\n";
    1347                 :          55 :     file << "bottoming_method=" << p.bottoming_method << "\n";
    1348         [ -  + ]:          55 :     file << "rest_api_fallback_enabled=" << (p.rest_api_enabled ? "1" : "0") << "\n";
    1349                 :          55 :     file << "rest_api_port=" << p.rest_api_port << "\n";
    1350                 :             : 
    1351                 :          55 :     file << "safety_window_duration=" << p.safety_window_duration << "\n";
    1352                 :          55 :     file << "safety_gain_reduction=" << p.safety_gain_reduction << "\n";
    1353                 :          55 :     file << "safety_smoothing_tau=" << p.safety_smoothing_tau << "\n";
    1354                 :          55 :     file << "spike_detection_threshold=" << p.spike_detection_threshold << "\n";
    1355                 :          55 :     file << "immediate_spike_threshold=" << p.immediate_spike_threshold << "\n";
    1356                 :          55 :     file << "safety_slew_full_scale_time_s=" << p.safety_slew_full_scale_time_s << "\n";
    1357         [ -  + ]:          55 :     file << "stutter_safety_enabled=" << (p.stutter_safety_enabled ? "1" : "0") << "\n";
    1358                 :          55 :     file << "stutter_threshold=" << p.stutter_threshold << "\n";
    1359                 :             : 
    1360                 :          55 :     file << "speed_gate_lower=" << p.speed_gate_lower << "\n";
    1361                 :          55 :     file << "speed_gate_upper=" << p.speed_gate_upper << "\n";
    1362                 :          55 : }
    1363                 :             : 
    1364                 :           2 : void Config::ExportPreset(int index, const std::string& filename) {
    1365   [ +  +  +  -  :           2 :     if (index < 0 || index >= presets.size()) return;
                   +  - ]
    1366                 :             : 
    1367                 :           0 :     const Preset& p = presets[index];
    1368         [ #  # ]:           0 :     std::ofstream file(filename);
    1369         [ #  # ]:           0 :     if (file.is_open()) {
    1370   [ #  #  #  #  :           0 :         file << "[Preset:" << p.name << "]\n";
                   #  # ]
    1371         [ #  # ]:           0 :         WritePresetFields(file, p);
    1372         [ #  # ]:           0 :         file.close();
    1373   [ #  #  #  # ]:           0 :         Logger::Get().LogFile("[Config] Exported preset '%s' to %s", p.name.c_str(), filename.c_str());
    1374                 :             :     } else {
    1375   [ #  #  #  # ]:           0 :         Logger::Get().LogFile("[Config] Failed to export preset to %s", filename.c_str());
    1376                 :             :     }
    1377                 :           0 : }
    1378                 :             : 
    1379                 :           4 : bool Config::ImportPreset(const std::string& filename, const FFBEngine& engine) {
    1380         [ +  - ]:           4 :     std::ifstream file(filename);
    1381         [ +  + ]:           4 :     if (!file.is_open()) return false;
    1382                 :             : 
    1383                 :           2 :     std::string line;
    1384         [ +  - ]:           2 :     std::string current_preset_name = "";
    1385         [ +  - ]:           2 :     Preset current_preset;
    1386         [ +  - ]:           2 :     std::string current_preset_version = "";
    1387                 :           2 :     bool preset_pending = false;
    1388                 :           2 :     bool imported = false;
    1389                 :           2 :     bool legacy_torque_hack = false;
    1390                 :           2 :     float legacy_torque_val = 100.0f;
    1391                 :             : 
    1392   [ +  -  +  -  :          84 :     while (std::getline(file, line)) {
                   +  + ]
    1393                 :             :         // Strip whitespace
    1394         [ +  - ]:          82 :         line.erase(0, line.find_first_not_of(" \t\r\n"));
    1395         [ +  - ]:          82 :         line.erase(line.find_last_not_of(" \t\r\n") + 1);
    1396                 :             : 
    1397   [ +  -  +  -  :          82 :         if (line.empty() || line[0] == ';') continue;
             -  +  -  + ]
    1398                 :             : 
    1399   [ +  -  +  + ]:          82 :         if (line[0] == '[') {
    1400         [ +  + ]:           3 :             if (preset_pending) {
    1401                 :           1 :                 bool dummy_ns = false;
    1402         [ +  - ]:           1 :                 FinalizePreset(presets, current_preset, current_preset_name, current_preset_version, legacy_torque_hack, legacy_torque_val, dummy_ns, true);
    1403                 :           1 :                 imported = true;
    1404                 :           1 :                 preset_pending = false;
    1405                 :             :             }
    1406                 :             : 
    1407   [ +  -  +  - ]:           3 :             if (line.rfind("[Preset:", 0) == 0) {
    1408                 :           3 :                 size_t end_pos = line.find(']');
    1409         [ +  - ]:           3 :                 if (end_pos != std::string::npos) {
    1410         [ +  - ]:           3 :                     current_preset_name = line.substr(8, end_pos - 8);
    1411   [ +  -  +  - ]:           3 :                     current_preset = Preset(current_preset_name, false);
    1412                 :           3 :                     preset_pending = true;
    1413         [ +  - ]:           3 :                     current_preset_version = "";
    1414                 :           3 :                     legacy_torque_hack = false;
    1415                 :           3 :                     legacy_torque_val = 100.0f;
    1416                 :             :                 }
    1417                 :             :             }
    1418                 :           3 :             continue;
    1419                 :           3 :         }
    1420                 :             : 
    1421         [ +  - ]:          79 :         if (preset_pending) {
    1422                 :          79 :             bool dummy_needs_save = false;
    1423         [ +  - ]:          79 :             ParsePresetLine(line, current_preset, current_preset_version, dummy_needs_save, legacy_torque_hack, legacy_torque_val);
    1424                 :             :         }
    1425                 :             :     }
    1426                 :             : 
    1427         [ +  - ]:           2 :     if (preset_pending) {
    1428                 :           2 :         bool dummy_ns = false;
    1429         [ +  - ]:           2 :         FinalizePreset(presets, current_preset, current_preset_name, current_preset_version, legacy_torque_hack, legacy_torque_val, dummy_ns, true);
    1430                 :           2 :         imported = true;
    1431                 :             :     }
    1432                 :             : 
    1433         [ +  - ]:           2 :     if (imported) {
    1434   [ +  -  +  - ]:           2 :         Save(engine);
    1435   [ +  -  +  - ]:           2 :         Logger::Get().LogFile("[Config] Imported preset '%s' from %s", current_preset.name.c_str(), filename.c_str());
    1436                 :           2 :         return true;
    1437                 :             :     }
    1438                 :             : 
    1439                 :           0 :     return false;
    1440                 :           4 : }
    1441                 :             : 
    1442                 :           9 : void Config::AddUserPreset(const std::string& name, const FFBEngine& engine) {
    1443                 :             :     // Check if name exists and overwrite, or add new
    1444                 :           9 :     bool found = false;
    1445         [ +  + ]:         128 :     for (auto& p : presets) {
    1446   [ +  +  +  -  :         121 :         if (p.name == name && !p.is_builtin) {
                   +  + ]
    1447         [ +  - ]:           2 :             p.UpdateFromEngine(engine);
    1448                 :           2 :             found = true;
    1449                 :           2 :             break;
    1450                 :             :         }
    1451                 :             :     }
    1452                 :             :     
    1453         [ +  + ]:           9 :     if (!found) {
    1454   [ +  -  +  - ]:           7 :         Preset p(name, false);
    1455         [ +  - ]:           7 :         p.UpdateFromEngine(engine);
    1456         [ +  - ]:           7 :         presets.push_back(p);
    1457                 :           7 :     }
    1458                 :             :     
    1459                 :           9 :     m_last_preset_name = name;
    1460                 :             : 
    1461                 :             :     // Save immediately to persist
    1462   [ +  -  +  - ]:           9 :     Save(engine);
    1463                 :           9 : }
    1464                 :             : 
    1465                 :           9 : void Config::DeletePreset(int index, const FFBEngine& engine) {
    1466   [ +  +  +  +  :          12 :     if (index < 0 || index >= (int)presets.size()) return;
                   +  + ]
    1467         [ +  + ]:           6 :     if (presets[index].is_builtin) return; // Cannot delete builtin presets
    1468                 :             : 
    1469         [ +  - ]:           3 :     std::string name = presets[index].name;
    1470         [ +  - ]:           3 :     presets.erase(presets.begin() + index);
    1471   [ +  -  +  - ]:           3 :     Logger::Get().LogFile("[Config] Deleted preset: %s", name.c_str());
    1472                 :             : 
    1473                 :             :     // If the deleted preset was the last used one, reset it
    1474         [ +  + ]:           3 :     if (m_last_preset_name == name) {
    1475         [ +  - ]:           2 :         m_last_preset_name = "Default";
    1476                 :             :     }
    1477                 :             : 
    1478   [ +  -  +  - ]:           3 :     Save(engine);
    1479                 :           3 : }
    1480                 :             : 
    1481                 :           6 : void Config::DuplicatePreset(int index, const FFBEngine& engine) {
    1482   [ +  +  +  +  :           6 :     if (index < 0 || index >= (int)presets.size()) return;
                   +  + ]
    1483                 :             : 
    1484         [ +  - ]:           4 :     Preset p = presets[index];
    1485         [ +  - ]:           4 :     p.name = p.name + " (Copy)";
    1486                 :           4 :     p.is_builtin = false;
    1487         [ +  - ]:           4 :     p.app_version = LMUFFB_VERSION;
    1488                 :             : 
    1489                 :             :     // Ensure unique name
    1490         [ +  - ]:           4 :     std::string base_name = p.name;
    1491                 :           4 :     int counter = 1;
    1492                 :           4 :     bool exists = true;
    1493         [ +  + ]:          11 :     while (exists) {
    1494                 :           7 :         exists = false;
    1495         [ +  + ]:          44 :         for (const auto& existing : presets) {
    1496         [ +  + ]:          40 :             if (existing.name == p.name) {
    1497   [ +  -  +  - ]:           3 :                 p.name = base_name + " " + std::to_string(counter++);
    1498                 :           3 :                 exists = true;
    1499                 :           3 :                 break;
    1500                 :             :             }
    1501                 :             :         }
    1502                 :             :     }
    1503                 :             : 
    1504         [ +  - ]:           4 :     presets.push_back(p);
    1505         [ +  - ]:           4 :     m_last_preset_name = p.name;
    1506   [ +  -  +  - ]:           4 :     Logger::Get().LogFile("[Config] Duplicated preset to: %s", p.name.c_str());
    1507   [ +  -  +  - ]:           4 :     Save(engine);
    1508                 :           4 : }
    1509                 :             : 
    1510                 :         383 : bool Config::IsEngineDirtyRelativeToPreset(int index, const FFBEngine& engine) {
    1511   [ +  +  +  +  :         383 :     if (index < 0 || index >= (int)presets.size()) return false;
                   +  + ]
    1512                 :             : 
    1513         [ +  - ]:         380 :     Preset current_state;
    1514         [ +  - ]:         380 :     current_state.UpdateFromEngine(engine);
    1515                 :             : 
    1516                 :         380 :     return !presets[index].Equals(current_state);
    1517                 :         380 : }
    1518                 :             : 
    1519                 :         192 : void Config::SetSavedStaticLoad(const std::string& vehicleName, double value) {
    1520         [ +  - ]:         192 :     std::lock_guard<std::recursive_mutex> lock(m_static_loads_mutex);
    1521         [ +  - ]:         192 :     m_saved_static_loads[vehicleName] = value;
    1522                 :         192 : }
    1523                 :             : 
    1524                 :         418 : bool Config::GetSavedStaticLoad(const std::string& vehicleName, double& value) {
    1525         [ +  - ]:         418 :     std::lock_guard<std::recursive_mutex> lock(m_static_loads_mutex);
    1526         [ +  - ]:         418 :     auto it = m_saved_static_loads.find(vehicleName);
    1527         [ +  + ]:         418 :     if (it != m_saved_static_loads.end()) {
    1528                 :         261 :         value = it->second;
    1529                 :         261 :         return true;
    1530                 :             :     }
    1531                 :         157 :     return false;
    1532                 :         418 : }
    1533                 :             : 
    1534                 :          70 : void Config::Save(const FFBEngine& engine, const std::string& filename) {
    1535         [ +  - ]:          70 :     std::lock_guard<std::recursive_mutex> lock(g_engine_mutex);
    1536   [ +  +  +  - ]:          70 :     std::string final_path = filename.empty() ? m_config_path : filename;
    1537         [ +  - ]:          70 :     std::ofstream file(final_path);
    1538         [ +  + ]:          70 :     if (file.is_open()) {
    1539         [ +  - ]:          68 :         file << "; --- System & Window ---\n";
    1540                 :             :         // Config Version Tracking: The ini_version field serves dual purposes:
    1541                 :             :         // 1. Records the app version that last saved this config
    1542                 :             :         // 2. Acts as an implicit config format version for migration logic
    1543                 :             :         // NOTE: Currently migration is threshold-based (e.g., understeer > 2.0 = legacy).
    1544                 :             :         //       For more complex migrations, consider adding explicit config_format_version field.
    1545   [ +  -  +  -  :          68 :         file << "ini_version=" << LMUFFB_VERSION << "\n";
                   +  - ]
    1546   [ +  -  +  -  :          68 :         file << "always_on_top=" << m_always_on_top << "\n";
                   +  - ]
    1547   [ +  -  +  -  :          68 :         file << "last_device_guid=" << m_last_device_guid << "\n";
                   +  - ]
    1548   [ +  -  +  -  :          68 :         file << "last_preset_name=" << m_last_preset_name << "\n";
                   +  - ]
    1549   [ +  -  +  -  :          68 :         file << "win_pos_x=" << win_pos_x << "\n";
                   +  - ]
    1550   [ +  -  +  -  :          68 :         file << "win_pos_y=" << win_pos_y << "\n";
                   +  - ]
    1551   [ +  -  +  -  :          68 :         file << "win_w_small=" << win_w_small << "\n";
                   +  - ]
    1552   [ +  -  +  -  :          68 :         file << "win_h_small=" << win_h_small << "\n";
                   +  - ]
    1553   [ +  -  +  -  :          68 :         file << "win_w_large=" << win_w_large << "\n";
                   +  - ]
    1554   [ +  -  +  -  :          68 :         file << "win_h_large=" << win_h_large << "\n";
                   +  - ]
    1555   [ +  -  +  -  :          68 :         file << "show_graphs=" << show_graphs << "\n";
                   +  - ]
    1556   [ +  -  +  -  :          68 :         file << "auto_start_logging=" << m_auto_start_logging << "\n";
                   +  - ]
    1557   [ +  -  +  -  :          68 :         file << "log_path=" << m_log_path << "\n";
                   +  - ]
    1558                 :             : 
    1559         [ +  - ]:          68 :         file << "\n; --- General FFB ---\n";
    1560   [ +  -  +  -  :          68 :         file << "invert_force=" << engine.m_invert_force << "\n";
                   +  - ]
    1561   [ +  -  +  -  :          68 :         file << "gain=" << engine.m_general.gain << "\n";
                   +  - ]
    1562   [ +  -  +  -  :          68 :         file << "dynamic_normalization_enabled=" << engine.m_general.dynamic_normalization_enabled << "\n";
                   +  - ]
    1563   [ +  -  +  -  :          68 :         file << "auto_load_normalization_enabled=" << engine.m_general.auto_load_normalization_enabled << "\n";
                   +  - ]
    1564   [ +  -  +  -  :          68 :         file << "soft_lock_enabled=" << engine.m_soft_lock_enabled << "\n";
                   +  - ]
    1565   [ +  -  +  -  :          68 :         file << "soft_lock_stiffness=" << engine.m_soft_lock_stiffness << "\n";
                   +  - ]
    1566   [ +  -  +  -  :          68 :         file << "soft_lock_damping=" << engine.m_soft_lock_damping << "\n";
                   +  - ]
    1567   [ +  -  +  -  :          68 :         file << "wheelbase_max_nm=" << engine.m_general.wheelbase_max_nm << "\n";
                   +  - ]
    1568   [ +  -  +  -  :          68 :         file << "target_rim_nm=" << engine.m_general.target_rim_nm << "\n";
                   +  - ]
    1569   [ +  -  +  -  :          68 :         file << "min_force=" << engine.m_general.min_force << "\n";
                   +  - ]
    1570                 :             : 
    1571         [ +  - ]:          68 :         file << "\n; --- Front Axle (Understeer) ---\n";
    1572   [ +  -  +  -  :          68 :         file << "steering_shaft_gain=" << engine.m_steering_shaft_gain << "\n";
                   +  - ]
    1573   [ +  -  +  -  :          68 :         file << "ingame_ffb_gain=" << engine.m_ingame_ffb_gain << "\n";
                   +  - ]
    1574   [ +  -  +  -  :          68 :         file << "steering_shaft_smoothing=" << engine.m_steering_shaft_smoothing << "\n";
                   +  - ]
    1575   [ +  -  +  -  :          68 :         file << "understeer=" << engine.m_understeer_effect << "\n";
                   +  - ]
    1576   [ +  -  +  -  :          68 :         file << "understeer_gamma=" << engine.m_understeer_gamma << "\n";
                   +  - ]
    1577   [ +  -  +  -  :          68 :         file << "torque_source=" << engine.m_torque_source << "\n";
                   +  - ]
    1578   [ +  -  +  -  :          68 :         file << "steering_100hz_reconstruction=" << engine.m_steering_100hz_reconstruction << "\n";
                   +  - ]
    1579   [ +  -  +  -  :          68 :         file << "torque_passthrough=" << engine.m_torque_passthrough << "\n";
                   +  - ]
    1580   [ +  -  +  -  :          68 :         file << "flatspot_suppression=" << engine.m_flatspot_suppression << "\n";
                   +  - ]
    1581   [ +  -  +  -  :          68 :         file << "notch_q=" << engine.m_notch_q << "\n";
                   +  - ]
    1582   [ +  -  +  -  :          68 :         file << "flatspot_strength=" << engine.m_flatspot_strength << "\n";
                   +  - ]
    1583   [ +  -  +  -  :          68 :         file << "static_notch_enabled=" << engine.m_static_notch_enabled << "\n";
                   +  - ]
    1584   [ +  -  +  -  :          68 :         file << "static_notch_freq=" << engine.m_static_notch_freq << "\n";
                   +  - ]
    1585   [ +  -  +  -  :          68 :         file << "static_notch_width=" << engine.m_static_notch_width << "\n";
                   +  - ]
    1586                 :             : 
    1587         [ +  - ]:          68 :         file << "\n; --- Rear Axle (Oversteer) ---\n";
    1588   [ +  -  +  -  :          68 :         file << "oversteer_boost=" << engine.m_oversteer_boost << "\n";
                   +  - ]
    1589   [ +  -  +  -  :          68 :         file << "long_load_effect=" << engine.m_long_load_effect << "\n";
                   +  - ]
    1590   [ +  -  +  -  :          68 :         file << "long_load_smoothing=" << engine.m_long_load_smoothing << "\n";
                   +  - ]
    1591   [ +  -  +  -  :          68 :         file << "long_load_transform=" << static_cast<int>(engine.m_long_load_transform) << "\n";
                   +  - ]
    1592   [ +  -  +  -  :          68 :         file << "grip_smoothing_steady=" << engine.m_grip_smoothing_steady << "\n";
                   +  - ]
    1593   [ +  -  +  -  :          68 :         file << "grip_smoothing_fast=" << engine.m_grip_smoothing_fast << "\n";
                   +  - ]
    1594   [ +  -  +  -  :          68 :         file << "grip_smoothing_sensitivity=" << engine.m_grip_smoothing_sensitivity << "\n";
                   +  - ]
    1595   [ +  -  +  -  :          68 :         file << "sop=" << engine.m_sop_effect << "\n";
                   +  - ]
    1596   [ +  -  +  -  :          68 :         file << "lateral_load_effect=" << engine.m_lat_load_effect << "\n";
                   +  - ]
    1597   [ +  -  +  -  :          68 :         file << "lat_load_transform=" << static_cast<int>(engine.m_lat_load_transform) << "\n";
                   +  - ]
    1598   [ +  -  +  -  :          68 :         file << "rear_align_effect=" << engine.m_rear_align_effect << "\n";
                   +  - ]
    1599   [ +  -  +  -  :          68 :         file << "kerb_strike_rejection=" << engine.m_kerb_strike_rejection << "\n";
                   +  - ]
    1600   [ +  -  +  -  :          68 :         file << "sop_yaw_gain=" << engine.m_sop_yaw_gain << "\n";
                   +  - ]
    1601   [ +  -  +  -  :          68 :         file << "yaw_kick_threshold=" << engine.m_yaw_kick_threshold << "\n";
                   +  - ]
    1602   [ +  -  +  -  :          68 :         file << "unloaded_yaw_gain=" << engine.m_unloaded_yaw_gain << "\n";
                   +  - ]
    1603   [ +  -  +  -  :          68 :         file << "unloaded_yaw_threshold=" << engine.m_unloaded_yaw_threshold << "\n";
                   +  - ]
    1604   [ +  -  +  -  :          68 :         file << "unloaded_yaw_sens=" << engine.m_unloaded_yaw_sens << "\n";
                   +  - ]
    1605   [ +  -  +  -  :          68 :         file << "unloaded_yaw_gamma=" << engine.m_unloaded_yaw_gamma << "\n";
                   +  - ]
    1606   [ +  -  +  -  :          68 :         file << "unloaded_yaw_punch=" << engine.m_unloaded_yaw_punch << "\n";
                   +  - ]
    1607   [ +  -  +  -  :          68 :         file << "power_yaw_gain=" << engine.m_power_yaw_gain << "\n";
                   +  - ]
    1608   [ +  -  +  -  :          68 :         file << "power_yaw_threshold=" << engine.m_power_yaw_threshold << "\n";
                   +  - ]
    1609   [ +  -  +  -  :          68 :         file << "power_slip_threshold=" << engine.m_power_slip_threshold << "\n";
                   +  - ]
    1610   [ +  -  +  -  :          68 :         file << "power_yaw_gamma=" << engine.m_power_yaw_gamma << "\n";
                   +  - ]
    1611   [ +  -  +  -  :          68 :         file << "power_yaw_punch=" << engine.m_power_yaw_punch << "\n";
                   +  - ]
    1612   [ +  -  +  -  :          68 :         file << "yaw_accel_smoothing=" << engine.m_yaw_accel_smoothing << "\n";
                   +  - ]
    1613   [ +  -  +  -  :          68 :         file << "gyro_gain=" << engine.m_gyro_gain << "\n";
                   +  - ]
    1614   [ +  -  +  -  :          68 :     file << "stationary_damping=" << engine.m_stationary_damping << "\n";
                   +  - ]
    1615   [ +  -  +  -  :          68 :         file << "gyro_smoothing_factor=" << engine.m_gyro_smoothing << "\n";
                   +  - ]
    1616   [ +  -  +  -  :          68 :         file << "sop_smoothing_factor=" << engine.m_sop_smoothing_factor << "\n";
                   +  - ]
    1617   [ +  -  +  -  :          68 :         file << "sop_scale=" << engine.m_sop_scale << "\n";
                   +  - ]
    1618   [ +  -  +  -  :          68 :         file << "understeer_affects_sop=" << engine.m_understeer_affects_sop << "\n";
                   +  - ]
    1619                 :             : 
    1620         [ +  - ]:          68 :         file << "\n; --- Physics (Grip & Slip Angle) ---\n";
    1621   [ +  -  +  -  :          68 :         file << "slip_angle_smoothing=" << engine.m_slip_angle_smoothing << "\n";
                   +  - ]
    1622   [ +  -  +  -  :          68 :         file << "chassis_inertia_smoothing=" << engine.m_chassis_inertia_smoothing << "\n";
                   +  - ]
    1623   [ +  -  +  -  :          68 :         file << "load_sensitivity_enabled=" << engine.m_load_sensitivity_enabled << "\n";
                   +  - ]
    1624   [ +  -  +  -  :          68 :         file << "optimal_slip_angle=" << engine.m_optimal_slip_angle << "\n";
                   +  - ]
    1625   [ +  -  +  -  :          68 :         file << "optimal_slip_ratio=" << engine.m_optimal_slip_ratio << "\n";
                   +  - ]
    1626   [ +  -  +  -  :          68 :         file << "slope_detection_enabled=" << engine.m_slope_detection_enabled << "\n";
                   +  - ]
    1627   [ +  -  +  -  :          68 :         file << "slope_sg_window=" << engine.m_slope_sg_window << "\n";
                   +  - ]
    1628   [ +  -  +  -  :          68 :         file << "slope_sensitivity=" << engine.m_slope_sensitivity << "\n";
                   +  - ]
    1629                 :             : 
    1630   [ +  -  +  -  :          68 :         file << "slope_smoothing_tau=" << engine.m_slope_smoothing_tau << "\n";
                   +  - ]
    1631   [ +  -  +  -  :          68 :         file << "slope_min_threshold=" << engine.m_slope_min_threshold << "\n";
                   +  - ]
    1632   [ +  -  +  -  :          68 :         file << "slope_max_threshold=" << engine.m_slope_max_threshold << "\n";
                   +  - ]
    1633   [ +  -  +  -  :          68 :         file << "slope_alpha_threshold=" << engine.m_slope_alpha_threshold << "\n";
                   +  - ]
    1634   [ +  -  +  -  :          68 :         file << "slope_decay_rate=" << engine.m_slope_decay_rate << "\n";
                   +  - ]
    1635   [ +  -  +  -  :          68 :         file << "slope_confidence_enabled=" << engine.m_slope_confidence_enabled << "\n";
                   +  - ]
    1636   [ +  -  +  -  :          68 :         file << "slope_g_slew_limit=" << engine.m_slope_g_slew_limit << "\n";
                   +  - ]
    1637   [ +  -  +  -  :          68 :         file << "slope_use_torque=" << (engine.m_slope_use_torque ? "1" : "0") << "\n";
             +  -  +  - ]
    1638   [ +  -  +  -  :          68 :         file << "slope_torque_sensitivity=" << engine.m_slope_torque_sensitivity << "\n";
                   +  - ]
    1639   [ +  -  +  -  :          68 :         file << "slope_confidence_max_rate=" << engine.m_slope_confidence_max_rate << "\n";
                   +  - ]
    1640                 :             : 
    1641         [ +  - ]:          68 :         file << "\n; --- Braking & Lockup ---\n";
    1642   [ +  -  +  -  :          68 :         file << "lockup_enabled=" << engine.m_lockup_enabled << "\n";
                   +  - ]
    1643   [ +  -  +  -  :          68 :         file << "lockup_gain=" << engine.m_lockup_gain << "\n";
                   +  - ]
    1644   [ +  -  +  -  :          68 :         file << "brake_load_cap=" << engine.m_brake_load_cap << "\n";
                   +  - ]
    1645   [ +  -  +  -  :          68 :         file << "lockup_freq_scale=" << engine.m_lockup_freq_scale << "\n";
                   +  - ]
    1646   [ +  -  +  -  :          68 :         file << "lockup_gamma=" << engine.m_lockup_gamma << "\n";
                   +  - ]
    1647   [ +  -  +  -  :          68 :         file << "lockup_start_pct=" << engine.m_lockup_start_pct << "\n";
                   +  - ]
    1648   [ +  -  +  -  :          68 :         file << "lockup_full_pct=" << engine.m_lockup_full_pct << "\n";
                   +  - ]
    1649   [ +  -  +  -  :          68 :         file << "lockup_prediction_sens=" << engine.m_lockup_prediction_sens << "\n";
                   +  - ]
    1650   [ +  -  +  -  :          68 :         file << "lockup_bump_reject=" << engine.m_lockup_bump_reject << "\n";
                   +  - ]
    1651   [ +  -  +  -  :          68 :         file << "lockup_rear_boost=" << engine.m_lockup_rear_boost << "\n";
                   +  - ]
    1652   [ +  -  +  -  :          68 :         file << "abs_pulse_enabled=" << engine.m_abs_pulse_enabled << "\n";
                   +  - ]
    1653   [ +  -  +  -  :          68 :         file << "abs_gain=" << engine.m_abs_gain << "\n";
                   +  - ]
    1654   [ +  -  +  -  :          68 :         file << "abs_freq=" << engine.m_abs_freq_hz << "\n";
                   +  - ]
    1655                 :             : 
    1656         [ +  - ]:          68 :         file << "\n; --- Vibration Effects ---\n";
    1657   [ +  -  +  -  :          68 :         file << "texture_load_cap=" << engine.m_texture_load_cap << "\n";
                   +  - ]
    1658   [ +  -  +  -  :          68 :         file << "slide_enabled=" << engine.m_slide_texture_enabled << "\n";
                   +  - ]
    1659   [ +  -  +  -  :          68 :         file << "slide_gain=" << engine.m_slide_texture_gain << "\n";
                   +  - ]
    1660   [ +  -  +  -  :          68 :         file << "slide_freq=" << engine.m_slide_freq_scale << "\n";
                   +  - ]
    1661   [ +  -  +  -  :          68 :         file << "road_enabled=" << engine.m_road_texture_enabled << "\n";
                   +  - ]
    1662   [ +  -  +  -  :          68 :         file << "road_gain=" << engine.m_road_texture_gain << "\n";
                   +  - ]
    1663   [ +  -  +  -  :          68 :         file << "vibration_gain=" << engine.m_vibration_gain << "\n";
                   +  - ]
    1664   [ +  -  +  -  :          68 :         file << "road_fallback_scale=" << engine.m_road_fallback_scale << "\n";
                   +  - ]
    1665   [ +  -  +  -  :          68 :         file << "spin_enabled=" << engine.m_spin_enabled << "\n";
                   +  - ]
    1666   [ +  -  +  -  :          68 :         file << "spin_gain=" << engine.m_spin_gain << "\n";
                   +  - ]
    1667   [ +  -  +  -  :          68 :         file << "spin_freq_scale=" << engine.m_spin_freq_scale << "\n";
                   +  - ]
    1668   [ +  -  +  -  :          68 :         file << "scrub_drag_gain=" << engine.m_scrub_drag_gain << "\n";
                   +  - ]
    1669   [ +  -  +  +  :          68 :         file << "bottoming_enabled=" << (engine.m_bottoming_enabled ? "1" : "0") << "\n";
             +  -  +  - ]
    1670   [ +  -  +  -  :          68 :         file << "bottoming_gain=" << engine.m_bottoming_gain << "\n";
                   +  - ]
    1671   [ +  -  +  -  :          68 :         file << "bottoming_method=" << engine.m_bottoming_method << "\n";
                   +  - ]
    1672   [ +  -  +  -  :          68 :         file << "rest_api_fallback_enabled=" << engine.m_rest_api_enabled << "\n";
                   +  - ]
    1673   [ +  -  +  -  :          68 :         file << "rest_api_port=" << engine.m_rest_api_port << "\n";
                   +  - ]
    1674                 :             : 
    1675   [ +  -  +  -  :          68 :         file << "safety_window_duration=" << engine.m_safety.m_safety_window_duration << "\n";
                   +  - ]
    1676   [ +  -  +  -  :          68 :         file << "safety_gain_reduction=" << engine.m_safety.m_safety_gain_reduction << "\n";
                   +  - ]
    1677   [ +  -  +  -  :          68 :         file << "safety_smoothing_tau=" << engine.m_safety.m_safety_smoothing_tau << "\n";
                   +  - ]
    1678   [ +  -  +  -  :          68 :         file << "spike_detection_threshold=" << engine.m_safety.m_spike_detection_threshold << "\n";
                   +  - ]
    1679   [ +  -  +  -  :          68 :         file << "immediate_spike_threshold=" << engine.m_safety.m_immediate_spike_threshold << "\n";
                   +  - ]
    1680   [ +  -  +  -  :          68 :         file << "safety_slew_full_scale_time_s=" << engine.m_safety.m_safety_slew_full_scale_time_s << "\n";
                   +  - ]
    1681   [ +  -  +  -  :          68 :         file << "stutter_safety_enabled=" << engine.m_safety.m_stutter_safety_enabled << "\n";
                   +  - ]
    1682   [ +  -  +  -  :          68 :         file << "stutter_threshold=" << engine.m_safety.m_stutter_threshold << "\n";
                   +  - ]
    1683                 :             : 
    1684         [ +  - ]:          68 :         file << "\n; --- Advanced Settings ---\n";
    1685   [ +  -  +  -  :          68 :         file << "speed_gate_lower=" << engine.m_speed_gate_lower << "\n";
                   +  - ]
    1686   [ +  -  +  -  :          68 :         file << "speed_gate_upper=" << engine.m_speed_gate_upper << "\n";
                   +  - ]
    1687                 :             : 
    1688         [ +  - ]:          68 :         file << "\n[StaticLoads]\n";
    1689                 :             :         {
    1690         [ +  - ]:          68 :             std::lock_guard<std::recursive_mutex> static_lock(m_static_loads_mutex);
    1691         [ +  + ]:         476 :             for (const auto& pair : m_saved_static_loads) {
    1692   [ +  -  +  -  :         408 :                 file << pair.first << "=" << pair.second << "\n";
             +  -  +  - ]
    1693                 :             :             }
    1694                 :          68 :         }
    1695                 :             : 
    1696         [ +  - ]:          68 :         file << "\n[Presets]\n";
    1697         [ +  + ]:        1321 :         for (const auto& p : presets) {
    1698         [ +  + ]:        1253 :             if (!p.is_builtin) {
    1699   [ +  -  +  -  :          55 :                 file << "[Preset:" << p.name << "]\n";
                   +  - ]
    1700         [ +  - ]:          55 :                 WritePresetFields(file, p);
    1701         [ +  - ]:          55 :                 file << "\n";
    1702                 :             :             }
    1703                 :             :         }
    1704                 :             :         
    1705         [ +  - ]:          68 :         file.close();
    1706                 :             : 
    1707                 :             :     } else {
    1708   [ +  -  +  - ]:           2 :         Logger::Get().LogFile("[Config] Failed to save to %s", final_path.c_str());
    1709                 :             :     }
    1710                 :          70 : }
    1711                 :             : 
    1712                 :          67 : void Config::Load(FFBEngine& engine, const std::string& filename) {
    1713         [ +  - ]:          67 :     std::lock_guard<std::recursive_mutex> lock(g_engine_mutex);
    1714                 :             : 
    1715                 :             :     // v0.7.204: Ensure preset library is loaded before processing main config.
    1716                 :             :     // This prevents auto-save (m_needs_save) from overwriting user presets
    1717                 :             :     // with an empty vector if the GUI hasn't been opened yet.
    1718         [ +  - ]:          67 :     LoadPresets();
    1719                 :             : 
    1720   [ +  +  +  - ]:          67 :     std::string final_path = filename.empty() ? m_config_path : filename;
    1721         [ +  - ]:          67 :     std::ifstream file(final_path);
    1722         [ +  + ]:          67 :     if (!file.is_open()) {
    1723   [ +  -  +  - ]:           3 :         Logger::Get().LogFile("[Config] No config found, using defaults.");
    1724                 :           3 :         return;
    1725                 :             :     }
    1726                 :             : 
    1727                 :          64 :     std::string line;
    1728                 :          64 :     bool in_static_loads = false;
    1729                 :          64 :     bool in_presets = false;
    1730         [ +  - ]:          64 :     std::string config_version = "";
    1731                 :          64 :     bool legacy_torque_hack = false;
    1732                 :          64 :     float legacy_torque_val = 100.0f;
    1733                 :             : 
    1734   [ +  -  +  -  :        5520 :     while (std::getline(file, line)) {
                   +  + ]
    1735                 :             :         // Strip whitespace and check for section headers
    1736         [ +  - ]:        5456 :         line.erase(0, line.find_first_not_of(" \t\r\n"));
    1737         [ +  - ]:        5456 :         line.erase(line.find_last_not_of(" \t\r\n") + 1);
    1738   [ +  +  +  -  :       10220 :         if (line.empty() || line[0] == ';') continue;
             +  +  +  + ]
    1739                 :             : 
    1740   [ +  -  +  + ]:        4968 :         if (line[0] == '[') {
    1741   [ +  -  +  + ]:          80 :             if (line == "[StaticLoads]") {
    1742                 :          32 :                 in_static_loads = true;
    1743                 :          32 :                 in_presets = false;
    1744   [ +  -  +  +  :          48 :             } else if (line == "[Presets]" || line.rfind("[Preset:", 0) == 0) {
          +  -  +  +  +  
                      + ]
    1745                 :          42 :                 in_presets = true;
    1746                 :          42 :                 in_static_loads = false;
    1747                 :             :             } else {
    1748                 :           6 :                 in_static_loads = false;
    1749                 :           6 :                 in_presets = false;
    1750                 :             :             }
    1751                 :          80 :             continue;
    1752                 :             :         }
    1753                 :             : 
    1754         [ +  + ]:        4888 :         if (in_presets) continue;
    1755                 :             : 
    1756         [ +  - ]:        4090 :         std::istringstream is_line(line);
    1757                 :        4090 :         std::string key;
    1758   [ +  -  +  -  :        4090 :         if (std::getline(is_line, key, '=')) {
                   +  - ]
    1759                 :        4090 :             std::string value;
    1760   [ +  -  +  -  :        4090 :             if (std::getline(is_line, value)) {
                   +  + ]
    1761                 :             :                 try {
    1762         [ +  + ]:        4078 :                     if (in_static_loads) {
    1763   [ +  -  +  - ]:         148 :                         SetSavedStaticLoad(key, std::stod(value));
    1764                 :             :                     }
    1765   [ +  -  +  + ]:        3930 :                     else if (key == "ini_version") {
    1766         [ +  - ]:          36 :                         config_version = value;
    1767   [ +  -  +  - ]:          36 :                         Logger::Get().LogFile("[Config] Loading config version: %s", config_version.c_str());
    1768                 :             :                     }
    1769                 :        4078 :                     bool ns = m_needs_save.load();
    1770   [ +  +  +  + ]:        7376 :                     if (SyncSystemLine(key, value, engine, config_version, legacy_torque_hack, legacy_torque_val, ns)) { m_needs_save.store(ns); continue; }
    1771   [ +  +  +  + ]:        3489 :                     else if (SyncPhysicsLine(key, value, engine, config_version, ns)) { m_needs_save.store(ns); continue; }
    1772   [ +  -  +  + ]:        1623 :                     else if (SyncBrakingLine(key, value, engine)) continue;
    1773   [ +  -  +  + ]:        1202 :                     else if (SyncVibrationLine(key, value, engine)) continue;
    1774   [ +  -  +  + ]:         414 :                     else if (SyncSafetyLine(key, value, engine)) continue;
    1775                 :           2 :                 } catch (...) {
    1776   [ +  -  +  - ]:           2 :                     Logger::Get().Log("[Config] Error parsing line: %s", line.c_str());
    1777         [ +  - ]:           2 :                 }
    1778                 :             :             }
    1779         [ +  + ]:        4090 :         }
    1780   [ +  +  +  + ]:        7976 :     }
    1781                 :             :     
    1782                 :             :     // v0.7.16: Comprehensive Safety Validation & Clamping
    1783                 :             :     // These checks ensure that even if config.ini is manually edited with invalid values,
    1784                 :             :     // the engine remains stable and doesn't crash or produce NaN.
    1785                 :             : 
    1786         [ +  - ]:          64 :     engine.m_general.Validate();
    1787                 :          64 :     engine.m_sop_scale = (std::max)(0.01f, engine.m_sop_scale);
    1788                 :          64 :     engine.m_slip_angle_smoothing = (std::max)(0.0001f, engine.m_slip_angle_smoothing);
    1789                 :          64 :     engine.m_notch_q = (std::max)(0.1f, engine.m_notch_q);
    1790                 :          64 :     engine.m_static_notch_width = (std::max)(0.1f, engine.m_static_notch_width);
    1791                 :          64 :     engine.m_speed_gate_upper = (std::max)(0.1f, engine.m_speed_gate_upper);
    1792                 :             : 
    1793                 :          64 :     engine.m_torque_source = (std::max)(0, (std::min)(1, engine.m_torque_source));
    1794                 :          64 :     engine.m_gyro_gain = (std::max)(0.0f, (std::min)(1.0f, engine.m_gyro_gain));
    1795                 :          64 :     engine.m_stationary_damping = (std::max)(0.0f, (std::min)(1.0f, engine.m_stationary_damping));
    1796                 :          64 :     engine.m_scrub_drag_gain = (std::max)(0.0f, (std::min)(1.0f, engine.m_scrub_drag_gain));
    1797                 :             : 
    1798         [ +  + ]:          64 :     if (engine.m_optimal_slip_angle < 0.01f) {
    1799   [ +  -  +  - ]:           4 :         Logger::Get().Log("[Config] Invalid optimal_slip_angle (%.2f), resetting to default 0.10", engine.m_optimal_slip_angle);
    1800   [ +  -  +  - ]:           4 :         Logger::Get().Log("[Config] Invalid optimal_slip_angle (%.2f), resetting to default 0.10", engine.m_optimal_slip_angle);
    1801                 :           4 :         engine.m_optimal_slip_angle = 0.10f;
    1802                 :             :     }
    1803         [ +  + ]:          64 :     if (engine.m_optimal_slip_ratio < 0.01f) {
    1804   [ +  -  +  - ]:           2 :         Logger::Get().Log("[Config] Invalid optimal_slip_ratio (%.2f), resetting to default 0.12", engine.m_optimal_slip_ratio);
    1805   [ +  -  +  - ]:           2 :         Logger::Get().Log("[Config] Invalid optimal_slip_ratio (%.2f), resetting to default 0.12", engine.m_optimal_slip_ratio);
    1806                 :           2 :         engine.m_optimal_slip_ratio = 0.12f;
    1807                 :             :     }
    1808                 :             :     
    1809                 :             :     // Slope Detection Validation
    1810         [ +  + ]:          64 :     if (engine.m_slope_sg_window < 5) engine.m_slope_sg_window = 5;
    1811         [ -  + ]:          64 :     if (engine.m_slope_sg_window > 41) engine.m_slope_sg_window = 41;
    1812         [ +  + ]:          64 :     if (engine.m_slope_sg_window % 2 == 0) engine.m_slope_sg_window++; // Must be odd
    1813         [ -  + ]:          64 :     if (engine.m_slope_sensitivity < 0.1f) engine.m_slope_sensitivity = 0.1f;
    1814         [ -  + ]:          64 :     if (engine.m_slope_sensitivity > 10.0f) engine.m_slope_sensitivity = 10.0f;
    1815         [ -  + ]:          64 :     if (engine.m_slope_smoothing_tau < 0.001f) engine.m_slope_smoothing_tau = 0.04f;
    1816                 :             :     
    1817   [ +  +  +  + ]:          64 :     if (engine.m_slope_alpha_threshold < 0.001f || engine.m_slope_alpha_threshold > 0.1f) {
    1818   [ +  -  +  - ]:           2 :         Logger::Get().Log("[Config] Invalid slope_alpha_threshold (%.3f), resetting to 0.02f", engine.m_slope_alpha_threshold);
    1819   [ +  -  +  - ]:           2 :         Logger::Get().Log("[Config] Invalid slope_alpha_threshold (%.3f), resetting to 0.02f", engine.m_slope_alpha_threshold);
    1820                 :           2 :         engine.m_slope_alpha_threshold = 0.02f;
    1821                 :             :     }
    1822   [ +  -  -  + ]:          64 :     if (engine.m_slope_decay_rate < 0.1f || engine.m_slope_decay_rate > 20.0f) {
    1823   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Invalid slope_decay_rate (%.2f), resetting to 5.0f", engine.m_slope_decay_rate);
    1824   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Invalid slope_decay_rate (%.2f), resetting to 5.0f", engine.m_slope_decay_rate);
    1825                 :           0 :         engine.m_slope_decay_rate = 5.0f;
    1826                 :             :     }
    1827                 :             : 
    1828                 :             :     // Advanced Slope Validation (v0.7.40)
    1829                 :          64 :     engine.m_slope_g_slew_limit = (std::max)(1.0f, (std::min)(1000.0f, engine.m_slope_g_slew_limit));
    1830                 :          64 :     engine.m_slope_torque_sensitivity = (std::max)(0.01f, (std::min)(10.0f, engine.m_slope_torque_sensitivity));
    1831                 :          64 :     engine.m_slope_confidence_max_rate = (std::max)(engine.m_slope_alpha_threshold + 0.01f, (std::min)(1.0f, engine.m_slope_confidence_max_rate));
    1832                 :             : 
    1833                 :             :     // Migration: v0.7.x sensitivity → v0.7.11 thresholds
    1834                 :             :     // If loading old config with sensitivity but at default thresholds
    1835         [ +  + ]:          64 :     if (engine.m_slope_min_threshold == -0.3f && 
    1836         [ +  - ]:          58 :         engine.m_slope_max_threshold == -2.0f &&
    1837         [ +  + ]:          58 :         engine.m_slope_sensitivity != 0.5f) {
    1838                 :             :         
    1839                 :             :         // Old formula: factor = 1 - (excess * 0.1 * sens)
    1840                 :             :         // At factor=0.2 (floor): excess * 0.1 * sens = 0.8
    1841                 :             :         // excess = 0.8 / (0.1 * sens) = 8 / sens
    1842                 :             :         // max = min - excess = -0.3 - (8/sens)
    1843                 :           3 :         double sens = (double)engine.m_slope_sensitivity;
    1844         [ +  - ]:           3 :         if (sens > 0.01) {
    1845                 :           3 :             engine.m_slope_max_threshold = (float)(engine.m_slope_min_threshold - (8.0 / sens));
    1846   [ +  -  +  - ]:           3 :             Logger::Get().Log("[Config] Migrated slope_sensitivity %.2f to max_threshold %.2f", sens, engine.m_slope_max_threshold);
    1847   [ +  -  +  - ]:           3 :             Logger::Get().Log("[Config] Migrated slope_sensitivity %.2f to max_threshold %.2f", sens, engine.m_slope_max_threshold);
    1848                 :             :         }
    1849                 :             :     }
    1850                 :             : 
    1851                 :             :     // Validation: max should be more negative than min
    1852         [ +  + ]:          64 :     if (engine.m_slope_max_threshold > engine.m_slope_min_threshold) {
    1853                 :           2 :         std::swap(engine.m_slope_min_threshold, engine.m_slope_max_threshold);
    1854   [ +  -  +  - ]:           2 :         Logger::Get().Log("[Config] Swapped slope thresholds (min should be > max)");
    1855   [ +  -  +  - ]:           2 :         Logger::Get().Log("[Config] Swapped slope thresholds (min should be > max)");
    1856                 :             :     }
    1857                 :             :     
    1858                 :             :     // v0.6.20: Safety Validation - Clamp Advanced Braking Parameters to Valid Ranges
    1859   [ +  +  -  + ]:          64 :     if (engine.m_lockup_gamma < 0.1f || engine.m_lockup_gamma > 4.0f) {
    1860   [ +  -  +  - ]:           1 :         Logger::Get().Log("[Config] Invalid lockup_gamma (%.2f), clamping to range [0.1, 4.0]", engine.m_lockup_gamma);
    1861   [ +  -  +  - ]:           1 :         Logger::Get().Log("[Config] Invalid lockup_gamma (%.2f), clamping to range [0.1, 4.0]", engine.m_lockup_gamma);
    1862                 :           1 :         engine.m_lockup_gamma = (std::max)(0.1f, (std::min)(4.0f, engine.m_lockup_gamma));
    1863                 :             :     }
    1864   [ +  -  -  + ]:          64 :     if (engine.m_lockup_prediction_sens < 10.0f || engine.m_lockup_prediction_sens > 100.0f) {
    1865   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Invalid lockup_prediction_sens (%.2f), clamping to range [10.0, 100.0]", engine.m_lockup_prediction_sens);
    1866   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Invalid lockup_prediction_sens (%.2f), clamping to range [10.0, 100.0]", engine.m_lockup_prediction_sens);
    1867                 :           0 :         engine.m_lockup_prediction_sens = (std::max)(10.0f, (std::min)(100.0f, engine.m_lockup_prediction_sens));
    1868                 :             :     }
    1869   [ +  +  -  + ]:          64 :     if (engine.m_lockup_bump_reject < 0.1f || engine.m_lockup_bump_reject > 5.0f) {
    1870   [ +  -  +  - ]:           1 :         Logger::Get().Log("[Config] Invalid lockup_bump_reject (%.2f), clamping to range [0.1, 5.0]", engine.m_lockup_bump_reject);
    1871   [ +  -  +  - ]:           1 :         Logger::Get().Log("[Config] Invalid lockup_bump_reject (%.2f), clamping to range [0.1, 5.0]", engine.m_lockup_bump_reject);
    1872                 :           1 :         engine.m_lockup_bump_reject = (std::max)(0.1f, (std::min)(5.0f, engine.m_lockup_bump_reject));
    1873                 :             :     }
    1874   [ +  -  -  + ]:          64 :     if (engine.m_abs_gain < 0.0f || engine.m_abs_gain > 10.0f) {
    1875   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Invalid abs_gain (%.2f), clamping to range [0.0, 10.0]", engine.m_abs_gain);
    1876   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Invalid abs_gain (%.2f), clamping to range [0.0, 10.0]", engine.m_abs_gain);
    1877                 :           0 :         engine.m_abs_gain = (std::max)(0.0f, (std::min)(10.0f, engine.m_abs_gain));
    1878                 :             :     }
    1879                 :             :     // Issue #211: Legacy 100Nm hack scaling
    1880   [ +  +  +  -  :          76 :     if (legacy_torque_hack && IsVersionLessEqual(config_version, "0.7.66")) {
          +  -  +  -  +  
          +  +  +  +  +  
             -  -  -  - ]
    1881                 :           6 :         engine.m_general.gain *= (15.0f / legacy_torque_val);
    1882   [ +  -  +  - ]:           6 :         Logger::Get().Log("[Config] Migrated legacy 100Nm hack for main config. Scaling gain.");
    1883   [ +  -  +  - ]:           6 :         Logger::Get().Log("[Config] Migrated legacy 100Nm hack for main config. Scaling gain.");
    1884                 :           6 :         m_needs_save = true;
    1885                 :             :     }
    1886                 :             : 
    1887                 :             :     // Legacy Migration: Convert 0-200 range to 0-2.0 range
    1888         [ -  + ]:          64 :     if (engine.m_understeer_effect > 2.0f) {
    1889                 :           0 :         float old_val = engine.m_understeer_effect;
    1890                 :           0 :         engine.m_understeer_effect = engine.m_understeer_effect / 100.0f;
    1891   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Migrated legacy understeer_effect: %.2f -> %.2f", old_val, engine.m_understeer_effect);
    1892   [ #  #  #  # ]:           0 :         Logger::Get().Log("[Config] Migrated legacy understeer_effect: %.2f -> %.2f", old_val, engine.m_understeer_effect);
    1893                 :             :     }
    1894                 :             :     // Clamp to new valid range [0.0, 2.0]
    1895   [ +  -  -  + ]:          64 :     if (engine.m_understeer_effect < 0.0f || engine.m_understeer_effect > 2.0f) {
    1896                 :           0 :         engine.m_understeer_effect = (std::max)(0.0f, (std::min)(2.0f, engine.m_understeer_effect));
    1897                 :             :     }
    1898   [ +  -  -  + ]:          64 :     if (engine.m_steering_shaft_gain < 0.0f || engine.m_steering_shaft_gain > 2.0f) {
    1899                 :           0 :         engine.m_steering_shaft_gain = (std::max)(0.0f, (std::min)(2.0f, engine.m_steering_shaft_gain));
    1900                 :             :     }
    1901   [ +  -  -  + ]:          64 :     if (engine.m_ingame_ffb_gain < 0.0f || engine.m_ingame_ffb_gain > 2.0f) {
    1902                 :           0 :         engine.m_ingame_ffb_gain = (std::max)(0.0f, (std::min)(2.0f, engine.m_ingame_ffb_gain));
    1903                 :             :     }
    1904   [ +  -  +  + ]:          64 :     if (engine.m_lockup_gain < 0.0f || engine.m_lockup_gain > 3.0f) {
    1905                 :           2 :         engine.m_lockup_gain = (std::max)(0.0f, (std::min)(3.0f, engine.m_lockup_gain));
    1906                 :             :     }
    1907   [ +  +  +  + ]:          64 :     if (engine.m_brake_load_cap < 1.0f || engine.m_brake_load_cap > 10.0f) {
    1908                 :           2 :         engine.m_brake_load_cap = (std::max)(1.0f, (std::min)(10.0f, engine.m_brake_load_cap));
    1909                 :             :     }
    1910   [ +  +  -  + ]:          64 :     if (engine.m_lockup_rear_boost < 1.0f || engine.m_lockup_rear_boost > 10.0f) {
    1911                 :           1 :         engine.m_lockup_rear_boost = (std::max)(1.0f, (std::min)(10.0f, engine.m_lockup_rear_boost));
    1912                 :             :     }
    1913   [ +  -  -  + ]:          64 :     if (engine.m_oversteer_boost < 0.0f || engine.m_oversteer_boost > 4.0f) {
    1914                 :           0 :         engine.m_oversteer_boost = (std::max)(0.0f, (std::min)(4.0f, engine.m_oversteer_boost));
    1915                 :             :     }
    1916   [ +  -  +  + ]:          64 :     if (engine.m_sop_yaw_gain < 0.0f || engine.m_sop_yaw_gain > 1.0f) {
    1917                 :           1 :          engine.m_sop_yaw_gain = (std::max)(0.0f, (std::min)(1.0f, engine.m_sop_yaw_gain));
    1918                 :             :     }
    1919   [ +  -  +  + ]:          64 :     if (engine.m_slide_texture_gain < 0.0f || engine.m_slide_texture_gain > 2.0f) {
    1920                 :           1 :         engine.m_slide_texture_gain = (std::max)(0.0f, (std::min)(2.0f, engine.m_slide_texture_gain));
    1921                 :             :     }
    1922   [ +  -  +  + ]:          64 :     if (engine.m_road_texture_gain < 0.0f || engine.m_road_texture_gain > 2.0f) {
    1923                 :           1 :         engine.m_road_texture_gain = (std::max)(0.0f, (std::min)(2.0f, engine.m_road_texture_gain));
    1924                 :             :     }
    1925   [ +  -  +  + ]:          64 :     if (engine.m_spin_gain < 0.0f || engine.m_spin_gain > 2.0f) {
    1926                 :           1 :         engine.m_spin_gain = (std::max)(0.0f, (std::min)(2.0f, engine.m_spin_gain));
    1927                 :             :     }
    1928   [ +  -  +  + ]:          64 :     if (engine.m_rear_align_effect < 0.0f || engine.m_rear_align_effect > 2.0f) {
    1929                 :           1 :         engine.m_rear_align_effect = (std::max)(0.0f, (std::min)(2.0f, engine.m_rear_align_effect));
    1930                 :             :     }
    1931   [ +  -  -  + ]:          64 :     if (engine.m_kerb_strike_rejection < 0.0f || engine.m_kerb_strike_rejection > 1.0f) {
    1932                 :           0 :         engine.m_kerb_strike_rejection = (std::max)(0.0f, (std::min)(1.0f, engine.m_kerb_strike_rejection));
    1933                 :             :     }
    1934   [ +  -  +  + ]:          64 :     if (engine.m_sop_effect < 0.0f || engine.m_sop_effect > 2.0f) {
    1935                 :           1 :         engine.m_sop_effect = (std::max)(0.0f, (std::min)(2.0f, engine.m_sop_effect));
    1936                 :             :     }
    1937                 :          64 :     engine.m_soft_lock_stiffness = (std::max)(0.0f, engine.m_soft_lock_stiffness);
    1938                 :          64 :     engine.m_soft_lock_damping = (std::max)(0.0f, engine.m_soft_lock_damping);
    1939                 :          64 :     engine.m_rest_api_port = (std::max)(1, engine.m_rest_api_port);
    1940                 :             : 
    1941                 :             :     // FFB Safety Validation
    1942                 :          64 :     engine.m_safety.m_safety_window_duration = (std::max)(0.0f, engine.m_safety.m_safety_window_duration);
    1943                 :          64 :     engine.m_safety.m_safety_gain_reduction = (std::max)(0.0f, (std::min)(1.0f, engine.m_safety.m_safety_gain_reduction));
    1944                 :          64 :     engine.m_safety.m_safety_smoothing_tau = (std::max)(0.001f, engine.m_safety.m_safety_smoothing_tau);
    1945                 :          64 :     engine.m_safety.m_spike_detection_threshold = (std::max)(1.0f, engine.m_safety.m_spike_detection_threshold);
    1946                 :          64 :     engine.m_safety.m_immediate_spike_threshold = (std::max)(1.0f, engine.m_safety.m_immediate_spike_threshold);
    1947                 :          64 :     engine.m_safety.m_safety_slew_full_scale_time_s = (std::max)(0.01f, engine.m_safety.m_safety_slew_full_scale_time_s);
    1948                 :          64 :     engine.m_safety.m_stutter_threshold = (std::max)(1.01f, engine.m_safety.m_stutter_threshold);
    1949                 :             : 
    1950   [ +  -  +  - ]:          64 :     Logger::Get().LogFile("[Config] Loaded from %s", final_path.c_str());
    1951   [ +  +  +  +  :          73 : }
                   +  + ]
    1952                 :             : 
    1953                 :             : 
    1954                 :             : 
    1955                 :             : 
    1956                 :             : 
    1957                 :             : 
    1958                 :             : 
    1959                 :             : 
    1960                 :             : 
        

Generated by: LCOV version 2.0-1