aboutsummaryrefslogtreecommitdiff
path: root/src/Config.cpp
blob: 8f28c89909139b3a5c2d1fcd7939dd3619d4d5d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "../include/Config.hpp"
#include "../include/Utils.hpp"
#include <variant>
#include <limits.h>
#include <inttypes.h>
#include <libgen.h>

#define FORMAT_I32 "%" PRIi32
#define FORMAT_I64 "%" PRIi64
#define FORMAT_U32 "%" PRIu32

#define CONFIG_FILE_VERSION 1

namespace gsr {
    static std::optional<KeyValue> parse_key_value(std::string_view line) {
        const size_t space_index = line.find(' ');
        if(space_index == std::string_view::npos)
            return std::nullopt;
        return KeyValue{line.substr(0, space_index), line.substr(space_index + 1)};
    }

    using ConfigValue = std::variant<bool*, std::string*, int32_t*, ConfigHotkey*, std::vector<std::string>*>;

    static std::map<std::string_view, ConfigValue> get_config_options(Config &config) {
        return {
            {"main.config_file_version", &config.main_config.config_file_version},
            {"main.software_encoding_warning_shown", &config.main_config.software_encoding_warning_shown},

            {"streaming.record_options.record_area_option", &config.streaming_config.record_options.record_area_option},
            {"streaming.record_options.record_area_width", &config.streaming_config.record_options.record_area_width},
            {"streaming.record_options.record_area_height", &config.streaming_config.record_options.record_area_height},
            {"streaming.record_options.fps", &config.streaming_config.record_options.fps},
            {"streaming.record_options.merge_audio_tracks", &config.streaming_config.record_options.merge_audio_tracks},
            {"streaming.record_options.audio_track", &config.streaming_config.record_options.audio_tracks},
            {"streaming.record_options.color_range", &config.streaming_config.record_options.color_range},
            {"streaming.record_options.video_quality", &config.streaming_config.record_options.video_quality},
            {"streaming.record_options.codec", &config.streaming_config.record_options.video_codec},
            {"streaming.record_options.audio_codec", &config.streaming_config.record_options.audio_codec},
            {"streaming.record_options.framerate_mode", &config.streaming_config.record_options.framerate_mode},
            {"streaming.record_options.advanced_view", &config.streaming_config.record_options.advanced_view},
            {"streaming.record_options.overclock", &config.streaming_config.record_options.overclock},
            {"streaming.record_options.record_cursor", &config.streaming_config.record_options.record_cursor},
            {"streaming.record_options.restore_portal_session", &config.streaming_config.record_options.restore_portal_session},
            {"streaming.show_streaming_started_notifications", &config.streaming_config.show_streaming_started_notifications},
            {"streaming.show_streaming_stopped_notifications", &config.streaming_config.show_streaming_stopped_notifications},
            {"streaming.service", &config.streaming_config.streaming_service},
            {"streaming.youtube.key", &config.streaming_config.youtube.stream_key},
            {"streaming.twitch.key", &config.streaming_config.twitch.stream_key},
            {"streaming.custom.url", &config.streaming_config.custom.url},
            {"streaming.custom.container", &config.streaming_config.custom.container},
            {"streaming.start_stop_recording_hotkey", &config.streaming_config.start_stop_recording_hotkey},

            {"record.record_options.record_area_option", &config.record_config.record_options.record_area_option},
            {"record.record_options.record_area_width", &config.record_config.record_options.record_area_width},
            {"record.record_options.record_area_height", &config.record_config.record_options.record_area_height},
            {"record.record_options.fps", &config.record_config.record_options.fps},
            {"record.record_options.merge_audio_tracks", &config.record_config.record_options.merge_audio_tracks},
            {"record.record_options.audio_track", &config.record_config.record_options.audio_tracks},
            {"record.record_options.color_range", &config.record_config.record_options.color_range},
            {"record.record_options.video_quality", &config.record_config.record_options.video_quality},
            {"record.record_options.codec", &config.record_config.record_options.video_codec},
            {"record.record_options.audio_codec", &config.record_config.record_options.audio_codec},
            {"record.record_options.framerate_mode", &config.record_config.record_options.framerate_mode},
            {"record.record_options.advanced_view", &config.record_config.record_options.advanced_view},
            {"record.record_options.overclock", &config.record_config.record_options.overclock},
            {"record.record_options.record_cursor", &config.record_config.record_options.record_cursor},
            {"record.record_options.restore_portal_session", &config.record_config.record_options.restore_portal_session},
            {"record.show_recording_started_notifications", &config.record_config.show_recording_started_notifications},
            {"record.show_video_saved_notifications", &config.record_config.show_video_saved_notifications},
            {"record.save_directory", &config.record_config.save_directory},
            {"record.container", &config.record_config.container},
            {"record.start_stop_recording_hotkey", &config.record_config.start_stop_recording_hotkey},
            {"record.pause_unpause_recording_hotkey", &config.record_config.pause_unpause_recording_hotkey},

            {"replay.record_options.record_area_option", &config.replay_config.record_options.record_area_option},
            {"replay.record_options.record_area_width", &config.replay_config.record_options.record_area_width},
            {"replay.record_options.record_area_height", &config.replay_config.record_options.record_area_height},
            {"replay.record_options.fps", &config.replay_config.record_options.fps},
            {"replay.record_options.merge_audio_tracks", &config.replay_config.record_options.merge_audio_tracks},
            {"replay.record_options.audio_track", &config.replay_config.record_options.audio_tracks},
            {"replay.record_options.color_range", &config.replay_config.record_options.color_range},
            {"replay.record_options.video_quality", &config.replay_config.record_options.video_quality},
            {"replay.record_options.codec", &config.replay_config.record_options.video_codec},
            {"replay.record_options.audio_codec", &config.replay_config.record_options.audio_codec},
            {"replay.record_options.framerate_mode", &config.replay_config.record_options.framerate_mode},
            {"replay.record_options.advanced_view", &config.replay_config.record_options.advanced_view},
            {"replay.record_options.overclock", &config.replay_config.record_options.overclock},
            {"replay.record_options.record_cursor", &config.replay_config.record_options.record_cursor},
            {"replay.record_options.restore_portal_session", &config.replay_config.record_options.restore_portal_session},
            {"replay.show_replay_started_notifications", &config.replay_config.show_replay_started_notifications},
            {"replay.show_replay_stopped_notifications", &config.replay_config.show_replay_stopped_notifications},
            {"replay.show_replay_saved_notifications", &config.replay_config.show_replay_saved_notifications},
            {"replay.save_directory", &config.replay_config.save_directory},
            {"replay.container", &config.replay_config.container},
            {"replay.time", &config.replay_config.replay_time},
            {"replay.start_stop_recording_hotkey", &config.replay_config.start_stop_recording_hotkey},
            {"replay.save_recording_hotkey", &config.replay_config.save_recording_hotkey}
        };
    }

