102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
schema.c
Go to the documentation of this file.
1#include "config_internal.h"
2#include <dttr_log.h>
3#include <dttr_path.h>
4
5#include <khash.h>
6
7#include <stddef.h>
8#include <stdio.h>
9#include <string.h>
10
11#define CONFIG_LOOKUP_KEY_CAPACITY 64
12
13KHASH_MAP_INIT_STR(dttr_config_lookup, int)
14
15// clang-format off
16#define FIELD(_section, _key, _field, _type) \
17 { \
18 .section = (_section), \
19 .key = (_key), \
20 .offset = offsetof(DTTR_Config, _field), \
21 .size = sizeof(((DTTR_Config *)0)->_field), \
22 .value_type = (_type) \
23 }
24
25#define FIELD_TOP(_key, _field, _type) FIELD(NULL, _key, _field, _type)
26#define FIELD_GAMEPAD_AXIS(_key, _index) \
27 FIELD("gamepad", _key, gamepad_axes[_index], CONFIG_GAMEPAD_AXIS),
28#define FIELD_GAMEPAD_DEADZONE(_key, _index) \
29 FIELD("gamepad", _key, gamepad_axis_deadzone[_index], CONFIG_INT),
30
32 FIELD_TOP("schema_major_version", schema_major_version, CONFIG_INT),
33 FIELD("graphics", "scaling_fit", scaling_fit, CONFIG_SCALING_FIT),
34 FIELD("graphics", "scaling_method", scaling_method, CONFIG_SCALING_METHOD),
35 FIELD("graphics", "graphics_api", graphics_api, CONFIG_GRAPHICS_API),
36 FIELD(
37 "graphics",
38 "present_scaling_algorithm",
39 present_filter,
41 ),
42 FIELD("graphics", "window_width", window_width, CONFIG_INT),
43 FIELD("graphics", "window_height", window_height, CONFIG_INT),
44 FIELD("graphics", "msaa_samples", msaa_samples, CONFIG_INT),
45 FIELD("graphics", "texture_upload_sync", texture_upload_sync, CONFIG_BOOL),
46 FIELD("graphics", "generate_texture_mipmaps", generate_texture_mipmaps, CONFIG_BOOL),
47 FIELD("graphics", "vertex_precision", vertex_precision, CONFIG_VERTEX_PRECISION),
48 FIELD("graphics", "sprite_smooth", sprite_smooth, CONFIG_BOOL),
49 FIELD("graphics", "fullscreen", fullscreen, CONFIG_BOOL),
50
51 FIELD("audio", "mss_sample_gain", mss_sample_gain, CONFIG_FLOAT),
52 FIELD("audio", "mss_sample_preemphasis", mss_sample_preemphasis, CONFIG_FLOAT),
53
54 FIELD("modding", "hot_reload", hot_reload, CONFIG_BOOL),
55
56 FIELD_TOP("log_level", log_level, CONFIG_LOG_LEVEL),
58 FIELD_TOP("show_crash_popup", show_crash_popup, CONFIG_BOOL),
59 FIELD_TOP("log_file_path", log_file_path, CONFIG_STRING),
60 FIELD_TOP("pcdogs_path", pcdogs_path, CONFIG_STRING),
61 FIELD_TOP("saves_path", saves_path, CONFIG_STRING),
62 FIELD_TOP("skip_intro_movies", skip_intro_movies, CONFIG_BOOL),
63
64 FIELD("gamepad", "enabled", gamepad_enabled, CONFIG_BOOL),
65 FIELD("gamepad", "index", gamepad_index, CONFIG_INT),
68};
69
70// clang-format on
71
72#define CONFIG_SCHEMA_COUNT ((int)SDL_arraysize(config_schema))
73
74#undef FIELD_GAMEPAD_DEADZONE
75#undef FIELD_GAMEPAD_AXIS
76
77static khash_t(dttr_config_lookup) *config_lookup = NULL;
78static char config_lookup_keys[CONFIG_SCHEMA_COUNT][CONFIG_LOOKUP_KEY_CAPACITY];
79
80static bool config_build_lookup_key(
81 char *out,
82 size_t out_size,
83 const char *section,
84 const char *key
85) {
86 const int written = section ? snprintf(out, out_size, "%s.%s", section, key)
87 : snprintf(out, out_size, "%s", key);
88 return written > 0 && (size_t)written < out_size;
89}
90
94
96 if (index < 0 || index >= CONFIG_SCHEMA_COUNT) {
97 return NULL;
98 }
99
100 return &config_schema[index];
101}
102
103static const char *config_field_bytes(
104 const DTTR_Config *config,
105 const DTTR_ConfigFieldSpec *spec
106) {
107 return ((const char *)config) + spec->offset;
108}
109
111 const DTTR_Config *current,
112 const DTTR_Config *base,
113 const DTTR_ConfigFieldSpec *spec
114) {
115 if (!current || !base || !spec) {
116 return false;
117 }
118
119 const char *current_field = config_field_bytes(current, spec);
120 const char *base_field = config_field_bytes(base, spec);
122 return !DTTR_Path_MatchesNormalized(current_field, base_field);
123 }
124
125 return spec->size > 0 && memcmp(current_field, base_field, spec->size) != 0;
126}
127
128bool DTTR_Config_SchemaChanged(const DTTR_Config *current, const DTTR_Config *base) {
129 for (int i = 0; i < CONFIG_SCHEMA_COUNT; i++) {
130 const DTTR_ConfigFieldSpec *const spec = &config_schema[i];
131 if (DTTR_Config_FieldChanged(current, base, spec)) {
132 return true;
133 }
134 }
135
136 return false;
137}
138
139static void config_schema_init() {
140 if (config_lookup) {
141 return;
142 }
143
144 config_lookup = kh_init(dttr_config_lookup);
145 if (!config_lookup) {
146 return;
147 }
148
149 for (int i = 0; i < CONFIG_SCHEMA_COUNT; i++) {
150 const DTTR_ConfigFieldSpec *const spec = &config_schema[i];
151 if (!config_build_lookup_key(
152 config_lookup_keys[i],
153 sizeof(config_lookup_keys[i]),
154 spec->section,
155 spec->key
156 )) {
157 continue;
158 }
159
160 int put_ret = 0;
161 const khint_t it = kh_put(
162 dttr_config_lookup,
163 config_lookup,
164 config_lookup_keys[i],
165 &put_ret
166 );
167 if (it != kh_end(config_lookup)) {
168 kh_value(config_lookup, it) = i;
169 }
170 }
171}
172
173const DTTR_ConfigFieldSpec *config_schema_find(const char *section, const char *key) {
175 if (!config_lookup) {
176 return NULL;
177 }
178
179 char lookup_key[CONFIG_LOOKUP_KEY_CAPACITY];
180 if (!config_build_lookup_key(lookup_key, sizeof(lookup_key), section, key)) {
181 return NULL;
182 }
183
184 const khint_t it = kh_get(dttr_config_lookup, config_lookup, lookup_key);
185 if (it == kh_end(config_lookup)) {
186 return NULL;
187 }
188
189 const int index = kh_value(config_lookup, it);
190 return DTTR_Config_SchemaGet(index);
191}
192
193#define CONFIG_ASSIGN_TYPES(X) \
194 X(CONFIG_BOOL, bool, false, config_parse_bool, config_assign_bool) \
195 X(CONFIG_SCALING_FIT, \
196 DTTR_ScalingMode, \
197 DTTR_SCALING_MODE_LETTERBOX, \
198 config_parse_scaling_fit, \
199 config_assign_scaling_fit) \
200 X(CONFIG_SCALING_METHOD, \
201 DTTR_ScalingMethod, \
202 DTTR_SCALING_METHOD_PRESENT, \
203 config_parse_scaling_method, \
204 config_assign_scaling_method) \
205 X(CONFIG_GRAPHICS_API, \
206 DTTR_GraphicsApi, \
207 DTTR_GRAPHICS_API_AUTO, \
208 config_parse_graphics_api, \
209 config_assign_graphics_api) \
210 X(CONFIG_INT, int, 0, config_parse_int, config_assign_int) \
211 X(CONFIG_FLOAT, float, 0.0f, config_parse_float, config_assign_float) \
212 X(CONFIG_PRESENT_FILTER, \
213 SDL_GPUFilter, \
214 SDL_GPU_FILTER_LINEAR, \
215 config_parse_present_filter, \
216 config_assign_present_filter) \
217 X(CONFIG_LOG_LEVEL, int, LOG_INFO, config_parse_log_level, config_assign_log_level) \
218 X(CONFIG_MINIDUMP_TYPE, \
219 DTTR_MinidumpType, \
220 DTTR_MINIDUMP_NORMAL, \
221 config_parse_minidump_type, \
222 config_assign_minidump_type) \
223 X(CONFIG_VERTEX_PRECISION, \
224 DTTR_VertexPrecision, \
225 DTTR_VERTEX_PRECISION_NATIVE, \
226 config_parse_vertex_precision, \
227 config_assign_vertex_precision) \
228 X(CONFIG_GAMEPAD_AXIS, \
229 int, \
230 DTTR_GAMEPAD_MAPPING_NONE, \
231 config_parse_gamepad_axis, \
232 config_assign_gamepad_axis)
233
234#define CONFIG_ASSIGN_FN(value_type, type, default_val, parse_fn, fn_name) \
235 static bool fn_name(char *field, const char *value) { \
236 type parsed = default_val; \
237 if (!parse_fn(value, &parsed)) { \
238 return false; \
239 } \
240 *(type *)field = parsed; \
241 return true; \
242 }
243
245
246#undef CONFIG_ASSIGN_FN
247
248static bool config_assign_string(char *field, size_t field_size, const char *value) {
249 return field_size > 0 && config_parse_string(value, field, field_size);
250}
251
253 DTTR_Config *config,
254 const char *section,
255 const char *key,
256 const char *value
257) {
258 if (!config || !key || !value) {
259 return false;
260 }
261
262 const DTTR_ConfigFieldSpec *const spec = config_schema_find(section, key);
263 if (!spec) {
264 return false;
265 }
266
267 char *const field = ((char *)config) + spec->offset;
268#define CONFIG_ASSIGN_CASE(value_type, type, default_val, parse_fn, fn_name) \
269 case value_type: \
270 return fn_name(field, value);
271
272 switch (spec->value_type) {
274 case CONFIG_STRING:
275 return config_assign_string(field, spec->size, value);
276
277 default:
278 return false;
279 }
280
281#undef CONFIG_ASSIGN_CASE
282}
283
284#undef CONFIG_SCHEMA_COUNT
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
bool config_parse_string(const char *value, char *out_value, size_t out_size)
Definition parse.c:386
#define CONFIG_GAMEPAD_AXIS_FIELDS(X)
#define CONFIG_GAMEPAD_DEADZONE_FIELDS(X)
@ CONFIG_VERTEX_PRECISION
@ CONFIG_SCALING_FIT
@ CONFIG_MINIDUMP_TYPE
@ CONFIG_STRING
@ CONFIG_PRESENT_FILTER
@ CONFIG_GRAPHICS_API
@ CONFIG_LOG_LEVEL
@ CONFIG_INT
@ CONFIG_FLOAT
@ CONFIG_BOOL
@ CONFIG_SCALING_METHOD
static MINIDUMP_TYPE minidump_type()
Definition crashdump.c:391
@ DTTR_CONFIG_VALUE_STRING
bool DTTR_Path_MatchesNormalized(const char *lhs, const char *rhs)
Definition path.c:165
#define CONFIG_ASSIGN_CASE(value_type, type, default_val, parse_fn, fn_name)
bool config_apply_entry(DTTR_Config *config, const char *section, const char *key, const char *value)
Definition schema.c:252
static void config_schema_init()
Definition schema.c:139
static bool config_assign_string(char *field, size_t field_size, const char *value)
Definition schema.c:248
#define CONFIG_ASSIGN_TYPES(X)
Definition schema.c:193
static const char * config_field_bytes(const DTTR_Config *config, const DTTR_ConfigFieldSpec *spec)
Definition schema.c:103
#define CONFIG_SCHEMA_COUNT
Definition schema.c:72
int DTTR_Config_SchemaCount()
Definition schema.c:91
#define FIELD_TOP(_key, _field, _type)
Definition schema.c:25
#define FIELD_GAMEPAD_DEADZONE(_key, _index)
Definition schema.c:28
bool DTTR_Config_SchemaChanged(const DTTR_Config *current, const DTTR_Config *base)
Definition schema.c:128
const DTTR_ConfigFieldSpec * config_schema_find(const char *section, const char *key)
Definition schema.c:173
static const DTTR_ConfigFieldSpec config_schema[]
Definition schema.c:31
#define CONFIG_ASSIGN_FN(value_type, type, default_val, parse_fn, fn_name)
Definition schema.c:234
#define FIELD_GAMEPAD_AXIS(_key, _index)
Definition schema.c:26
const DTTR_ConfigFieldSpec * DTTR_Config_SchemaGet(int index)
Definition schema.c:95
bool DTTR_Config_FieldChanged(const DTTR_Config *current, const DTTR_Config *base, const DTTR_ConfigFieldSpec *spec)
Definition schema.c:110
#define CONFIG_LOOKUP_KEY_CAPACITY
Definition schema.c:11
static khash_t(dttr_config_lookup)
Definition schema.c:77
#define FIELD(_section, _key, _field, _type)
Definition schema.c:16
DTTR_ConfigValueType value_type
const char * section