SDL_Libretro
SDL3-power libretro frontend.
Loading...
Searching...
No Matches
SDL_libretro_options.h
Go to the documentation of this file.
1
6#if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_OPTIONS_IMPL_ONCE)
7#define SDL_LIBRETRO_OPTIONS_IMPL_ONCE
8
9static char* SDL_Libretro_Strdup(const char* s) {
10 if (!s) return SDL_strdup("");
11 return SDL_strdup(s);
12}
13
14static void SDL_Libretro_InitCoreOption(SDL_Libretro* lr, const char* key, const char* defaultValue,
15 const char* desc, const struct retro_core_option_value* values,
16 const char* info, const char* categoryKey) {
17 if (!lr || !key) return;
18
19 // Check if already registered. Re-registration is intentionally ignored:
20 // the first definition wins, so an option's declared values and default stay
21 // stable for the life of the core.
22 if (SDL_Libretro_GetOption(lr, key)) {
23 return;
24 }
25
26 // Grow array if needed
27 if (lr->core.optionCount >= lr->core.optionCapacity) {
28 unsigned newCap = lr->core.optionCapacity ? lr->core.optionCapacity * 2 : 16;
29 SDL_LibretroOption* newOpts = (SDL_LibretroOption*)SDL_realloc(lr->core.options, newCap * sizeof(SDL_LibretroOption));
30 if (!newOpts) return;
31 lr->core.options = newOpts;
32 lr->core.optionCapacity = newCap;
33 }
34
35 // Set the option; strings are deep-copied and owned by the context.
36 SDL_LibretroOption* slot = &lr->core.options[lr->core.optionCount];
37 slot->key = SDL_Libretro_Strdup(key);
38 slot->value = SDL_Libretro_Strdup(defaultValue);
39 slot->defaultValue = SDL_Libretro_Strdup(defaultValue);
40 slot->desc = SDL_Libretro_Strdup(desc);
41 slot->info = SDL_Libretro_Strdup(info);
42 slot->category = SDL_Libretro_Strdup(categoryKey);
43 slot->visible = true;
44
45 // The available values.
46 slot->values = NULL;
47 slot->valuesCount = 0;
48 slot->valuesCapacity = 0;
49 if (values) {
50 // Count how many values there are available for the option.
51 unsigned count = 0;
52 while (values[count].value) count++;
53
54 // Build the available values.
55 if (count > 0) {
56 slot->values = (SDL_LibretroOptionValue*)SDL_calloc(count, sizeof(SDL_LibretroOptionValue));
57 if (slot->values) {
58 slot->valuesCapacity = count;
59 for (unsigned v = 0; v < count; v++) {
60 slot->values[v].value = SDL_Libretro_Strdup(values[v].value);
61 slot->values[v].label = values[v].label ? SDL_Libretro_Strdup(values[v].label) : NULL;
62 slot->valuesCount++;
63 }
64 }
65 }
66 }
67
68 lr->core.optionCount++;
69}
70
71static void SDL_Libretro_InitCoreOptionCategory(SDL_Libretro* lr, const char* key,
72 const char* desc, const char* info) {
73 if (!lr || !key || key[0] == '\0') return;
74
75 // Check if already registered
76 for (unsigned i = 0; i < lr->core.optionCategoryCount; i++) {
77 if (lr->core.optionCategories[i].key && SDL_strcmp(lr->core.optionCategories[i].key, key) == 0) {
78 return;
79 }
80 }
81
82 // Grow array if needed
83 if (lr->core.optionCategoryCount >= lr->core.optionCategoryCapacity) {
84 unsigned newCap = lr->core.optionCategoryCapacity ? lr->core.optionCategoryCapacity * 2 : 8;
85 SDL_LibretroCategory* newCats = (SDL_LibretroCategory*)SDL_realloc(lr->core.optionCategories, newCap * sizeof(SDL_LibretroCategory));
86 if (!newCats) return;
87 lr->core.optionCategories = newCats;
88 lr->core.optionCategoryCapacity = newCap;
89 }
90
91 // Set the category; strings are deep-copied and owned by the context.
92 SDL_LibretroCategory* cat = &lr->core.optionCategories[lr->core.optionCategoryCount];
93 cat->key = SDL_Libretro_Strdup(key);
94 cat->desc = SDL_Libretro_Strdup(desc);
95 cat->info = SDL_Libretro_Strdup(info);
96
97 lr->core.optionCategoryCount++;
98}
99
100static void SDL_Libretro_FreeCoreOptions(SDL_Libretro* lr) {
101 if (!lr) return;
102
103 // Options
104 if (lr->core.options) {
105 for (unsigned i = 0; i < lr->core.optionCount; i++) {
106 SDL_LibretroOption* opt = &lr->core.options[i];
107 SDL_free((void*)opt->key);
108 SDL_free((void*)opt->value);
109 SDL_free((void*)opt->defaultValue);
110 SDL_free((void*)opt->desc);
111 SDL_free((void*)opt->info);
112 SDL_free((void*)opt->category);
113 for (unsigned v = 0; v < opt->valuesCount; v++) {
114 SDL_free((void*)opt->values[v].value);
115 SDL_free((void*)opt->values[v].label);
116 }
117 SDL_free(opt->values);
118 }
119 SDL_free(lr->core.options);
120 lr->core.options = NULL;
121 }
122 lr->core.optionCount = 0;
123 lr->core.optionCapacity = 0;
124
125 // Categories
126 if (lr->core.optionCategories) {
127 for (unsigned i = 0; i < lr->core.optionCategoryCount; i++) {
128 SDL_free((void*)lr->core.optionCategories[i].key);
129 SDL_free((void*)lr->core.optionCategories[i].desc);
130 SDL_free((void*)lr->core.optionCategories[i].info);
131 }
132 SDL_free(lr->core.optionCategories);
133 lr->core.optionCategories = NULL;
134 }
135 lr->core.optionCategoryCount = 0;
136 lr->core.optionCategoryCapacity = 0;
137}
138
142unsigned SDL_Libretro_GetOptionCount(const SDL_Libretro* lr) {
143 return lr ? lr->core.optionCount : 0;
144}
145
149const SDL_LibretroOption* SDL_Libretro_GetOption(const SDL_Libretro* lr, const char* key) {
150 if (!lr || !key) return NULL;
151 for (unsigned i = 0; i < lr->core.optionCount; i++) {
152 if (lr->core.options[i].key && SDL_strcmp(lr->core.options[i].key, key) == 0) {
153 return &lr->core.options[i];
154 }
155 }
156 return NULL;
157}
158
162const SDL_LibretroOption* SDL_Libretro_GetOptionByIndex(const SDL_Libretro* lr, unsigned index) {
163 if (!lr || index >= lr->core.optionCount) return NULL;
164 return &lr->core.options[index];
165}
166
170bool SDL_Libretro_SetOptionValue(SDL_Libretro* lr, const char* key, const char* value) {
171 if (!lr || !key || !value) return false;
173 if (!opt) return false;
174
175 // Reject values the core never offered, so GET_VARIABLE only ever hands the
176 // core a value it declared. Options with no declared values accept anything.
177 if (opt->valuesCount > 0) {
178 bool valid = false;
179 for (unsigned v = 0; v < opt->valuesCount; v++) {
180 if (opt->values[v].value && SDL_strcmp(opt->values[v].value, value) == 0) {
181 valid = true;
182 break;
183 }
184 }
185 if (!valid) {
186 return SDL_SetError("[SDL_Libretro] '%s' is not a valid value for option '%s'", value, key);
187 }
188 }
189
190 SDL_free((void*)opt->value);
191 opt->value = SDL_strdup(value);
192 lr->core.optionsDirty = true;
193 return true;
194}
195
199const char* SDL_Libretro_GetOptionValue(SDL_Libretro* lr, const char* key) {
200 const SDL_LibretroOption* opt = SDL_Libretro_GetOption(lr, key);
201 if (!opt) return NULL;
202 return opt->value;
203}
204
211const char* SDL_Libretro_GetOptionValueLabel(SDL_Libretro* lr, const char* key) {
212 const SDL_LibretroOption* opt = SDL_Libretro_GetOption(lr, key);
213 if (!opt || !opt->value) return NULL;
214 for (unsigned v = 0; v < opt->valuesCount; v++) {
215 if (opt->values[v].value && SDL_strcmp(opt->values[v].value, opt->value) == 0) {
216 return opt->values[v].label ? opt->values[v].label : opt->values[v].value;
217 }
218 }
219 return opt->value;
220}
221
225bool SDL_Libretro_CycleOptionValue(SDL_Libretro* lr, const char* key, int direction) {
226 if (direction == 0) return false;
228 if (!opt || opt->valuesCount == 0) return false;
229
230 // Find the current value's index; default to 0 if it isn't in the list.
231 unsigned current = 0;
232 for (unsigned v = 0; v < opt->valuesCount; v++) {
233 if (opt->values[v].value && opt->value && SDL_strcmp(opt->values[v].value, opt->value) == 0) {
234 current = v;
235 break;
236 }
237 }
238
239 unsigned count = opt->valuesCount;
240 unsigned step = (direction > 0) ? 1 : count - 1;
241 unsigned next = (current + step) % count;
242 return SDL_Libretro_SetOptionValue(lr, key, opt->values[next].value);
243}
244
248bool SDL_Libretro_ResetOption(SDL_Libretro* lr, const char* key) {
250 if (!opt) return false;
251 SDL_free((void*)opt->value);
252 opt->value = SDL_strdup(opt->defaultValue);
253 lr->core.optionsDirty = true;
254 return true;
255}
256
260void SDL_Libretro_ResetAllOptions(SDL_Libretro* lr) {
261 if (!lr) return;
262 for (unsigned i = 0; i < lr->core.optionCount; i++) {
263 SDL_LibretroOption* opt = &lr->core.options[i];
264 SDL_free((void*)opt->value);
265 opt->value = SDL_strdup(opt->defaultValue);
266 }
267 lr->core.optionsDirty = true;
268}
269
273bool SDL_Libretro_AreOptionsDirty(SDL_Libretro* lr) {
274 if (!lr) return false;
275 bool dirty = lr->core.optionsDirty;
276 lr->core.optionsDirty = false;
277 return dirty;
278}
279
280bool SDL_Libretro_UpdateOptionVisibility(SDL_Libretro* lr) {
281 if (!lr || !lr->core.optionsUpdateDisplayCallback) return false;
282 return lr->core.optionsUpdateDisplayCallback();
283}
284
288unsigned SDL_Libretro_GetCategoryCount(const SDL_Libretro* lr) {
289 return lr ? lr->core.optionCategoryCount : 0;
290}
291
295const SDL_LibretroCategory* SDL_Libretro_GetCategory(const SDL_Libretro* lr, const char* key) {
296 if (!lr || !key) return NULL;
297 for (unsigned i = 0; i < lr->core.optionCategoryCount; i++) {
298 if (lr->core.optionCategories[i].key && SDL_strcmp(lr->core.optionCategories[i].key, key) == 0) {
299 return &lr->core.optionCategories[i];
300 }
301 }
302 return NULL;
303}
304
308const SDL_LibretroCategory* SDL_Libretro_GetCategoryByIndex(const SDL_Libretro* lr, unsigned index) {
309 if (!lr || index >= lr->core.optionCategoryCount) return NULL;
310 return &lr->core.optionCategories[index];
311}
312
313#endif /* SDL_LIBRETRO_OPTIONS_IMPL_ONCE */
bool SDL_Libretro_CycleOptionValue(SDL_Libretro *lr, const char *key, int direction)
Advance a core option to the next (+1) or previous (-1) value.
bool SDL_Libretro_AreOptionsDirty(SDL_Libretro *lr)
Retrieves whether the options have been changed since the last time they were checked.
const SDL_LibretroCategory * SDL_Libretro_GetCategoryByIndex(const SDL_Libretro *lr, unsigned index)
Retrieve an option category by index, or NULL if out of range.
const SDL_LibretroCategory * SDL_Libretro_GetCategory(const SDL_Libretro *lr, const char *key)
Retrieve an option category by key, or NULL if there's no such category.
unsigned SDL_Libretro_GetCategoryCount(const SDL_Libretro *lr)
Get the number of option categories registered by the core.
const char * SDL_Libretro_GetOptionValueLabel(SDL_Libretro *lr, const char *key)
Get the human-readable label for a core option's current value.
const SDL_LibretroOption * SDL_Libretro_GetOptionByIndex(const SDL_Libretro *lr, unsigned index)
Retrieve a core option by its registration index, or NULL if out of range.
const char * SDL_Libretro_GetOptionValue(SDL_Libretro *lr, const char *key)
Get a core option's current value, or NULL if there's no such option.
bool SDL_Libretro_ResetOption(SDL_Libretro *lr, const char *key)
Reset a core option to its default value.
bool SDL_Libretro_SetOptionValue(SDL_Libretro *lr, const char *key, const char *value)
Set a core option's value; fails if the value isn't one the core declared.
void SDL_Libretro_ResetAllOptions(SDL_Libretro *lr)
Reset every core option to its default value.
unsigned SDL_Libretro_GetOptionCount(const SDL_Libretro *lr)
Get the number of core options that have been set.
const SDL_LibretroOption * SDL_Libretro_GetOption(const SDL_Libretro *lr, const char *key)
Retrieve details about a given core option.
A group of related core options.
const char * desc
Unique identifier referenced by SDL_LibretroOption::category.
const char * info
Human-readable name.
A core option and its current state.
bool visible
Key of the category it belongs to; empty if none.
SDL_LibretroOptionValue * values
The number of populated entries in values.
unsigned valuesCapacity
The selectable values; dynamically allocated.
const char * desc
Unique identifier the core queries.
const char * value
Description / help text; may be empty.
const char * category
The default value.
unsigned valuesCount
Whether the frontend should display it.
const char * defaultValue
The current value.
const char * info
Human-readable name.
One selectable value for a core option.
const char * label
The value the core stores.