    std::optional<Config> read_config() {
        std::optional<Config> config;

        const std::string config_path = get_config_dir() + "/overlay_config";
        std::string file_content;
        if(!file_get_content(config_path.c_str(), file_content)) {
            fprintf(stderr, "Warning: Failed to read config file: %s\n", config_path.c_str());
            return config;
        }

        config = Config();
        auto config_options = get_config_options(config.value());

        string_split_char(file_content, '\n', [&](std::string_view line) {
            const std::optional<KeyValue> key_value = parse_key_value(line);
            if(!key_value) {
                fprintf(stderr, "Warning: Invalid config option format: %.*s\n", (int)line.size(), line.data());
                return true;
            }

            if(key_value->key.empty() || key_value->value.empty())
                return true;

            auto it = config_options.find(key_value->key);
            if(it == config_options.end())
                return true;

            if(std::holds_alternative<bool*>(it->second)) {
                *std::get<bool*>(it->second) = key_value->value == "true";
            } else if(std::holds_alternative<std::string*>(it->second)) {
                std::get<std::string*>(it->second)->assign(key_value->value.data(), key_value->value.size());
            } else if(std::holds_alternative<int32_t*>(it->second)) {
                std::string value_str(key_value->value);
                int32_t *value = std::get<int32_t*>(it->second);
                if(sscanf(value_str.c_str(), FORMAT_I32, value) != 1) {
                    fprintf(stderr, "Warning: Invalid config option value for %.*s\n", (int)key_value->key.size(), key_value->key.data());
                    *value = 0;
                }
            } else if(std::holds_alternative<ConfigHotkey*>(it->second)) {
                std::string value_str(key_value->value);
                ConfigHotkey *config_hotkey = std::get<ConfigHotkey*>(it->second);
                if(sscanf(value_str.c_str(), FORMAT_I64 " " FORMAT_U32, &config_hotkey->keysym, &config_hotkey->modifiers) != 2) {
                    fprintf(stderr, "Warning: Invalid config option value for %.*s\n", (int)key_value->key.size(), key_value->key.data());
                    config_hotkey->keysym = 0;
                    config_hotkey->modifiers = 0;
                }
            } else if(std::holds_alternative<std::vector<std::string>*>(it->second)) {
                std::string array_value(key_value->value);
                std::get<std::vector<std::string>*>(it->second)->push_back(std::move(array_value));
            }

            return true;
        });

        if(config->main_config.config_file_version != CONFIG_FILE_VERSION) {
            fprintf(stderr, "Info: the config file is outdated, resetting it\n");
            config = std::nullopt;
        }

        return config;
    }

    void save_config(Config &config) {
        config.main_config.config_file_version = CONFIG_FILE_VERSION;

        const std::string config_path = get_config_dir() + "/overlay_config";

        char dir_tmp[PATH_MAX];
        snprintf(dir_tmp, sizeof(dir_tmp), "%s", config_path.c_str());
        char *dir = dirname(dir_tmp);

        if(create_directory_recursive(dir) != 0) {
            fprintf(stderr, "Warning: Failed to create config directory: %s\n", dir);
            return;
        }

        FILE *file = fopen(config_path.c_str(), "wb");
        if(!file) {
            fprintf(stderr, "Warning: Failed to create config file: %s\n", config_path.c_str());
            return;
        }

        const auto config_options = get_config_options(config);
        for(auto it : config_options) {
            if(std::holds_alternative<bool*>(it.second)) {
                fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), *std::get<bool*>(it.second) ? "true" : "false");
            } else if(std::holds_alternative<std::string*>(it.second)) {
                fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), std::get<std::string*>(it.second)->c_str());
            } else if(std::holds_alternative<int32_t*>(it.second)) {
                fprintf(file, "%.*s " FORMAT_I32 "\n", (int)it.first.size(), it.first.data(), *std::get<int32_t*>(it.second));
            } else if(std::holds_alternative<ConfigHotkey*>(it.second)) {
                const ConfigHotkey *config_hotkey = std::get<ConfigHotkey*>(it.second);
                    fprintf(file, "%.*s " FORMAT_I64 " " FORMAT_U32 "\n", (int)it.first.size(), it.first.data(), config_hotkey->keysym, config_hotkey->modifiers);
            } else if(std::holds_alternative<std::vector<std::string>*>(it.second)) {
                std::vector<std::string> *array = std::get<std::vector<std::string>*>(it.second);
                for(const std::string &value : *array) {
                    fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), value.c_str());
                }
            }
        }

        fclose(file);
    }
}