6#if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_OPTIONS_IMPL_ONCE)
7#define SDL_LIBRETRO_OPTIONS_IMPL_ONCE
9static char* SDL_Libretro_Strdup(
const char* s) {
10 if (!s)
return SDL_strdup(
"");
18static bool SDL_Libretro_IsOptionValueValid(
const SDL_LibretroOption* opt,
const char* value) {
19 if (!opt || !value)
return false;
35 for (
unsigned v = 0; v < count; v++) {
36 SDL_free((
void*)values[v].value);
37 SDL_free((
void*)values[v].label);
51static bool SDL_Libretro_CopyOptionValues(
const struct retro_core_option_value* values,
unsigned valuesMax,
SDL_LibretroOptionValue** outValues,
unsigned* outCount) {
54 if (!values)
return true;
58 while (count < valuesMax && values[count].value) count++;
59 if (count == 0)
return true;
62 if (!copy)
return false;
63 for (
unsigned v = 0; v < count; v++) {
64 copy[v].
value = SDL_strdup(values[v].value);
65 copy[v].
label = values[v].label ? SDL_strdup(values[v].label) : NULL;
66 if (!copy[v].value || (values[v].label && !copy[v].label)) {
67 SDL_Libretro_FreeOptionValues(copy, v + 1);
76static void SDL_Libretro_InitCoreOption(SDL_Libretro* lr,
const char* key,
const char* defaultValue,
const char* desc,
const struct retro_core_option_value* values,
unsigned valuesMax,
const char* info,
const char* categoryKey) {
77 if (!lr || !key)
return;
80 bool reregistered = slot != NULL;
83 if (!reregistered && lr->core.optionCount >= lr->core.optionCapacity) {
84 unsigned newCap = lr->core.optionCapacity ? lr->core.optionCapacity * 2 : 16;
87 lr->core.options = newOpts;
88 lr->core.optionCapacity = newCap;
94 copy.
key = reregistered ? NULL : SDL_Libretro_Strdup(key);
96 copy.
desc = SDL_Libretro_Strdup(desc);
97 copy.
info = SDL_Libretro_Strdup(info);
98 copy.
category = SDL_Libretro_Strdup(categoryKey);
100 ok = ok && SDL_Libretro_CopyOptionValues(values, valuesMax, ©.values, ©.valuesCount);
101 copy.valuesCapacity = copy.valuesCount;
107 const char* current = reregistered ? slot->
value : NULL;
108 bool keepCurrent = current && SDL_Libretro_IsOptionValueValid(©, current);
109 if (ok && !keepCurrent) {
110 const char* saved = SDL_Libretro_GetSavedCoreOption(lr, key);
111 copy.
value = SDL_strdup((saved && SDL_Libretro_IsOptionValueValid(©, saved)) ? saved : copy.defaultValue);
112 ok = copy.
value != NULL;
116 SDL_free((
void*)copy.key);
117 SDL_free((
void*)copy.
value);
118 SDL_free((
void*)copy.defaultValue);
119 SDL_free((
void*)copy.desc);
120 SDL_free((
void*)copy.info);
121 SDL_free((
void*)copy.category);
122 SDL_Libretro_FreeOptionValues(copy.values, copy.valuesCount);
123 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] Out of memory registering option '%s'", key);
130 lr->core.options[lr->core.optionCount++] = copy;
136 SDL_free((
void*)slot->
desc);
137 SDL_free((
void*)slot->
info);
141 slot->
desc = copy.desc;
142 slot->
info = copy.info;
144 slot->
values = copy.values;
148 SDL_free((
void*)slot->
value);
150 lr->core.optionsDirtyCore =
true;
152 lr->core.optionsDirtyApp =
true;
155static void SDL_Libretro_InitCoreOptionCategory(SDL_Libretro* lr,
const char* key,
const char* desc,
const char* info) {
156 if (!lr || !key || key[0] ==
'\0')
return;
159 for (
unsigned i = 0; i < lr->core.optionCategoriesCount; i++) {
161 if (existing->
key && SDL_strcmp(existing->
key, key) == 0) {
163 char* newDesc = SDL_Libretro_Strdup(desc);
164 char* newInfo = SDL_Libretro_Strdup(info);
165 if (!newDesc || !newInfo) {
168 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] Out of memory registering option category '%s'", key);
171 SDL_free((
void*)existing->
desc);
172 SDL_free((
void*)existing->
info);
173 existing->
desc = newDesc;
174 existing->
info = newInfo;
175 lr->core.optionsDirtyApp =
true;
181 if (lr->core.optionCategoriesCount >= lr->core.optionCategoriesCapacity) {
182 unsigned newCap = lr->core.optionCategoriesCapacity ? lr->core.optionCategoriesCapacity * 2 : 8;
184 if (!newCats)
return;
185 lr->core.optionCategories = newCats;
186 lr->core.optionCategoriesCapacity = newCap;
191 cat->
key = SDL_Libretro_Strdup(key);
192 cat->
desc = SDL_Libretro_Strdup(desc);
193 cat->
info = SDL_Libretro_Strdup(info);
195 SDL_free((
void*)cat->
key);
196 SDL_free((
void*)cat->
desc);
197 SDL_free((
void*)cat->
info);
198 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] Out of memory registering option category '%s'", key);
202 lr->core.optionCategoriesCount++;
205static void SDL_Libretro_FreeCoreOptions(SDL_Libretro* lr) {
209 if (lr->core.options) {
210 for (
unsigned i = 0; i < lr->core.optionCount; i++) {
212 SDL_free((
void*)opt->
key);
213 SDL_free((
void*)opt->
value);
215 SDL_free((
void*)opt->
desc);
216 SDL_free((
void*)opt->
info);
220 SDL_free(lr->core.options);
221 lr->core.options = NULL;
223 lr->core.optionCount = 0;
224 lr->core.optionCapacity = 0;
227 if (lr->core.optionCategories) {
228 for (
unsigned i = 0; i < lr->core.optionCategoriesCount; i++) {
229 SDL_free((
void*)lr->core.optionCategories[i].key);
230 SDL_free((
void*)lr->core.optionCategories[i].desc);
231 SDL_free((
void*)lr->core.optionCategories[i].info);
233 SDL_free(lr->core.optionCategories);
234 lr->core.optionCategories = NULL;
236 lr->core.optionCategoriesCount = 0;
237 lr->core.optionCategoriesCapacity = 0;
249 return lr ? lr->core.optionCount : 0;
266 if (!lr || !key)
return NULL;
267 for (
unsigned i = 0; i < lr->core.optionCount; i++) {
268 if (lr->core.options[i].key && SDL_strcmp(lr->core.options[i].key, key) == 0) {
269 return &lr->core.options[i];
288 if (!lr || index >= lr->core.optionCount)
return NULL;
289 return &lr->core.options[index];
307 if (!lr || !key || !value) {
308 SDL_SetError(
"[SDL_Libretro] Invalid SetOptionValue arguments");
313 return SDL_SetError(
"[SDL_Libretro] No such option '%s'", key);
318 if (!SDL_Libretro_IsOptionValueValid(opt, value)) {
319 return SDL_SetError(
"[SDL_Libretro] '%s' is not a valid value for option '%s'", value, key);
324 char* dup = SDL_strdup(value);
326 return SDL_SetError(
"[SDL_Libretro] Out of memory setting option '%s'", key);
328 SDL_free((
void*)opt->
value);
330 lr->core.optionsDirtyCore =
true;
331 lr->core.optionsDirtyApp =
true;
349 if (!opt)
return NULL;
367 if (!opt || !opt->
value)
return NULL;
389 if (direction == 0) {
390 return SDL_InvalidParamError(
"direction");
394 return SDL_SetError(
"[SDL_Libretro] No such option '%s'", key);
397 return SDL_SetError(
"[SDL_Libretro] Option '%s' has no declared values to cycle", key);
401 unsigned current = 0;
410 unsigned step = (direction > 0) ? 1 : count - 1;
411 unsigned next = (current + step) % count;
427 return SDL_InvalidParamError(
"opt");
429 char* dupicateValue = SDL_Libretro_Strdup(opt->
defaultValue);
430 if (!dupicateValue) {
431 return SDL_SetError(
"[SDL_Libretro] Out of memory resetting option '%s'", key);
433 SDL_free((
void*)opt->
value);
434 opt->
value = dupicateValue;
435 lr->core.optionsDirtyCore =
true;
436 lr->core.optionsDirtyApp =
true;
449 for (
unsigned i = 0; i < lr->core.optionCount; i++) {
451 char* dupicateValue = SDL_Libretro_Strdup(opt->
defaultValue);
452 if (!dupicateValue)
continue;
453 SDL_free((
void*)opt->
value);
454 opt->
value = dupicateValue;
456 lr->core.optionsDirtyCore =
true;
457 lr->core.optionsDirtyApp =
true;
470 if (!lr)
return false;
471 bool dirty = lr->core.optionsDirtyApp;
472 lr->core.optionsDirtyApp =
false;
490 if (!lr || !lr->core.optionsUpdateDisplayCallback)
return false;
491 return lr->core.optionsUpdateDisplayCallback();
503 return lr ? lr->core.optionCategoriesCount : 0;
516 if (!lr || !key)
return NULL;
517 for (
unsigned i = 0; i < lr->core.optionCategoriesCount; i++) {
518 if (lr->core.optionCategories[i].key && SDL_strcmp(lr->core.optionCategories[i].key, key) == 0) {
519 return &lr->core.optionCategories[i];
535 if (!lr || index >= lr->core.optionCategoriesCount)
return NULL;
536 return &lr->core.optionCategories[index];
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_UpdateOptionVisibility(SDL_Libretro *lr)
Ask the core to refresh which of its options should be displayed.
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.
const SDL_LibretroCategory * SDL_Libretro_GetCategory(const SDL_Libretro *lr, const char *key)
Retrieve an option category by key.
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.
const char * SDL_Libretro_GetOptionValue(SDL_Libretro *lr, const char *key)
Get a core option's current value.
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 * key
Unique identifier referenced by SDL_LibretroOption::category.
const char * desc
Human-readable name.
const char * info
Description / help text; may be empty.
A core option and its current state.
SDL_LibretroOptionValue * values
The selectable values; dynamically allocated.
unsigned valuesCapacity
Allocated capacity of the values array.
const char * desc
Human-readable name.
const char * value
The current value.
const char * category
Key of the category it belongs to; empty if none.
unsigned valuesCount
The number of populated entries in values.
const char * key
Unique identifier the core queries.
const char * defaultValue
The default value.
const char * info
Description / help text; may be empty.
One selectable value for a core option.
const char * value
The value the core stores.
const char * label
Human-readable name; may be NULL, in which case display value itself.