Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/pc/configfile.c
7857 views
1
// configfile.c - handles loading and saving the configuration options
2
#include <stdbool.h>
3
#include <stdlib.h>
4
#include <stdio.h>
5
#include <string.h>
6
#include <assert.h>
7
#include <ctype.h>
8
#ifdef __linux__
9
#include <sys/stat.h>
10
#endif
11
12
#include "configfile.h"
13
#include "../game/settings.h"
14
#include "../game/main.h"
15
16
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
17
18
static const struct ConfigOption options[] = {
19
// DISPLAY
20
{ .name = "DISPLAY", .type = CONFIG_TYPE_SECTION },
21
{ .name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configFullscreen },
22
{ .name = "default_monitor", .type = CONFIG_TYPE_UINT, .uintValue = &configDefaultMonitor },
23
{ .name = "vsync", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configVSync },
24
{ .name = "window_width", .type = CONFIG_TYPE_UINT, .uintValue = &configWindowWidth },
25
{ .name = "window_height", .type = CONFIG_TYPE_UINT, .uintValue = &configWindowHeight },
26
{ .name = "fullscreen_display_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configFullscreenDisplayMode },
27
{ .name = "graphics_backend", .type = CONFIG_TYPE_UINT, .uintValue = &configGraphicsBackend },
28
29
// AUDIO
30
{ .name = "AUDIO", .type = CONFIG_TYPE_SECTION },
31
{ .name = "overall_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configOverallVolume },
32
{ .name = "music_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configSeqVolume[0] },
33
{ .name = "jingle_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configSeqVolume[1] },
34
{ .name = "sound_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configSeqVolume[2] },
35
36
// GRAPHICS
37
{ .name = "GRAPHICS", .type = CONFIG_TYPE_SECTION },
38
{ .name = "internal_resolution", .type = CONFIG_TYPE_FLOAT, .floatValue = &configInternalResolution },
39
{ .name = "aspect_ratio", .type = CONFIG_TYPE_UINT, .uintValue = &configAspectRatio },
40
{ .name = "frame_rate", .type = CONFIG_TYPE_UINT, .uintValue = &configFrameRate },
41
{ .name = "anti_aliasing", .type = CONFIG_TYPE_UINT, .uintValue = &configAntiAliasing },
42
{ .name = "draw_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configDrawDistanceMultiplier },
43
{ .name = "level_of_detail", .type = CONFIG_TYPE_UINT, .uintValue = &configLevelOfDetail },
44
{ .name = "texture_filtering", .type = CONFIG_TYPE_SINT, .sintValue = &configTextureFiltering },
45
{ .name = "anisotropic_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configAnisotropicFiltering },
46
{ .name = "noise_type", .type = CONFIG_TYPE_UINT, .uintValue = &configNoiseType },
47
{ .name = "n64_blur", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configN64Blur },
48
49
// CONTROLS
50
{ .name = "CONTROLS", .type = CONFIG_TYPE_SECTION },
51
{ .name = "improved_controls", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configImprovedControls },
52
{ .name = "improved_swimming", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configImprovedSwimming },
53
{ .name = "improved_hanging", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configImprovedHanging },
54
{ .name = "enemy_bouncing", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configEnemyBouncing },
55
{ .name = "dpad_controls", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configDpadControls },
56
{ .name = "full_air_control", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configFullAirControl },
57
58
// GAMEPLAY
59
{ .name = "GAMEPLAY", .type = CONFIG_TYPE_SECTION },
60
{ .name = "fix_gameplay_bugs", .type = CONFIG_TYPE_UINT, .uintValue = &configApplyBugFixes },
61
{ .name = "save_lives_to_save_file", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configSaveLives },
62
{ .name = "make_items_respawn", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configRespawnCertainItems },
63
{ .name = "remove_inconvenient_warps", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configRemoveAnnoyingWarps },
64
{ .name = "improved_powerups", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configBetterPowerups },
65
{ .name = "improved_enemies", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configBetterEnemies },
66
{ .name = "improved_npcs", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configTalkNPCs },
67
{ .name = "improved_blast_away_the_wall", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configBetterBlastAwayTheWall },
68
{ .name = "bring_mips_back", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configBringMipsBack },
69
{ .name = "disable_fall_damage", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configDisableFallDamage },
70
{ .name = "allow_leaving_the_course_at_any_time", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configLeaveAnyTime },
71
72
// PROGRESSION
73
{ .name = "PROGRESSION", .type = CONFIG_TYPE_SECTION },
74
{ .name = "tie_bowsers_sub_to_missions", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configBowsersSub },
75
{ .name = "always_stay_in_course", .type = CONFIG_TYPE_UINT, .uintValue = &configStayInCourse },
76
{ .name = "skip_mission_select", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configSkipMissionSelect },
77
{ .name = "auto_switch_to_the_next_mission", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configSwitchToNextMission },
78
{ .name = "skip_cutscenes", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configSkipCutscenes },
79
80
// CAMERA
81
{ .name = "CAMERA", .type = CONFIG_TYPE_SECTION },
82
{ .name = "default_camera_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configDefaultCameraMode },
83
{ .name = "alternate_camera_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configAlternateCameraMode },
84
{ .name = "horizontal_analog_camera", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configImprovedCamera },
85
{ .name = "vertical_analog_camera", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configVerticalCamera },
86
{ .name = "improved_cbutton_camera", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configImprovedCButtonCamera },
87
{ .name = "center_camera_button", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configCenterCameraButton },
88
{ .name = "invert_horizontal_camera_controls", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configInvertedCamera },
89
{ .name = "invert_vertical_camera_controls", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configInvertedVerticalCamera },
90
{ .name = "analog_camera_speed", .type = CONFIG_TYPE_FLOAT, .floatValue = &configCameraSpeed },
91
{ .name = "additional_camera_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configAdditionalCameraDistance },
92
{ .name = "additional_fov", .type = CONFIG_TYPE_FLOAT, .floatValue = &configAdditionalFOV },
93
94
// HUD AND UI
95
{ .name = "HUD AND UI", .type = CONFIG_TYPE_SECTION },
96
{ .name = "fix_text_typos", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configFixTextTypos },
97
{ .name = "add_quit_option", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configQuitOption },
98
{ .name = "hud_layout", .type = CONFIG_TYPE_UINT, .uintValue = &configHudLayout },
99
{ .name = "4by3_hud", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&config4by3Hud },
100
{ .name = "show_the_collected_stars", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configHudStars },
101
{ .name = "add_zeroes_to_counters", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configAddZeroes },
102
{ .name = "always_show_the_100_coin_star", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configShow100CoinStar },
103
{ .name = "always_show_the_health_meter", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configAlwaysShowHealth },
104
{ .name = "hud_filtering", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configHUDFiltering },
105
{ .name = "hide_hud", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configHideHud },
106
107
// EXTRA MOVES
108
{ .name = "EXTRA MOVES", .type = CONFIG_TYPE_SECTION },
109
{ .name = "wall_sliding", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configWallSliding },
110
{ .name = "ground_pound_jump", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configGroundPoundJump },
111
{ .name = "sunshine_dive_hop", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configSunshineDive },
112
{ .name = "odyssey_ground_pound_dive", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configOdysseyDive },
113
{ .name = "odyssey_rolling", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configRolling },
114
{ .name = "flashback_ground_pound", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configFlashbackGroundPound },
115
116
// RESTORATIONS
117
{ .name = "RESTORATIONS", .type = CONFIG_TYPE_SECTION },
118
{ .name = "enable_the_unused_pyramid_cutscene", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configUnusedPyramidCutscene },
119
{ .name = "restore_unused_sound_effects", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configRestoreUnusedSounds },
120
{ .name = "restore_mother_penguins_sad_eyes", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configPenguinSadEyes },
121
{ .name = "replace_triple_jump_with_twirl", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configTwirlTripleJump },
122
{ .name = "use_beta_like_camera", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configBetaLikeCamera },
123
{ .name = "make_mario_sparkle_at_course_start", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configSpawnSparkles },
124
{ .name = "replace_keys_with_stars_when_collected", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configReplaceKeysWithStars },
125
126
// BONUS MODES
127
{ .name = "BONUS MODES", .type = CONFIG_TYPE_SECTION },
128
{ .name = "casual_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configCasualMode },
129
{ .name = "infinite_lives_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configLifeMode },
130
{ .name = "encore_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configEncoreMode },
131
{ .name = "invisible_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configInvisibleMode },
132
{ .name = "no_healing_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configNoHealingMode },
133
{ .name = "green_demon_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configGreenDemon },
134
{ .name = "hard_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configHardSave },
135
{ .name = "daredevil_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configDaredevilSave },
136
{ .name = "permadeath_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configHardcoreSave },
137
138
// COLORS
139
{ .name = "COLORS", .type = CONFIG_TYPE_SECTION },
140
{ .name = "color_palette", .type = CONFIG_TYPE_UINT, .uintValue = &configColorPalette },
141
{ .name = "color_cap_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[0][0] },
142
{ .name = "color_cap_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[0][1] },
143
{ .name = "color_cap_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[0][2] },
144
{ .name = "color_cap_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[1][0] },
145
{ .name = "color_cap_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[1][1] },
146
{ .name = "color_cap_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[1][2] },
147
{ .name = "color_shirt_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[0][0] },
148
{ .name = "color_shirt_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[0][1] },
149
{ .name = "color_shirt_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[0][2] },
150
{ .name = "color_shirt_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[1][0] },
151
{ .name = "color_shirt_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[1][1] },
152
{ .name = "color_shirt_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[1][2] },
153
{ .name = "color_overalls_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[0][0] },
154
{ .name = "color_overalls_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[0][1] },
155
{ .name = "color_overalls_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[0][2] },
156
{ .name = "color_overalls_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[1][0] },
157
{ .name = "color_overalls_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[1][1] },
158
{ .name = "color_overalls_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[1][2] },
159
{ .name = "color_gloves_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[0][0] },
160
{ .name = "color_gloves_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[0][1] },
161
{ .name = "color_gloves_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[0][2] },
162
{ .name = "color_gloves_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[1][0] },
163
{ .name = "color_gloves_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[1][1] },
164
{ .name = "color_gloves_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[1][2] },
165
{ .name = "color_shoes_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[0][0] },
166
{ .name = "color_shoes_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[0][1] },
167
{ .name = "color_shoes_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[0][2] },
168
{ .name = "color_shoes_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[1][0] },
169
{ .name = "color_shoes_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[1][1] },
170
{ .name = "color_shoes_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[1][2] },
171
{ .name = "color_skin_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[0][0] },
172
{ .name = "color_skin_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[0][1] },
173
{ .name = "color_skin_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[0][2] },
174
{ .name = "color_skin_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[1][0] },
175
{ .name = "color_skin_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[1][1] },
176
{ .name = "color_skin_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[1][2] },
177
{ .name = "color_hair_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[0][0] },
178
{ .name = "color_hair_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[0][1] },
179
{ .name = "color_hair_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[0][2] },
180
{ .name = "color_hair_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[1][0] },
181
{ .name = "color_hair_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[1][1] },
182
{ .name = "color_hair_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[1][2] },
183
{ .name = "show_cap_logo", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configShowCapLogo },
184
185
// CHEATS
186
{ .name = "CHEATS", .type = CONFIG_TYPE_SECTION },
187
{ .name = "level_select", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&gDebugLevelSelect },
188
{ .name = "debug_movement_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configDebugMovementMode },
189
{ .name = "debug_cap_changer", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configDebugCapChanger },
190
{ .name = "debug_object_spawner", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configDebugObjectSpawner },
191
{ .name = "moon_jump", .type = CONFIG_TYPE_UINT, .uintValue = &configMoonJump },
192
{ .name = "blj_everywhere", .type = CONFIG_TYPE_UINT, .uintValue = &configBLJEverywhere },
193
{ .name = "god_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configGodMode },
194
{ .name = "hyperspeed_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configHyperspeedMode },
195
{ .name = "easy_bowser_throws", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configEasyBowserThrows },
196
{ .name = "make_secrets_visible", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configVisibleSecrets },
197
{ .name = "no_cannon_limits", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configFlexibleCannons },
198
{ .name = "coins_required_for_the_coin_stars", .type = CONFIG_TYPE_UINT, .uintValue = &configCoinStarCoins },
199
200
// FOR FUN
201
{ .name = "FOR FUN", .type = CONFIG_TYPE_SECTION },
202
{ .name = "rock_paper_scissors", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configRockPaperScissors },
203
{ .name = "mad_penguin", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configAngryPenguin },
204
{ .name = "paper_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configPaperMode },
205
{ .name = "fx_mode", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configFXMode },
206
{ .name = "disable_lighting", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configDisableLighting },
207
208
// ADVANCED
209
{ .name = "ADVANCED", .type = CONFIG_TYPE_SECTION },
210
{ .name = "show_debug_display", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&gShowDebugText },
211
{ .name = "show_debug_profiler", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&gShowProfiler },
212
{ .name = "custom_camera_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configCustomCameraDistance },
213
{ .name = "zoomed_out_custom_camera_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configCustomCameraDistanceZoomedOut },
214
215
// CONTROLLER MAPPING
216
{ .name = "CONTROLLER MAPPING", .type = CONFIG_TYPE_SECTION },
217
{ .name = "button_a", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonA },
218
{ .name = "button_b", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonB },
219
{ .name = "button_start", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonStart },
220
{ .name = "button_l", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonL },
221
{ .name = "button_r", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonR },
222
{ .name = "button_z", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonZ },
223
{ .name = "button_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCUp },
224
{ .name = "button_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCDown },
225
{ .name = "button_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCLeft },
226
{ .name = "button_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCRight },
227
{ .name = "left_analog_stick_deadzone", .type = CONFIG_TYPE_UINT, .uintValue = &gControllerLeftDeadzone },
228
{ .name = "right_analog_stick_deadzone", .type = CONFIG_TYPE_UINT, .uintValue = &gControllerRightDeadzone },
229
{ .name = "rumble_strength", .type = CONFIG_TYPE_FLOAT, .floatValue = &configRumbleStrength },
230
{ .name = "block_non_xinput_controllers", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configBlockNonXinputControllers },
231
232
// KEYBOARD MAPPING
233
{ .name = "KEYBOARD MAPPING", .type = CONFIG_TYPE_SECTION },
234
{ .name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA },
235
{ .name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB },
236
{ .name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart },
237
{ .name = "key_l", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyL },
238
{ .name = "key_r", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyR },
239
{ .name = "key_z", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyZ },
240
{ .name = "key_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCUp },
241
{ .name = "key_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCDown },
242
{ .name = "key_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCLeft },
243
{ .name = "key_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCRight },
244
{ .name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp },
245
{ .name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown },
246
{ .name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft },
247
{ .name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight },
248
{ .name = "key_walktrigger", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyWalk },
249
250
// MOUSE
251
{ .name = "MOUSE", .type = CONFIG_TYPE_SECTION },
252
{ .name = "mouse_support", .type = CONFIG_TYPE_BOOL, .boolValue = (bool*)&configMouseCam },
253
{ .name = "mouse_sensitivity", .type = CONFIG_TYPE_FLOAT, .floatValue = &configMouseSensitivity },
254
{ .name = "left_mouse_button_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseLeft },
255
{ .name = "right_mouse_button_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseRight },
256
{ .name = "middle_mouse_button_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseMiddle },
257
{ .name = "mouse_wheel_up_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseWheelUp },
258
{ .name = "mouse_wheel_down_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseWheelDown },
259
};
260
261
// Reads an entire line from a file (excluding the newline character) and returns an allocated string
262
// Returns NULL if no lines could be read from the file
263
static char *read_file_line(FILE *file) {
264
char *buffer;
265
size_t bufferSize = 8;
266
size_t offset = 0; // offset in buffer to write
267
268
buffer = malloc(bufferSize);
269
while (1) {
270
// Read a line from the file
271
if (fgets(buffer + offset, bufferSize - offset, file) == NULL) {
272
free(buffer);
273
return NULL; // Nothing could be read.
274
}
275
offset = strlen(buffer);
276
assert(offset > 0);
277
278
// If a newline was found, remove the trailing newline and exit
279
if (buffer[offset - 1] == '\n') {
280
buffer[offset - 1] = '\0';
281
break;
282
}
283
284
if (feof(file)) // EOF was reached
285
break;
286
287
// If no newline or EOF was reached, then the whole line wasn't read.
288
bufferSize *= 2; // Increase buffer size
289
buffer = realloc(buffer, bufferSize);
290
assert(buffer != NULL);
291
}
292
293
return buffer;
294
}
295
296
// Returns the position of the first character we shouldn't ignore
297
static char *skip_whitespace(char *str) {
298
while (isspace(*str) || *str == '=' || *str == '\"')
299
str++;
300
return str;
301
}
302
303
// NULL-terminates the current whitespace-delimited word, and returns a pointer to the next word
304
static char *word_split(char *str) {
305
// Precondition: str must not point to whitespace
306
assert(!isspace(*str));
307
308
if (*str == '\"')
309
str++;
310
311
// Find either the next whitespace char or end of string
312
while (!isspace(*str) && *str != '\0' && *str != '=' && *str != '\"')
313
str++;
314
if (*str == '\0') // End of string
315
return str;
316
317
// Terminate current word
318
*(str++) = '\0';
319
320
// Skip whitespace to next word
321
return skip_whitespace(str);
322
}
323
324
// Splits a string into words, and stores the words into the 'tokens' array
325
// 'maxTokens' is the length of the 'tokens' array
326
// Returns the number of tokens parsed
327
static unsigned int tokenize_string(char *str, int maxTokens, char **tokens) {
328
int count = 0;
329
330
str = skip_whitespace(str);
331
while (str[0] != '\0' && count < maxTokens) {
332
if ((str[count] == ';') || (str[count] == '#'))
333
break;
334
if (str[count] == '\"') {
335
str[count]++;
336
}
337
tokens[count] = str;
338
str = word_split(str);
339
count++;
340
}
341
return count;
342
}
343
344
// Loads the config file specified by 'filename'
345
void configfile_load(const char *filename) {
346
FILE *file;
347
char *line;
348
349
printf("Loading configuration from '%s'\n", filename);
350
351
file = fopen(filename, "r");
352
if (file == NULL) {
353
// Create a new config file and save defaults
354
printf("Config file '%s' not found. Creating it.\n", filename);
355
configfile_save(filename);
356
return;
357
}
358
359
// Go through each line in the file
360
while ((line = read_file_line(file)) != NULL) {
361
char *p = line;
362
char *tokens[2];
363
int numTokens;
364
365
while (isspace(*p))
366
p++;
367
if ((*p == '[') || (*p == '\n'))
368
continue;
369
numTokens = tokenize_string(p, 2, tokens);
370
if (numTokens != 0) {
371
if (numTokens == 2) {
372
const struct ConfigOption *option = NULL;
373
374
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
375
if (strcmp(tokens[0], options[i].name) == 0) {
376
option = &options[i];
377
break;
378
}
379
}
380
if (option == NULL)
381
printf("unknown option '%s'\n", tokens[0]);
382
else {
383
switch (option->type) {
384
case CONFIG_TYPE_BOOL:
385
if (strcmp(tokens[1], "true") == 0)
386
*option->boolValue = true;
387
else if (strcmp(tokens[1], "false") == 0)
388
*option->boolValue = false;
389
break;
390
case CONFIG_TYPE_UINT:
391
sscanf(tokens[1], "%u", option->uintValue);
392
break;
393
case CONFIG_TYPE_SINT:
394
sscanf(tokens[1], "%d", option->sintValue);
395
break;
396
case CONFIG_TYPE_FLOAT:
397
sscanf(tokens[1], "%f", option->floatValue);
398
break;
399
default:
400
assert(0); // bad type
401
}
402
printf("option: '%s', value: '%s'\n", tokens[0], tokens[1]);
403
}
404
} else
405
puts("error: expected value");
406
}
407
free(line);
408
}
409
410
fclose(file);
411
}
412
413
// Writes the config file to 'filename'
414
void configfile_save(const char *filename) {
415
FILE *file;
416
417
char *dir = malloc(128);
418
419
printf("Saving configuration to '%s'\n", filename);
420
421
#ifdef __linux__
422
strcat(dir, "/");
423
char* copy = strdup(filename);
424
strcat(dir, strtok(copy + 1, "/"));
425
free(copy);
426
mkdir(dir, 0777);
427
#endif
428
429
file = fopen(filename, "w");
430
if (file == NULL) {
431
// error
432
return;
433
}
434
435
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
436
const struct ConfigOption *option = &options[i];
437
438
switch (option->type) {
439
case CONFIG_TYPE_BOOL:
440
fprintf(file, "%s = \"%s\"\n", option->name, *option->boolValue ? "true" : "false");
441
break;
442
case CONFIG_TYPE_UINT:
443
fprintf(file, "%s = \"%u\"\n", option->name, *option->uintValue);
444
break;
445
case CONFIG_TYPE_SINT:
446
fprintf(file, "%s = \"%d\"\n", option->name, *option->sintValue);
447
break;
448
case CONFIG_TYPE_FLOAT:
449
fprintf(file, "%s = \"%f\"\n", option->name, *option->floatValue);
450
break;
451
case CONFIG_TYPE_SECTION:
452
/*if (i != 0)
453
fprintf(file, "\n", option->name);*/
454
fprintf(file, "[%s]\n", option->name);
455
break;
456
default:
457
assert(0); // unknown type
458
}
459
}
460
461
fclose(file);
462
}
463
464