Commit | Line | Data |
---|---|---|
3719602c PC |
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 |