450 lines
25 KiB
C
450 lines
25 KiB
C
// configfile.c - handles loading and saving the configuration options
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
|
|
#include "configfile.h"
|
|
#include "../game/settings.h"
|
|
#include "../game/main.h"
|
|
|
|
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
|
|
|
|
enum ConfigOptionType {
|
|
CONFIG_TYPE_BOOL,
|
|
CONFIG_TYPE_UINT,
|
|
CONFIG_TYPE_FLOAT,
|
|
CONFIG_TYPE_SECTION
|
|
};
|
|
|
|
struct ConfigOption {
|
|
const char *name;
|
|
enum ConfigOptionType type;
|
|
union {
|
|
bool *boolValue;
|
|
unsigned int *uintValue;
|
|
float *floatValue;
|
|
};
|
|
};
|
|
|
|
static const struct ConfigOption options[] = {
|
|
{ .name = "DISPLAY", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen },
|
|
{ .name = "default_monitor", .type = CONFIG_TYPE_UINT, .uintValue = &configDefaultMonitor },
|
|
{ .name = "window_width", .type = CONFIG_TYPE_UINT, .uintValue = &configWindowWidth },
|
|
{ .name = "window_height", .type = CONFIG_TYPE_UINT, .uintValue = &configWindowHeight },
|
|
{ .name = "custom_fullscreen_resolution", .type = CONFIG_TYPE_BOOL, .boolValue = &configCustomFullscreenResolution },
|
|
{ .name = "fullscreen_width", .type = CONFIG_TYPE_UINT, .uintValue = &configFullscreenWidth },
|
|
{ .name = "fullscreen_height", .type = CONFIG_TYPE_UINT, .uintValue = &configFullscreenHeight },
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
{ .name = "custom_internal_resolution", .type = CONFIG_TYPE_BOOL, .boolValue = &configCustomInternalResolution },
|
|
{ .name = "internal_resolution_width", .type = CONFIG_TYPE_UINT, .uintValue = &configInternalResolutionWidth },
|
|
{ .name = "internal_resolution_height", .type = CONFIG_TYPE_UINT, .uintValue = &configInternalResolutionHeight },
|
|
#endif
|
|
{ .name = "graphics_backend", .type = CONFIG_TYPE_UINT, .uintValue = &configGraphicsBackend },
|
|
|
|
{ .name = "AUDIO", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "overall_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configOverallVolume },
|
|
{ .name = "music_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configSeqVolume[0] },
|
|
{ .name = "jingle_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configSeqVolume[1] },
|
|
{ .name = "sound_volume", .type = CONFIG_TYPE_FLOAT, .floatValue = &configSeqVolume[2] },
|
|
|
|
{ .name = "GRAPHICS", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "frame_rate", .type = CONFIG_TYPE_UINT, .uintValue = &configFrameRate },
|
|
{ .name = "draw_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configDrawDistanceMultiplier },
|
|
{ .name = "level_of_detail", .type = CONFIG_TYPE_UINT, .uintValue = &configLevelOfDetail },
|
|
{ .name = "texture_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configTextureFiltering },
|
|
{ .name = "noise_type", .type = CONFIG_TYPE_UINT, .uintValue = &configNoiseType },
|
|
{ .name = "force_4by3", .type = CONFIG_TYPE_BOOL, .boolValue = &configForce4by3 },
|
|
|
|
{ .name = "CONTROLS", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "improved_controls", .type = CONFIG_TYPE_BOOL, .boolValue = &configImprovedControls },
|
|
{ .name = "improved_swimming", .type = CONFIG_TYPE_BOOL, .boolValue = &configImprovedSwimming },
|
|
{ .name = "improved_hanging", .type = CONFIG_TYPE_BOOL, .boolValue = &configImprovedHanging },
|
|
{ .name = "enemy_bouncing", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnemyBouncing },
|
|
{ .name = "dpad_controls", .type = CONFIG_TYPE_BOOL, .boolValue = &configDpadControls },
|
|
{ .name = "full_air_control", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullAirControl },
|
|
|
|
{ .name = "GAMEPLAY", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "apply_bug_fixes", .type = CONFIG_TYPE_UINT, .uintValue = &configApplyBugFixes },
|
|
{ .name = "save_lives_to_save_file", .type = CONFIG_TYPE_BOOL, .boolValue = &configSaveLives },
|
|
{ .name = "make_items_respawn", .type = CONFIG_TYPE_BOOL, .boolValue = &configRespawnCertainItems },
|
|
{ .name = "remove_inconvenient_warps", .type = CONFIG_TYPE_BOOL, .boolValue = &configRemoveAnnoyingWarps },
|
|
{ .name = "improve_powerups", .type = CONFIG_TYPE_BOOL, .boolValue = &configBetterPowerups },
|
|
{ .name = "improve_enemies", .type = CONFIG_TYPE_BOOL, .boolValue = &configBetterEnemies },
|
|
{ .name = "improve_npcs", .type = CONFIG_TYPE_BOOL, .boolValue = &configTalkNPCs },
|
|
{ .name = "improve_blast_away_the_wall", .type = CONFIG_TYPE_BOOL, .boolValue = &configBetterBlastAwayTheWall },
|
|
{ .name = "disable_fall_damage", .type = CONFIG_TYPE_BOOL, .boolValue = &configDisableFallDamage },
|
|
{ .name = "allow_leaving_the_course_at_any_time", .type = CONFIG_TYPE_BOOL, .boolValue = &configLeaveAnyTime },
|
|
{ .name = "make_secrets_visible", .type = CONFIG_TYPE_BOOL, .boolValue = &configVisibleSecrets },
|
|
{ .name = "fix_exploits", .type = CONFIG_TYPE_BOOL, .boolValue = &configFixExploits },
|
|
|
|
{ .name = "PROGRESSION", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "tie_bowsers_sub_to_missions", .type = CONFIG_TYPE_BOOL, .boolValue = &configBowsersSub },
|
|
{ .name = "always_stay_in_course", .type = CONFIG_TYPE_UINT, .uintValue = &configStayInCourse },
|
|
{ .name = "skip_mission_select", .type = CONFIG_TYPE_BOOL, .boolValue = &configSkipMissionSelect },
|
|
{ .name = "auto_switch_to_the_next_mission", .type = CONFIG_TYPE_BOOL, .boolValue = &configSwitchToNextMission },
|
|
{ .name = "skip_cutscenes", .type = CONFIG_TYPE_BOOL, .boolValue = &configSkipCutscenes },
|
|
|
|
{ .name = "CAMERA", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "default_camera_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configDefaultCameraMode },
|
|
{ .name = "alternate_camera_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configAlternateCameraMode },
|
|
{ .name = "horizontal_analog_camera", .type = CONFIG_TYPE_BOOL, .boolValue = &configImprovedCamera },
|
|
{ .name = "vertical_analog_camera", .type = CONFIG_TYPE_BOOL, .boolValue = &configVerticalCamera },
|
|
{ .name = "center_camera_button", .type = CONFIG_TYPE_BOOL, .boolValue = &configCenterCameraButton },
|
|
{ .name = "invert_horizontal_camera_controls", .type = CONFIG_TYPE_BOOL, .boolValue = &configInvertedCamera },
|
|
{ .name = "invert_vertical_camera_controls", .type = CONFIG_TYPE_BOOL, .boolValue = &configInvertedVerticalCamera },
|
|
{ .name = "analog_camera_speed", .type = CONFIG_TYPE_FLOAT, .floatValue = &configCameraSpeed },
|
|
{ .name = "additional_camera_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configAdditionalCameraDistance },
|
|
{ .name = "additional_fov", .type = CONFIG_TYPE_FLOAT, .floatValue = &configAdditionalFOV },
|
|
|
|
{ .name = "HUD AND UI", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "add_quit_option", .type = CONFIG_TYPE_BOOL, .boolValue = &configQuitOption },
|
|
{ .name = "hud_layout", .type = CONFIG_TYPE_UINT, .uintValue = &configHudLayout },
|
|
{ .name = "4by3_hud", .type = CONFIG_TYPE_BOOL, .boolValue = &config4by3Hud },
|
|
{ .name = "show_the_collected_stars", .type = CONFIG_TYPE_BOOL, .boolValue = &gHudStars },
|
|
{ .name = "add_zeroes_to_counters", .type = CONFIG_TYPE_BOOL, .boolValue = &configAddZeroes },
|
|
{ .name = "always_show_the_100_coin_star", .type = CONFIG_TYPE_BOOL, .boolValue = &gShow100CoinStar },
|
|
{ .name = "always_show_the_health_meter", .type = CONFIG_TYPE_BOOL, .boolValue = &gAlwaysShowHealth },
|
|
{ .name = "hud_filtering", .type = CONFIG_TYPE_BOOL, .boolValue = &gHUDFiltering },
|
|
{ .name = "hide_hud", .type = CONFIG_TYPE_BOOL, .boolValue = &gHideHud },
|
|
|
|
{ .name = "MOUSE", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "mouse_support", .type = CONFIG_TYPE_BOOL, .boolValue = &gMouseCam },
|
|
{ .name = "mouse_sensitivity", .type = CONFIG_TYPE_FLOAT, .floatValue = &gMouseSensitivity },
|
|
{ .name = "left_mouse_button_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseLeft },
|
|
{ .name = "right_mouse_button_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseRight },
|
|
{ .name = "middle_mouse_button_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseMiddle },
|
|
{ .name = "mouse_wheel_up_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseWheelUp },
|
|
{ .name = "mouse_wheel_down_action", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseWheelDown },
|
|
|
|
{ .name = "EXTRA MOVES", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "wall_sliding", .type = CONFIG_TYPE_BOOL, .boolValue = &gWallSliding },
|
|
{ .name = "ground_pound_jump", .type = CONFIG_TYPE_BOOL, .boolValue = &gGroundPoundJump },
|
|
{ .name = "sunshine_dive_hop", .type = CONFIG_TYPE_BOOL, .boolValue = &gSunshineDive },
|
|
{ .name = "odyssey_ground_pound_dive", .type = CONFIG_TYPE_BOOL, .boolValue = &gOdysseyDive },
|
|
{ .name = "odyssey_rolling", .type = CONFIG_TYPE_BOOL, .boolValue = &configRolling },
|
|
{ .name = "flashback_ground_pound", .type = CONFIG_TYPE_BOOL, .boolValue = &gFlashbackPound },
|
|
|
|
{ .name = "RESTORATIONS", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "enable_the_unused_pyramid_cutscene", .type = CONFIG_TYPE_BOOL, .boolValue = &configUnusedPyramidCutscene },
|
|
{ .name = "restore_unused_sound_effects", .type = CONFIG_TYPE_BOOL, .boolValue = &configRestoreUnusedSounds },
|
|
{ .name = "restore_mother_penguins_sad_eyes", .type = CONFIG_TYPE_BOOL, .boolValue = &gPenguinSadEyes },
|
|
{ .name = "replace_triple_jump_with_twirl", .type = CONFIG_TYPE_BOOL, .boolValue = &gTwirlTripleJump },
|
|
{ .name = "use_beta_like_camera", .type = CONFIG_TYPE_BOOL, .boolValue = &configBetaLikeCamera },
|
|
{ .name = "make_mario_sparkle_at_course_start", .type = CONFIG_TYPE_BOOL, .boolValue = &gSpawnSparkles },
|
|
{ .name = "replace_keys_with_stars_when_collected", .type = CONFIG_TYPE_BOOL, .boolValue = &gReplaceKeysWithStars },
|
|
|
|
{ .name = "BONUS MODES", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "infinite_lives_mode", .type = CONFIG_TYPE_UINT, .uintValue = &gLifeMode },
|
|
{ .name = "encore_mode", .type = CONFIG_TYPE_UINT, .uintValue = &gEncoreMode },
|
|
{ .name = "green_demon_mode", .type = CONFIG_TYPE_UINT, .uintValue = &gGreenDemon },
|
|
{ .name = "no_healing_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gNoHealingMode },
|
|
{ .name = "hard_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gHardSave },
|
|
{ .name = "daredevil_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gDaredevilSave },
|
|
{ .name = "permadeath_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gHardcoreSave },
|
|
{ .name = "casual_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gCasualMode },
|
|
|
|
{ .name = "COLORS", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "color_palette", .type = CONFIG_TYPE_UINT, .uintValue = &configColorPalette },
|
|
{ .name = "color_cap_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[0][0] },
|
|
{ .name = "color_cap_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[0][1] },
|
|
{ .name = "color_cap_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[0][2] },
|
|
{ .name = "color_cap_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[1][0] },
|
|
{ .name = "color_cap_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[1][1] },
|
|
{ .name = "color_cap_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorCap[1][2] },
|
|
{ .name = "color_shirt_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[0][0] },
|
|
{ .name = "color_shirt_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[0][1] },
|
|
{ .name = "color_shirt_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[0][2] },
|
|
{ .name = "color_shirt_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[1][0] },
|
|
{ .name = "color_shirt_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[1][1] },
|
|
{ .name = "color_shirt_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShirt[1][2] },
|
|
{ .name = "color_overalls_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[0][0] },
|
|
{ .name = "color_overalls_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[0][1] },
|
|
{ .name = "color_overalls_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[0][2] },
|
|
{ .name = "color_overalls_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[1][0] },
|
|
{ .name = "color_overalls_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[1][1] },
|
|
{ .name = "color_overalls_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorOveralls[1][2] },
|
|
{ .name = "color_gloves_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[0][0] },
|
|
{ .name = "color_gloves_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[0][1] },
|
|
{ .name = "color_gloves_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[0][2] },
|
|
{ .name = "color_gloves_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[1][0] },
|
|
{ .name = "color_gloves_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[1][1] },
|
|
{ .name = "color_gloves_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorGloves[1][2] },
|
|
{ .name = "color_shoes_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[0][0] },
|
|
{ .name = "color_shoes_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[0][1] },
|
|
{ .name = "color_shoes_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[0][2] },
|
|
{ .name = "color_shoes_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[1][0] },
|
|
{ .name = "color_shoes_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[1][1] },
|
|
{ .name = "color_shoes_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorShoes[1][2] },
|
|
{ .name = "color_skin_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[0][0] },
|
|
{ .name = "color_skin_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[0][1] },
|
|
{ .name = "color_skin_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[0][2] },
|
|
{ .name = "color_skin_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[1][0] },
|
|
{ .name = "color_skin_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[1][1] },
|
|
{ .name = "color_skin_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorSkin[1][2] },
|
|
{ .name = "color_hair_main_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[0][0] },
|
|
{ .name = "color_hair_main_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[0][1] },
|
|
{ .name = "color_hair_main_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[0][2] },
|
|
{ .name = "color_hair_shading_r", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[1][0] },
|
|
{ .name = "color_hair_shading_g", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[1][1] },
|
|
{ .name = "color_hair_shading_b", .type = CONFIG_TYPE_UINT, .uintValue = &configColorHair[1][2] },
|
|
{ .name = "show_cap_logo", .type = CONFIG_TYPE_BOOL, .boolValue = &configShowCapLogo },
|
|
|
|
{ .name = "CHEATS", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "level_select", .type = CONFIG_TYPE_BOOL, .boolValue = &gDebugLevelSelect },
|
|
{ .name = "debug_movement_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gDebugMovementMode },
|
|
{ .name = "debug_cap_changer", .type = CONFIG_TYPE_BOOL, .boolValue = &gDebugCapChanger },
|
|
{ .name = "moon_jump", .type = CONFIG_TYPE_UINT, .uintValue = &configMoonJump },
|
|
{ .name = "blj_everywhere", .type = CONFIG_TYPE_UINT, .uintValue = &configBLJEverywhere },
|
|
{ .name = "god_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &configGodMode },
|
|
{ .name = "hyperspeed_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &configHyperspeedMode },
|
|
{ .name = "no_cannon_limits", .type = CONFIG_TYPE_BOOL, .boolValue = &gFlexibleCannons },
|
|
{ .name = "coins_required_for_the_coin_stars", .type = CONFIG_TYPE_UINT, .uintValue = &configCoinStarCoins },
|
|
|
|
{ .name = "FOR FUN", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "paper_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gPaperMode },
|
|
{ .name = "fx_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gFXMode },
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
{ .name = "wireframe_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gWireframeMode },
|
|
#endif
|
|
{ .name = "disable_lighting", .type = CONFIG_TYPE_BOOL, .boolValue = &gDisableLighting },
|
|
|
|
{ .name = "ADVANCED", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "show_debug_display", .type = CONFIG_TYPE_BOOL, .boolValue = &gShowDebugText },
|
|
{ .name = "show_debug_profiler", .type = CONFIG_TYPE_BOOL, .boolValue = &gShowProfiler },
|
|
{ .name = "fullscreen_refresh_rate", .type = CONFIG_TYPE_UINT, .uintValue = &configFullscreenRefreshRate },
|
|
{ .name = "custom_camera_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configCustomCameraDistance },
|
|
{ .name = "zoomed_out_custom_camera_distance", .type = CONFIG_TYPE_FLOAT, .floatValue = &configCustomCameraDistanceZoomedOut },
|
|
|
|
{ .name = "INPUT MAPPING", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "button_a", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonA },
|
|
{ .name = "button_b", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonB },
|
|
{ .name = "button_start", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonStart },
|
|
{ .name = "button_l", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonL },
|
|
{ .name = "button_r", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonR },
|
|
{ .name = "button_z", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonZ },
|
|
{ .name = "button_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCUp },
|
|
{ .name = "button_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCDown },
|
|
{ .name = "button_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCLeft },
|
|
{ .name = "button_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configButtonCRight },
|
|
{ .name = "left_analog_stick_deadzone", .type = CONFIG_TYPE_UINT, .uintValue = &gControllerLeftDeadzone },
|
|
{ .name = "right_analog_stick_deadzone", .type = CONFIG_TYPE_UINT, .uintValue = &gControllerRightDeadzone },
|
|
{ .name = "rumble_strength", .type = CONFIG_TYPE_FLOAT, .floatValue = &configRumbleStrength },
|
|
|
|
{ .name = "KEY MAPPING", .type = CONFIG_TYPE_SECTION },
|
|
{ .name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA },
|
|
{ .name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB },
|
|
{ .name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart },
|
|
{ .name = "key_l", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyL },
|
|
{ .name = "key_r", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyR },
|
|
{ .name = "key_z", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyZ },
|
|
{ .name = "key_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCUp },
|
|
{ .name = "key_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCDown },
|
|
{ .name = "key_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCLeft },
|
|
{ .name = "key_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCRight },
|
|
{ .name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp },
|
|
{ .name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown },
|
|
{ .name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft },
|
|
{ .name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight },
|
|
{ .name = "key_walktrigger", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyWalk },
|
|
};
|
|
|
|
// Reads an entire line from a file (excluding the newline character) and returns an allocated string
|
|
// Returns NULL if no lines could be read from the file
|
|
static char *read_file_line(FILE *file) {
|
|
char *buffer;
|
|
size_t bufferSize = 8;
|
|
size_t offset = 0; // offset in buffer to write
|
|
|
|
buffer = malloc(bufferSize);
|
|
while (1) {
|
|
// Read a line from the file
|
|
if (fgets(buffer + offset, bufferSize - offset, file) == NULL) {
|
|
free(buffer);
|
|
return NULL; // Nothing could be read.
|
|
}
|
|
offset = strlen(buffer);
|
|
assert(offset > 0);
|
|
|
|
// If a newline was found, remove the trailing newline and exit
|
|
if (buffer[offset - 1] == '\n') {
|
|
buffer[offset - 1] = '\0';
|
|
break;
|
|
}
|
|
|
|
if (feof(file)) // EOF was reached
|
|
break;
|
|
|
|
// If no newline or EOF was reached, then the whole line wasn't read.
|
|
bufferSize *= 2; // Increase buffer size
|
|
buffer = realloc(buffer, bufferSize);
|
|
assert(buffer != NULL);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
// Returns the position of the first character we shouldn't ignore
|
|
static char *skip_whitespace(char *str) {
|
|
while (isspace(*str) || *str == '=' || *str == '\"')
|
|
str++;
|
|
return str;
|
|
}
|
|
|
|
// NULL-terminates the current whitespace-delimited word, and returns a pointer to the next word
|
|
static char *word_split(char *str) {
|
|
// Precondition: str must not point to whitespace
|
|
assert(!isspace(*str));
|
|
|
|
if (*str == '\"')
|
|
str++;
|
|
|
|
// Find either the next whitespace char or end of string
|
|
while (!isspace(*str) && *str != '\0' && *str != '=' && *str != '\"')
|
|
str++;
|
|
if (*str == '\0') // End of string
|
|
return str;
|
|
|
|
// Terminate current word
|
|
*(str++) = '\0';
|
|
|
|
// Skip whitespace to next word
|
|
return skip_whitespace(str);
|
|
}
|
|
|
|
// Splits a string into words, and stores the words into the 'tokens' array
|
|
// 'maxTokens' is the length of the 'tokens' array
|
|
// Returns the number of tokens parsed
|
|
static unsigned int tokenize_string(char *str, int maxTokens, char **tokens) {
|
|
int count = 0;
|
|
|
|
str = skip_whitespace(str);
|
|
while (str[0] != '\0' && count < maxTokens) {
|
|
if ((str[count] == ';') || (str[count] == '#'))
|
|
break;
|
|
if (str[count] == '\"') {
|
|
str[count]++;
|
|
}
|
|
tokens[count] = str;
|
|
str = word_split(str);
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// Loads the config file specified by 'filename'
|
|
void configfile_load(const char *filename) {
|
|
FILE *file;
|
|
char *line;
|
|
|
|
printf("Loading configuration from '%s'\n", filename);
|
|
|
|
file = fopen(filename, "r");
|
|
if (file == NULL) {
|
|
// Create a new config file and save defaults
|
|
printf("Config file '%s' not found. Creating it.\n", filename);
|
|
configfile_save(filename);
|
|
return;
|
|
}
|
|
|
|
// Go through each line in the file
|
|
while ((line = read_file_line(file)) != NULL) {
|
|
char *p = line;
|
|
char *tokens[2];
|
|
int numTokens;
|
|
|
|
while (isspace(*p))
|
|
p++;
|
|
if ((*p == '[') || (*p == '\n'))
|
|
continue;
|
|
numTokens = tokenize_string(p, 2, tokens);
|
|
if (numTokens != 0) {
|
|
if (numTokens == 2) {
|
|
const struct ConfigOption *option = NULL;
|
|
|
|
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
|
|
if (strcmp(tokens[0], options[i].name) == 0) {
|
|
option = &options[i];
|
|
break;
|
|
}
|
|
}
|
|
if (option == NULL)
|
|
printf("unknown option '%s'\n", tokens[0]);
|
|
else {
|
|
switch (option->type) {
|
|
case CONFIG_TYPE_BOOL:
|
|
if (strcmp(tokens[1], "true") == 0)
|
|
*option->boolValue = true;
|
|
else if (strcmp(tokens[1], "false") == 0)
|
|
*option->boolValue = false;
|
|
break;
|
|
case CONFIG_TYPE_UINT:
|
|
sscanf(tokens[1], "%u", option->uintValue);
|
|
break;
|
|
case CONFIG_TYPE_FLOAT:
|
|
sscanf(tokens[1], "%f", option->floatValue);
|
|
break;
|
|
default:
|
|
assert(0); // bad type
|
|
}
|
|
printf("option: '%s', value: '%s'\n", tokens[0], tokens[1]);
|
|
}
|
|
} else
|
|
puts("error: expected value");
|
|
}
|
|
free(line);
|
|
}
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
// Writes the config file to 'filename'
|
|
void configfile_save(const char *filename) {
|
|
FILE *file;
|
|
|
|
char *dir = malloc(128);
|
|
|
|
printf("Saving configuration to '%s'\n", filename);
|
|
|
|
#ifdef __linux__
|
|
strcat(dir, "/");
|
|
char* copy = strdup(filename);
|
|
strcat(dir, strtok(copy + 1, "/"));
|
|
free(copy);
|
|
mkdir(dir, 0777);
|
|
#endif
|
|
|
|
file = fopen(filename, "w");
|
|
if (file == NULL) {
|
|
// error
|
|
return;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
|
|
const struct ConfigOption *option = &options[i];
|
|
|
|
switch (option->type) {
|
|
case CONFIG_TYPE_BOOL:
|
|
fprintf(file, "%s = \"%s\"\n", option->name, *option->boolValue ? "true" : "false");
|
|
break;
|
|
case CONFIG_TYPE_UINT:
|
|
fprintf(file, "%s = \"%u\"\n", option->name, *option->uintValue);
|
|
break;
|
|
case CONFIG_TYPE_FLOAT:
|
|
fprintf(file, "%s = \"%f\"\n", option->name, *option->floatValue);
|
|
break;
|
|
case CONFIG_TYPE_SECTION:
|
|
/*if (i != 0)
|
|
fprintf(file, "\n", option->name);*/
|
|
fprintf(file, "[%s]\n", option->name);
|
|
break;
|
|
default:
|
|
assert(0); // unknown type
|
|
}
|
|
}
|
|
|
|
fclose(file);
|
|
} |