lightrec: implement clock cache clear on cycle_multiplier change
[pcsx_rearmed.git] / deps / libretro-common / samples / core_options / example_categories / conversion_scripts / v1_to_v2_converter.py
1 #!/usr/bin/env python3
2
3 """Core options v1 to v2 converter
4
5 Just copy 'libretro_core_options.h' & 'libretro_core_options_intl.h' into the same folder as this script
6 and run it! The original files will be preserved as *.v1
7 """
8 import core_option_regex as cor
9 import os
10 import sys
11
12 if os.name == 'nt':
13     joiner = '\\'
14 else:
15     joiner = '/'
16 dir_path = os.path.dirname(os.path.realpath(__file__))
17 h_filename = joiner.join((dir_path, 'libretro_core_options.h'))
18 intl_filename = joiner.join((dir_path, 'libretro_core_options_intl.h'))
19
20
21 def create_v2_code_file(struct_text, file_name):
22     def replace_option(option_match):
23         _offset = option_match.start(0)
24
25         if option_match.group(3):
26             res = option_match.group(0)[:option_match.end(2) - _offset] + ',\n      NULL' + \
27                   option_match.group(0)[option_match.end(2) - _offset:option_match.end(3) - _offset] + \
28                   'NULL,\n      NULL,\n      ' + option_match.group(0)[option_match.end(3) - _offset:]
29         else:
30             return option_match.group(0)
31
32         return res
33
34     comment_v1 = '/*\n' \
35                  ' ********************************\n' \
36                  ' * VERSION: 1.3\n' \
37                  ' ********************************\n' \
38                  ' *\n' \
39                  ' * - 1.3: Move translations to libretro_core_options_intl.h\n' \
40                  ' *        - libretro_core_options_intl.h includes BOM and utf-8\n' \
41                  ' *          fix for MSVC 2010-2013\n' \
42                  ' *        - Added HAVE_NO_LANGEXTRA flag to disable translations\n' \
43                  ' *          on platforms/compilers without BOM support\n' \
44                  ' * - 1.2: Use core options v1 interface when\n' \
45                  ' *        RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1\n' \
46                  ' *        (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)\n' \
47                  ' * - 1.1: Support generation of core options v0 retro_core_option_value\n' \
48                  ' *        arrays containing options with a single value\n' \
49                  ' * - 1.0: First commit\n' \
50                  '*/\n'
51
52     comment_v2 = '/*\n' \
53                  ' ********************************\n' \
54                  ' * VERSION: 2.0\n' \
55                  ' ********************************\n' \
56                  ' *\n' \
57                  ' * - 2.0: Add support for core options v2 interface\n' \
58                  ' * - 1.3: Move translations to libretro_core_options_intl.h\n' \
59                  ' *        - libretro_core_options_intl.h includes BOM and utf-8\n' \
60                  ' *          fix for MSVC 2010-2013\n' \
61                  ' *        - Added HAVE_NO_LANGEXTRA flag to disable translations\n' \
62                  ' *          on platforms/compilers without BOM support\n' \
63                  ' * - 1.2: Use core options v1 interface when\n' \
64                  ' *        RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1\n' \
65                  ' *        (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)\n' \
66                  ' * - 1.1: Support generation of core options v0 retro_core_option_value\n' \
67                  ' *        arrays containing options with a single value\n' \
68                  ' * - 1.0: First commit\n' \
69                  '*/\n'
70
71     p_intl = cor.re.compile(r'(struct retro_core_option_definition \*option_defs_intl\[RETRO_LANGUAGE_LAST]) = {'
72                             r'((?:.|[\r\n])*?)};')
73     p_set = cor.re.compile(r'static INLINE void libretro_set_core_options\(retro_environment_t environ_cb\)'
74                            r'(?:.|[\r\n])*?};?\s*#ifdef __cplusplus\s*}\s*#endif')
75     new_set = 'static INLINE void libretro_set_core_options(retro_environment_t environ_cb,\n' \
76               '      bool *categories_supported)\n' \
77               '{\n' \
78               '   unsigned version  = 0;\n' \
79               '#ifndef HAVE_NO_LANGEXTRA\n' \
80               '   unsigned language = 0;\n' \
81               '#endif\n' \
82               '\n' \
83               '   if (!environ_cb || !categories_supported)\n' \
84               '      return;\n' \
85               '\n' \
86               '   *categories_supported = false;\n' \
87               '\n' \
88               '   if (!environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version))\n' \
89               '      version = 0;\n' \
90               '\n' \
91               '   if (version >= 2)\n' \
92               '   {\n' \
93               '#ifndef HAVE_NO_LANGEXTRA\n' \
94               '      struct retro_core_options_v2_intl core_options_intl;\n' \
95               '\n' \
96               '      core_options_intl.us    = &options_us;\n' \
97               '      core_options_intl.local = NULL;\n' \
98               '\n' \
99               '      if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) &&\n' \
100               '          (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH))\n' \
101               '         core_options_intl.local = options_intl[language];\n' \
102               '\n' \
103               '      *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL,\n' \
104               '            &core_options_intl);\n' \
105               '#else\n' \
106               '      *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2,\n' \
107               '            &options_us);\n' \
108               '#endif\n' \
109               '   }\n' \
110               '   else\n' \
111               '   {\n' \
112               '      size_t i, j;\n' \
113               '      size_t option_index              = 0;\n' \
114               '      size_t num_options               = 0;\n' \
115               '      struct retro_core_option_definition\n' \
116               '            *option_v1_defs_us         = NULL;\n' \
117               '#ifndef HAVE_NO_LANGEXTRA\n' \
118               '      size_t num_options_intl          = 0;\n' \
119               '      struct retro_core_option_v2_definition\n' \
120               '            *option_defs_intl          = NULL;\n' \
121               '      struct retro_core_option_definition\n' \
122               '            *option_v1_defs_intl       = NULL;\n' \
123               '      struct retro_core_options_intl\n' \
124               '            core_options_v1_intl;\n' \
125               '#endif\n' \
126               '      struct retro_variable *variables = NULL;\n' \
127               '      char **values_buf                = NULL;\n' \
128               '\n' \
129               '      /* Determine total number of options */\n' \
130               '      while (true)\n' \
131               '      {\n' \
132               '         if (option_defs_us[num_options].key)\n' \
133               '            num_options++;\n' \
134               '         else\n' \
135               '            break;\n' \
136               '      }\n' \
137               '\n' \
138               '      if (version >= 1)\n' \
139               '      {\n' \
140               '         /* Allocate US array */\n' \
141               '         option_v1_defs_us = (struct retro_core_option_definition *)\n' \
142               '               calloc(num_options + 1, sizeof(struct retro_core_option_definition));\n' \
143               '\n' \
144               '         /* Copy parameters from option_defs_us array */\n' \
145               '         for (i = 0; i < num_options; i++)\n' \
146               '         {\n' \
147               '            struct retro_core_option_v2_definition *option_def_us = &option_defs_us[i];\n' \
148               '            struct retro_core_option_value *option_values         = option_def_us->values;\n' \
149               '            struct retro_core_option_definition *option_v1_def_us = &option_v1_defs_us[i];\n' \
150               '            struct retro_core_option_value *option_v1_values      = option_v1_def_us->values;\n' \
151               '\n' \
152               '            option_v1_def_us->key           = option_def_us->key;\n' \
153               '            option_v1_def_us->desc          = option_def_us->desc;\n' \
154               '            option_v1_def_us->info          = option_def_us->info;\n' \
155               '            option_v1_def_us->default_value = option_def_us->default_value;\n' \
156               '\n' \
157               '            /* Values must be copied individually... */\n' \
158               '            while (option_values->value)\n' \
159               '            {\n' \
160               '               option_v1_values->value = option_values->value;\n' \
161               '               option_v1_values->label = option_values->label;\n' \
162               '\n' \
163               '               option_values++;\n' \
164               '               option_v1_values++;\n' \
165               '            }\n' \
166               '         }\n' \
167               '\n' \
168               '#ifndef HAVE_NO_LANGEXTRA\n' \
169               '         if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) &&\n' \
170               '             (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH) &&\n' \
171               '             options_intl[language])\n' \
172               '            option_defs_intl = options_intl[language]->definitions;\n' \
173               '\n' \
174               '         if (option_defs_intl)\n' \
175               '         {\n' \
176               '            /* Determine number of intl options */\n' \
177               '            while (true)\n' \
178               '            {\n' \
179               '               if (option_defs_intl[num_options_intl].key)\n' \
180               '                  num_options_intl++;\n' \
181               '               else\n' \
182               '                  break;\n' \
183               '            }\n' \
184               '\n' \
185               '            /* Allocate intl array */\n' \
186               '            option_v1_defs_intl = (struct retro_core_option_definition *)\n' \
187               '                  calloc(num_options_intl + 1, sizeof(struct retro_core_option_definition));\n' \
188               '\n' \
189               '            /* Copy parameters from option_defs_intl array */\n' \
190               '            for (i = 0; i < num_options_intl; i++)\n' \
191               '            {\n' \
192               '               struct retro_core_option_v2_definition *option_def_intl = &option_defs_intl[i];\n' \
193               '               struct retro_core_option_value *option_values           = option_def_intl->values;\n' \
194               '               struct retro_core_option_definition *option_v1_def_intl = &option_v1_defs_intl[i];\n' \
195               '               struct retro_core_option_value *option_v1_values        = option_v1_def_intl->values;\n' \
196               '\n' \
197               '               option_v1_def_intl->key           = option_def_intl->key;\n' \
198               '               option_v1_def_intl->desc          = option_def_intl->desc;\n' \
199               '               option_v1_def_intl->info          = option_def_intl->info;\n' \
200               '               option_v1_def_intl->default_value = option_def_intl->default_value;\n' \
201               '\n' \
202               '               /* Values must be copied individually... */\n' \
203               '               while (option_values->value)\n' \
204               '               {\n' \
205               '                  option_v1_values->value = option_values->value;\n' \
206               '                  option_v1_values->label = option_values->label;\n' \
207               '\n' \
208               '                  option_values++;\n' \
209               '                  option_v1_values++;\n' \
210               '               }\n' \
211               '            }\n' \
212               '         }\n' \
213               '\n' \
214               '         core_options_v1_intl.us    = option_v1_defs_us;\n' \
215               '         core_options_v1_intl.local = option_v1_defs_intl;\n' \
216               '\n' \
217               '         environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_v1_intl);\n' \
218               '#else\n' \
219               '         environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, option_v1_defs_us);\n' \
220               '#endif\n' \
221               '      }\n' \
222               '      else\n' \
223               '      {\n' \
224               '         /* Allocate arrays */\n' \
225               '         variables  = (struct retro_variable *)calloc(num_options + 1,\n' \
226               '               sizeof(struct retro_variable));\n' \
227               '         values_buf = (char **)calloc(num_options, sizeof(char *));\n' \
228               '\n' \
229               '         if (!variables || !values_buf)\n' \
230               '            goto error;\n' \
231               '\n' \
232               '         /* Copy parameters from option_defs_us array */\n' \
233               '         for (i = 0; i < num_options; i++)\n' \
234               '         {\n' \
235               '            const char *key                        = option_defs_us[i].key;\n' \
236               '            const char *desc                       = option_defs_us[i].desc;\n' \
237               '            const char *default_value              = option_defs_us[i].default_value;\n' \
238               '            struct retro_core_option_value *values = option_defs_us[i].values;\n' \
239               '            size_t buf_len                         = 3;\n' \
240               '            size_t default_index                   = 0;\n' \
241               '\n' \
242               '            values_buf[i] = NULL;\n' \
243               '\n' \
244               '            if (desc)\n' \
245               '            {\n' \
246               '               size_t num_values = 0;\n' \
247               '\n' \
248               '               /* Determine number of values */\n' \
249               '               while (true)\n' \
250               '               {\n' \
251               '                  if (values[num_values].value)\n' \
252               '                  {\n' \
253               '                     /* Check if this is the default value */\n' \
254               '                     if (default_value)\n' \
255               '                        if (strcmp(values[num_values].value, default_value) == 0)\n' \
256               '                           default_index = num_values;\n' \
257               '\n' \
258               '                     buf_len += strlen(values[num_values].value);\n' \
259               '                     num_values++;\n' \
260               '                  }\n' \
261               '                  else\n' \
262               '                     break;\n' \
263               '               }\n' \
264               '\n' \
265               '               /* Build values string */\n' \
266               '               if (num_values > 0)\n' \
267               '               {\n' \
268               '                  buf_len += num_values - 1;\n' \
269               '                  buf_len += strlen(desc);\n' \
270               '\n' \
271               '                  values_buf[i] = (char *)calloc(buf_len, sizeof(char));\n' \
272               '                  if (!values_buf[i])\n' \
273               '                     goto error;\n' \
274               '\n' \
275               '                  strcpy(values_buf[i], desc);\n' \
276               '                  strcat(values_buf[i], "; ");\n' \
277               '\n' \
278               '                  /* Default value goes first */\n' \
279               '                  strcat(values_buf[i], values[default_index].value);\n' \
280               '\n' \
281               '                  /* Add remaining values */\n' \
282               '                  for (j = 0; j < num_values; j++)\n' \
283               '                  {\n' \
284               '                     if (j != default_index)\n' \
285               '                     {\n' \
286               '                        strcat(values_buf[i], "|");\n' \
287               '                        strcat(values_buf[i], values[j].value);\n' \
288               '                     }\n' \
289               '                  }\n' \
290               '               }\n' \
291               '            }\n' \
292               '\n' \
293               '            variables[option_index].key   = key;\n' \
294               '            variables[option_index].value = values_buf[i];\n' \
295               '            option_index++;\n' \
296               '         }\n' \
297               '\n' \
298               '         /* Set variables */\n' \
299               '         environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables);\n' \
300               '      }\n' \
301               '\n' \
302               'error:\n' \
303               '      /* Clean up */\n' \
304               '\n' \
305               '      if (option_v1_defs_us)\n' \
306               '      {\n' \
307               '         free(option_v1_defs_us);\n' \
308               '         option_v1_defs_us = NULL;\n' \
309               '      }\n' \
310               '\n' \
311               '#ifndef HAVE_NO_LANGEXTRA\n' \
312               '      if (option_v1_defs_intl)\n' \
313               '      {\n' \
314               '         free(option_v1_defs_intl);\n' \
315               '         option_v1_defs_intl = NULL;\n' \
316               '      }\n' \
317               '#endif\n' \
318               '\n' \
319               '      if (values_buf)\n' \
320               '      {\n' \
321               '         for (i = 0; i < num_options; i++)\n' \
322               '         {\n' \
323               '            if (values_buf[i])\n' \
324               '            {\n' \
325               '               free(values_buf[i]);\n' \
326               '               values_buf[i] = NULL;\n' \
327               '            }\n' \
328               '         }\n' \
329               '\n' \
330               '         free(values_buf);\n' \
331               '         values_buf = NULL;\n' \
332               '      }\n' \
333               '\n' \
334               '      if (variables)\n' \
335               '      {\n' \
336               '         free(variables);\n' \
337               '         variables = NULL;\n' \
338               '      }\n' \
339               '   }\n' \
340               '}\n' \
341               '\n' \
342               '#ifdef __cplusplus\n' \
343               '}\n' \
344               '#endif'
345
346     struct_groups = cor.p_struct.finditer(struct_text)
347     out_text = struct_text
348
349     for construct in struct_groups:
350         repl_text = ''
351         declaration = construct.group(1)
352         struct_match = cor.p_type_name.search(declaration)
353         if struct_match:
354             struct_type_name = struct_match.group(1, 2)
355         else:
356             return -1
357
358         if 'retro_core_option_definition' == struct_type_name[0]:
359             import shutil
360             shutil.copy(file_name, file_name + '.v1')
361             new_declaration = f'\nstruct retro_core_option_v2_category option_cats_{struct_match.group(3)}[] = ' \
362                               '{\n   { NULL, NULL, NULL },\n' \
363                               '};\n\n' \
364                               + declaration[:struct_match.start(1)] + \
365                               'retro_core_option_v2_definition' \
366                               + declaration[struct_match.end(1):]
367             offset = construct.start(0)
368             repl_text = repl_text + cor.re.sub(cor.re.escape(declaration), new_declaration,
369                                                construct.group(0)[:construct.start(2) - offset])
370             content = construct.group(2)
371             new_content = cor.p_option.sub(replace_option, content)
372
373             repl_text = repl_text + new_content + cor.re.sub(r'{\s*NULL,\s*NULL,\s*NULL,\s*{\{0}},\s*NULL\s*},\s*};',
374                                                              '{ NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL },\n};'
375                                                              '\n\nstruct retro_core_options_v2 options_' +
376                                                              struct_match.group(3) + ' = {\n'
377                                                              f'   option_cats_{struct_match.group(3)},\n'
378                                                              f'   option_defs_{struct_match.group(3)}\n'
379                                                              '};',
380                                                              construct.group(0)[construct.end(2) - offset:])
381             out_text = cor.re.sub(cor.re.escape(construct.group(0)), repl_text, out_text)
382         else:
383             return -2
384     with open(file_name, 'w', encoding='utf-8') as code_file:
385         out_text = cor.re.sub(cor.re.escape(comment_v1), comment_v2, out_text)
386         intl = p_intl.search(out_text)
387         if intl:
388             new_intl = out_text[:intl.start(1)] \
389                        + 'struct retro_core_options_v2 *options_intl[RETRO_LANGUAGE_LAST]' \
390                        + out_text[intl.end(1):intl.start(2)] + cor.re.sub(r'option_defs_', '&options_', intl.group(2)) \
391                        + out_text[intl.end(2):]
392             out_text = p_set.sub(new_set, new_intl)
393         else:
394             out_text = p_set.sub(new_set, out_text)
395         code_file.write(out_text)
396
397     return 1
398
399
400 # --------------------          MAIN          -------------------- #
401
402 if __name__ == '__main__':
403     try:
404         for file in (h_filename, intl_filename):
405             if os.path.isfile(file):
406                 with open(file, 'r+', encoding='utf-8') as h_file:
407                     text = h_file.read()
408                     test = create_v2_code_file(text, file)
409                     if -1 > test:
410                         print('Your file looks like it already is v2? (' + file + ')')
411                         continue
412                     if 0 > test:
413                         print('An error occured! Please make sure to use the complete v1 struct! (' + file + ')')
414                         continue
415             else:
416                 print(file + ' not found.')
417     except EnvironmentError:
418         print('Something went wrong with reading or writing files!')
419         sys.exit(1)