git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / samples / core_options / example_categories / libretro_core_options.h
1 #ifndef LIBRETRO_CORE_OPTIONS_H__
2 #define LIBRETRO_CORE_OPTIONS_H__
3
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <libretro.h>
8 #include <retro_inline.h>
9
10 #ifndef HAVE_NO_LANGEXTRA
11 #include "libretro_core_options_intl.h"
12 #endif
13
14 /*
15  ********************************
16  * VERSION: 2.0
17  ********************************
18  *
19  * - 2.0: Add support for core options v2 interface
20  * - 1.3: Move translations to libretro_core_options_intl.h
21  *        - libretro_core_options_intl.h includes BOM and utf-8
22  *          fix for MSVC 2010-2013
23  *        - Added HAVE_NO_LANGEXTRA flag to disable translations
24  *          on platforms/compilers without BOM support
25  * - 1.2: Use core options v1 interface when
26  *        RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1
27  *        (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)
28  * - 1.1: Support generation of core options v0 retro_core_option_value
29  *        arrays containing options with a single value
30  * - 1.0: First commit
31 */
32
33 #ifdef __cplusplus
34 extern "C" {
35 #endif
36
37 /*
38  ********************************
39  * Core Option Definitions
40  ********************************
41 */
42
43 /* RETRO_LANGUAGE_ENGLISH */
44
45 /* Default language:
46  * - All other languages must include the same keys and values
47  * - Will be used as a fallback in the event that frontend language
48  *   is not available
49  * - Will be used as a fallback for any missing entries in
50  *   frontend language definition */
51
52 struct retro_core_option_v2_category option_cats_us[] = {
53    {
54       "video",                     /* key (category name) */
55       "Video",                     /* category description (label) */
56       "Configure display options." /* category sublabel */
57    },
58    {
59       "hacks",
60       "Advanced",
61       "Options affecting low-level emulation performance and accuracy."
62    },
63    { NULL, NULL, NULL },
64 };
65
66 struct retro_core_option_v2_definition option_defs_us[] = {
67    {
68       "mycore_region",                            /* key (option name) */
69       "Console Region",                           /* description (label) */
70       NULL,                                       /* 'categorised' description (used instead of
71                                                    * 'description' if frontend has category
72                                                    * support; if NULL or empty, regular
73                                                    * description is always used */
74       "Specify which region the system is from.", /* sublabel */
75       NULL,                                       /* 'categorised' sublabel (used instead of
76                                                    * 'sublabel' if frontend has category
77                                                    * support; if NULL or empty, regular
78                                                    * sublabel is always used */
79       NULL,                                       /* category key (must match an entry in
80                                                    * option_cats_us; if NULL or empty,
81                                                    * option is uncategorised */
82       {
83          { "auto",   "Auto" },                    /* value_1, value_1_label */
84          { "ntsc-j", "Japan" },                   /* value_2, value_2_label */
85          { "ntsc-u", "America" },                 /* value_3, value_3_label */
86          { "pal",    "Europe" },                  /* value_4, value_4_label */
87          { NULL, NULL },
88       },
89       "auto"                                      /* default_value */
90    },
91    {
92       "mycore_video_scale",
93       "Video > Scale",   /* description: here a 'Video >' prefix is used to
94                           * signify a category on frontends without explicit
95                           * category support */
96       "Scale",           /* 'categorised' description: will be displayed inside
97                           * the 'Video' submenu */
98       "Set internal video scale factor.",
99       NULL,
100       "video",           /* category key */
101       {
102          { "1x", NULL }, /* If value itself is human-readable (e.g. a number)  */
103          { "2x", NULL }, /* and can displayed directly, the value_label should */
104          { "3x", NULL }, /* be set to NULL                                     */
105          { "4x", NULL },
106          { NULL, NULL },
107       },
108       "3x"
109    },
110    {
111       "mycore_overclock",
112       "Advanced > Reduce Slowdown",
113       "Reduce Slowdown",
114       "Enabling 'Advanced > Reduce Slowdown' will reduce accuracy.", /* sublabel */
115       "Enabling 'Reduce Slowdown' will reduce accuracy.",            /* 'categorised' sublabel:
116                                * will be displayed inside the 'Advanced' submenu; note that
117                                * 'Advanced > Reduce Slowdown' is replaced with 'Reduce Slowdown' */
118       "hacks",
119       {
120          { "enabled",  NULL }, /* If value is equal to 'enabled' or 'disabled', */
121          { "disabled", NULL }, /* value_label should be set to NULL             */
122          { NULL, NULL },
123       },
124       "disabled"
125    },
126    { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL },
127 };
128
129 struct retro_core_options_v2 options_us = {
130    option_cats_us,
131    option_defs_us
132 };
133
134 /*
135  ********************************
136  * Language Mapping
137  ********************************
138 */
139
140 #ifndef HAVE_NO_LANGEXTRA
141 struct retro_core_options_v2 *options_intl[RETRO_LANGUAGE_LAST] = {
142    &options_us, /* RETRO_LANGUAGE_ENGLISH */
143    NULL,        /* RETRO_LANGUAGE_JAPANESE */
144    &options_fr, /* RETRO_LANGUAGE_FRENCH */
145    NULL,        /* RETRO_LANGUAGE_SPANISH */
146    NULL,        /* RETRO_LANGUAGE_GERMAN */
147    NULL,        /* RETRO_LANGUAGE_ITALIAN */
148    NULL,        /* RETRO_LANGUAGE_DUTCH */
149    NULL,        /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */
150    NULL,        /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */
151    NULL,        /* RETRO_LANGUAGE_RUSSIAN */
152    NULL,        /* RETRO_LANGUAGE_KOREAN */
153    NULL,        /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */
154    NULL,        /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */
155    NULL,        /* RETRO_LANGUAGE_ESPERANTO */
156    NULL,        /* RETRO_LANGUAGE_POLISH */
157    NULL,        /* RETRO_LANGUAGE_VIETNAMESE */
158    NULL,        /* RETRO_LANGUAGE_ARABIC */
159    NULL,        /* RETRO_LANGUAGE_GREEK */
160    NULL,        /* RETRO_LANGUAGE_TURKISH */
161    NULL,        /* RETRO_LANGUAGE_SLOVAK */
162    NULL,        /* RETRO_LANGUAGE_PERSIAN */
163    NULL,        /* RETRO_LANGUAGE_HEBREW */
164    NULL,        /* RETRO_LANGUAGE_ASTURIAN */
165    NULL,        /* RETRO_LANGUAGE_FINNISH */
166    NULL,        /* RETRO_LANGUAGE_INDONESIAN */
167    NULL,        /* RETRO_LANGUAGE_SWEDISH */
168    NULL,        /* RETRO_LANGUAGE_UKRAINIAN */
169    NULL,        /* RETRO_LANGUAGE_CZECH */
170 };
171 #endif
172
173 /*
174  ********************************
175  * Functions
176  ********************************
177 */
178
179 /* Handles configuration/setting of core options.
180  * Should be called as early as possible - ideally inside
181  * retro_set_environment(), and no later than retro_load_game()
182  * > We place the function body in the header to avoid the
183  *   necessity of adding more .c files (i.e. want this to
184  *   be as painless as possible for core devs)
185  */
186
187 static INLINE void libretro_set_core_options(retro_environment_t environ_cb,
188       bool *categories_supported)
189 {
190    unsigned version  = 0;
191 #ifndef HAVE_NO_LANGEXTRA
192    unsigned language = 0;
193 #endif
194
195    if (!environ_cb || !categories_supported)
196       return;
197
198    *categories_supported = false;
199
200    if (!environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version))
201       version = 0;
202
203    if (version >= 2)
204    {
205 #ifndef HAVE_NO_LANGEXTRA
206       struct retro_core_options_v2_intl core_options_intl;
207
208       core_options_intl.us    = &options_us;
209       core_options_intl.local = NULL;
210
211       if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) &&
212           (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH))
213          core_options_intl.local = options_intl[language];
214
215       *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL,
216             &core_options_intl);
217 #else
218       *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2,
219             &options_us);
220 #endif
221    }
222    else
223    {
224       size_t i, j;
225       size_t option_index              = 0;
226       size_t num_options               = 0;
227       struct retro_core_option_definition
228             *option_v1_defs_us         = NULL;
229 #ifndef HAVE_NO_LANGEXTRA
230       size_t num_options_intl          = 0;
231       struct retro_core_option_v2_definition
232             *option_defs_intl          = NULL;
233       struct retro_core_option_definition
234             *option_v1_defs_intl       = NULL;
235       struct retro_core_options_intl
236             core_options_v1_intl;
237 #endif
238       struct retro_variable *variables = NULL;
239       char **values_buf                = NULL;
240
241       /* Determine total number of options */
242       while (true)
243       {
244          if (option_defs_us[num_options].key)
245             num_options++;
246          else
247             break;
248       }
249
250       if (version >= 1)
251       {
252          /* Allocate US array */
253          option_v1_defs_us = (struct retro_core_option_definition *)
254                calloc(num_options + 1, sizeof(struct retro_core_option_definition));
255
256          /* Copy parameters from option_defs_us array */
257          for (i = 0; i < num_options; i++)
258          {
259             struct retro_core_option_v2_definition *option_def_us = &option_defs_us[i];
260             struct retro_core_option_value *option_values         = option_def_us->values;
261             struct retro_core_option_definition *option_v1_def_us = &option_v1_defs_us[i];
262             struct retro_core_option_value *option_v1_values      = option_v1_def_us->values;
263
264             option_v1_def_us->key           = option_def_us->key;
265             option_v1_def_us->desc          = option_def_us->desc;
266             option_v1_def_us->info          = option_def_us->info;
267             option_v1_def_us->default_value = option_def_us->default_value;
268
269             /* Values must be copied individually... */
270             while (option_values->value)
271             {
272                option_v1_values->value = option_values->value;
273                option_v1_values->label = option_values->label;
274
275                option_values++;
276                option_v1_values++;
277             }
278          }
279
280 #ifndef HAVE_NO_LANGEXTRA
281          if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) &&
282              (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH) &&
283              options_intl[language])
284             option_defs_intl = options_intl[language]->definitions;
285
286          if (option_defs_intl)
287          {
288             /* Determine number of intl options */
289             while (true)
290             {
291                if (option_defs_intl[num_options_intl].key)
292                   num_options_intl++;
293                else
294                   break;
295             }
296
297             /* Allocate intl array */
298             option_v1_defs_intl = (struct retro_core_option_definition *)
299                   calloc(num_options_intl + 1, sizeof(struct retro_core_option_definition));
300
301             /* Copy parameters from option_defs_intl array */
302             for (i = 0; i < num_options_intl; i++)
303             {
304                struct retro_core_option_v2_definition *option_def_intl = &option_defs_intl[i];
305                struct retro_core_option_value *option_values           = option_def_intl->values;
306                struct retro_core_option_definition *option_v1_def_intl = &option_v1_defs_intl[i];
307                struct retro_core_option_value *option_v1_values        = option_v1_def_intl->values;
308
309                option_v1_def_intl->key           = option_def_intl->key;
310                option_v1_def_intl->desc          = option_def_intl->desc;
311                option_v1_def_intl->info          = option_def_intl->info;
312                option_v1_def_intl->default_value = option_def_intl->default_value;
313
314                /* Values must be copied individually... */
315                while (option_values->value)
316                {
317                   option_v1_values->value = option_values->value;
318                   option_v1_values->label = option_values->label;
319
320                   option_values++;
321                   option_v1_values++;
322                }
323             }
324          }
325
326          core_options_v1_intl.us    = option_v1_defs_us;
327          core_options_v1_intl.local = option_v1_defs_intl;
328
329          environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_v1_intl);
330 #else
331          environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, option_v1_defs_us);
332 #endif
333       }
334       else
335       {
336          /* Allocate arrays */
337          variables  = (struct retro_variable *)calloc(num_options + 1,
338                sizeof(struct retro_variable));
339          values_buf = (char **)calloc(num_options, sizeof(char *));
340
341          if (!variables || !values_buf)
342             goto error;
343
344          /* Copy parameters from option_defs_us array */
345          for (i = 0; i < num_options; i++)
346          {
347             const char *key                        = option_defs_us[i].key;
348             const char *desc                       = option_defs_us[i].desc;
349             const char *default_value              = option_defs_us[i].default_value;
350             struct retro_core_option_value *values = option_defs_us[i].values;
351             size_t buf_len                         = 3;
352             size_t default_index                   = 0;
353
354             values_buf[i] = NULL;
355
356             if (desc)
357             {
358                size_t num_values = 0;
359
360                /* Determine number of values */
361                while (true)
362                {
363                   if (values[num_values].value)
364                   {
365                      /* Check if this is the default value */
366                      if (default_value)
367                         if (strcmp(values[num_values].value, default_value) == 0)
368                            default_index = num_values;
369
370                      buf_len += strlen(values[num_values].value);
371                      num_values++;
372                   }
373                   else
374                      break;
375                }
376
377                /* Build values string */
378                if (num_values > 0)
379                {
380                   buf_len += num_values - 1;
381                   buf_len += strlen(desc);
382
383                   values_buf[i] = (char *)calloc(buf_len, sizeof(char));
384                   if (!values_buf[i])
385                      goto error;
386
387                   strcpy(values_buf[i], desc);
388                   strcat(values_buf[i], "; ");
389
390                   /* Default value goes first */
391                   strcat(values_buf[i], values[default_index].value);
392
393                   /* Add remaining values */
394                   for (j = 0; j < num_values; j++)
395                   {
396                      if (j != default_index)
397                      {
398                         strcat(values_buf[i], "|");
399                         strcat(values_buf[i], values[j].value);
400                      }
401                   }
402                }
403             }
404
405             variables[option_index].key   = key;
406             variables[option_index].value = values_buf[i];
407             option_index++;
408          }
409
410          /* Set variables */
411          environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables);
412       }
413
414 error:
415       /* Clean up */
416
417       if (option_v1_defs_us)
418       {
419          free(option_v1_defs_us);
420          option_v1_defs_us = NULL;
421       }
422
423 #ifndef HAVE_NO_LANGEXTRA
424       if (option_v1_defs_intl)
425       {
426          free(option_v1_defs_intl);
427          option_v1_defs_intl = NULL;
428       }
429 #endif
430
431       if (values_buf)
432       {
433          for (i = 0; i < num_options; i++)
434          {
435             if (values_buf[i])
436             {
437                free(values_buf[i]);
438                values_buf[i] = NULL;
439             }
440          }
441
442          free(values_buf);
443          values_buf = NULL;
444       }
445
446       if (variables)
447       {
448          free(variables);
449          variables = NULL;
450       }
451    }
452 }
453
454 #ifdef __cplusplus
455 }
456 #endif
457
458 #endif