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