1 /* Copyright  (C) 2010-2017 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (c_converter.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <lrc_hash.h>
32 
33 #include <retro_assert.h>
34 #include <string/stdstring.h>
35 #include <streams/file_stream.h>
36 
37 #include "libretrodb.h"
38 
dat_converter_exit(int rc)39 static void dat_converter_exit(int rc)
40 {
41    fflush(stdout);
42    exit(rc);
43 }
44 
45 typedef enum
46 {
47    DAT_CONVERTER_STRING_MAP,
48    DAT_CONVERTER_LIST_MAP,
49 } dat_converter_map_enum;
50 
51 typedef enum
52 {
53    DAT_CONVERTER_TOKEN_LIST,
54    DAT_CONVERTER_STRING_LIST,
55    DAT_CONVERTER_MAP_LIST,
56    DAT_CONVERTER_LIST_LIST,
57 } dat_converter_list_enum;
58 
59 typedef struct
60 {
61    const char* label;
62    int line_no;
63    int column;
64    const char* fname;
65 } dat_converter_token_t;
66 
67 typedef struct dat_converter_map_t dat_converter_map_t;
68 typedef struct dat_converter_list_t dat_converter_list_t;
69 typedef union dat_converter_list_item_t dat_converter_list_item_t;
70 typedef struct dat_converter_search_tree_t dat_converter_search_tree_t;
71 typedef struct dat_converter_bt_node_t dat_converter_bt_node_t;
72 
73 struct dat_converter_map_t
74 {
75    const char* key;
76    uint32_t hash;
77    dat_converter_map_enum type;
78    union
79    {
80       const char* string;
81       dat_converter_list_t* list;
82    } value;
83 };
84 
85 struct dat_converter_list_t
86 {
87    dat_converter_list_enum type;
88    dat_converter_list_item_t* values;
89    dat_converter_bt_node_t* bt_root;
90    int count;
91    int capacity;
92 };
93 
94 union dat_converter_list_item_t
95 {
96    const char* string;
97    dat_converter_map_t map;
98    dat_converter_token_t token;
99    dat_converter_list_t* list;
100 };
101 
102 struct dat_converter_bt_node_t
103 {
104    int index;
105    uint32_t hash;
106    dat_converter_bt_node_t* right;
107    dat_converter_bt_node_t* left;
108 };
109 
dat_converter_list_create(dat_converter_list_enum type)110 static dat_converter_list_t* dat_converter_list_create(
111       dat_converter_list_enum type)
112 {
113    dat_converter_list_t* list = malloc(sizeof(*list));
114 
115    list->type                 = type;
116    list->count                = 0;
117    list->capacity             = (1 << 2);
118    list->bt_root              = NULL;
119    list->values               = (dat_converter_list_item_t*)malloc(
120          sizeof(*list->values) * list->capacity);
121 
122    return list;
123 }
124 
dat_converter_bt_node_free(dat_converter_bt_node_t * node)125 static void dat_converter_bt_node_free(dat_converter_bt_node_t* node)
126 {
127    if (!node)
128       return;
129 
130    dat_converter_bt_node_free(node->left);
131    dat_converter_bt_node_free(node->right);
132    free(node);
133 }
134 
dat_converter_list_free(dat_converter_list_t * list)135 static void dat_converter_list_free(dat_converter_list_t* list)
136 {
137    if (!list)
138       return;
139    switch (list->type)
140    {
141    case DAT_CONVERTER_LIST_LIST:
142       while (list->count--)
143          dat_converter_list_free(list->values[list->count].list);
144       break;
145    case DAT_CONVERTER_MAP_LIST:
146       while (list->count--)
147       {
148          if (list->values[list->count].map.type == DAT_CONVERTER_LIST_MAP)
149             dat_converter_list_free(list->values[list->count].map.value.list);
150       }
151       dat_converter_bt_node_free(list->bt_root);
152       break;
153    default:
154       break;
155    }
156 
157    free(list->values);
158    free(list);
159 }
160 static void dat_converter_list_append(dat_converter_list_t* dst, void* item);
161 
dat_converter_bt_node_insert(dat_converter_list_t * list,dat_converter_bt_node_t ** node,dat_converter_map_t * map)162 static dat_converter_bt_node_t* dat_converter_bt_node_insert(
163       dat_converter_list_t* list,
164       dat_converter_bt_node_t** node,
165       dat_converter_map_t* map)
166 {
167    retro_assert(map->key);
168    retro_assert(list->type == DAT_CONVERTER_MAP_LIST);
169 
170    if (!*node)
171    {
172       *node = calloc(1, sizeof(dat_converter_bt_node_t));
173       return *node;
174    }
175 
176    int diff = (*node)->hash - map->hash;
177 
178    if (!diff)
179       diff = strcmp(list->values[(*node)->index].map.key, map->key);
180 
181    if (diff < 0)
182       return dat_converter_bt_node_insert(list, &(*node)->left, map);
183    else if (diff > 0)
184       return dat_converter_bt_node_insert(list, &(*node)->right, map);
185 
186    /* found match */
187 
188    if (list->values[(*node)->index].map.type == DAT_CONVERTER_LIST_MAP)
189    {
190       if (map->type == DAT_CONVERTER_LIST_MAP)
191       {
192          int i;
193 
194          retro_assert(list->values[(*node)->index].map.value.list->type
195                == map->value.list->type);
196 
197          for (i = 0; i < map->value.list->count; i++)
198             dat_converter_list_append(
199                   list->values[(*node)->index].map.value.list,
200                   &map->value.list->values[i]);
201 
202          /* set count to 0 to prevent freeing the child nodes */
203          map->value.list->count = 0;
204          dat_converter_list_free(map->value.list);
205       }
206    }
207    else
208       list->values[(*node)->index].map = *map;
209 
210    return NULL;
211 }
212 
dat_converter_list_append(dat_converter_list_t * dst,void * item)213 static void dat_converter_list_append(dat_converter_list_t* dst, void* item)
214 {
215    if (dst->count == dst->capacity)
216    {
217       dst->capacity <<= 1;
218       dst->values = realloc(dst->values, sizeof(*dst->values) * dst->capacity);
219    }
220    switch (dst->type)
221    {
222    case DAT_CONVERTER_TOKEN_LIST:
223    {
224       dat_converter_token_t* token = (dat_converter_token_t*) item;
225       dst->values[dst->count].token = *token;
226       break;
227    }
228    case DAT_CONVERTER_STRING_LIST:
229    {
230       char* str = (char*) item;
231       dst->values[dst->count].string = str;
232       break;
233    }
234    case DAT_CONVERTER_MAP_LIST:
235    {
236       dat_converter_map_t* map = (dat_converter_map_t*) item;
237       if (!map->key)
238          dst->values[dst->count].map = *map;
239       else
240       {
241          map->hash = djb2_calculate(map->key);
242          dat_converter_bt_node_t* new_node =
243             dat_converter_bt_node_insert(dst, &dst->bt_root, map);
244 
245          if (!new_node)
246             return;
247 
248          dst->values[dst->count].map = *map;
249          new_node->index = dst->count;
250          new_node->hash = map->hash;
251       }
252       break;
253    }
254    case DAT_CONVERTER_LIST_LIST:
255    {
256       dat_converter_list_t* list = (dat_converter_list_t*) item;
257       dst->values[dst->count].list = list;
258       break;
259    }
260    default:
261       return;
262    }
263    dst->count++;
264 }
265 
dat_converter_lexer(char * src,const char * dat_path)266 static dat_converter_list_t* dat_converter_lexer(
267       char* src, const char* dat_path)
268 {
269    dat_converter_list_t* token_list =
270       dat_converter_list_create(DAT_CONVERTER_TOKEN_LIST);
271    dat_converter_token_t token      = {NULL, 1, 1, dat_path};
272    bool quoted_token                = false;
273 
274    while (*src)
275    {
276       if ((!quoted_token && (*src == '\t' || *src == ' ')) || (*src == '\r'))
277       {
278          *src = '\0';
279          src++;
280          token.column++;
281          token.label = NULL;
282          quoted_token = false;
283          continue;
284       }
285 
286       if (*src == '\n')
287       {
288          *src = '\0';
289          src++;
290          token.column = 1;
291          token.line_no++;
292          token.label = NULL;
293          quoted_token = false;
294          continue;
295       }
296 
297       if (*src == '\"')
298       {
299          *src = '\0';
300          src++;
301          token.column++;
302          quoted_token = !quoted_token;
303          token.label = NULL;
304 
305          if (quoted_token)
306          {
307             token.label = src;
308             dat_converter_list_append(token_list, &token);
309          }
310 
311          continue;
312       }
313 
314       if (!token.label)
315       {
316          token.label = src;
317          dat_converter_list_append(token_list, &token);
318       }
319 
320       src++;
321       token.column++;
322    }
323 
324    token.label = NULL;
325    dat_converter_list_append(token_list, &token);
326 
327    return token_list;
328 }
329 
dat_parser_table(dat_converter_list_item_t ** start_token)330 static dat_converter_list_t* dat_parser_table(
331       dat_converter_list_item_t** start_token)
332 {
333    dat_converter_list_t* parsed_table =
334       dat_converter_list_create(DAT_CONVERTER_MAP_LIST);
335    dat_converter_map_t map            = {0};
336    dat_converter_list_item_t* current = *start_token;
337 
338    while (current->token.label)
339    {
340 
341       if (!map.key)
342       {
343          if (string_is_equal(current->token.label, ")"))
344          {
345             current++;
346             *start_token = current;
347             return parsed_table;
348          }
349          else if (string_is_equal(current->token.label, "("))
350          {
351             printf("%s:%d:%d: fatal error: Unexpected '(' instead of key\n",
352                    current->token.fname,
353                    current->token.line_no,
354                    current->token.column);
355             dat_converter_exit(1);
356          }
357          else
358          {
359             map.key = current->token.label;
360             current++;
361          }
362       }
363       else
364       {
365          if (string_is_equal(current->token.label, "("))
366          {
367             current++;
368             map.type = DAT_CONVERTER_LIST_MAP;
369             map.value.list = dat_parser_table(&current);
370             dat_converter_list_append(parsed_table, &map);
371          }
372          else if (string_is_equal(current->token.label, ")"))
373          {
374             printf("%s:%d:%d: fatal error: Unexpected ')' instead of value\n",
375                    current->token.fname,
376                    current->token.line_no,
377                    current->token.column);
378             dat_converter_exit(1);
379          }
380          else
381          {
382             map.type = DAT_CONVERTER_STRING_MAP;
383             map.value.string = current->token.label;
384             dat_converter_list_append(parsed_table, &map);
385             current++;
386          }
387          map.key = NULL;
388       }
389    }
390 
391    printf("%s:%d:%d: fatal error: Missing ')' for '('\n",
392           (*start_token)->token.fname,
393           (*start_token)->token.line_no,
394           (*start_token)->token.column);
395    dat_converter_exit(1);
396 
397    /* unreached */
398    dat_converter_list_free(parsed_table);
399    return NULL;
400 }
401 
402 typedef struct dat_converter_match_key_t dat_converter_match_key_t;
403 struct dat_converter_match_key_t
404 {
405    char* value;
406    uint32_t hash;
407    dat_converter_match_key_t* next;
408 };
409 
dat_converter_match_key_create(const char * format)410 static dat_converter_match_key_t* dat_converter_match_key_create(
411       const char* format)
412 {
413    dat_converter_match_key_t* match_key;
414    dat_converter_match_key_t* current_mk;
415    char* dot;
416 
417    match_key = malloc(sizeof(*match_key));
418    match_key->value = strdup(format);
419    match_key->next = NULL;
420    current_mk = match_key;
421 
422    dot = match_key->value;
423    while (*dot++)
424    {
425       if (*dot == '.')
426       {
427          *dot++ = '\0';
428          current_mk->hash = djb2_calculate(current_mk->value);
429          current_mk->next = malloc(sizeof(*match_key));
430          current_mk = current_mk->next;
431          current_mk->value = dot;
432          current_mk->next = NULL;
433       }
434    }
435    current_mk->hash = djb2_calculate(current_mk->value);
436 
437    return match_key;
438 }
439 
dat_converter_match_key_free(dat_converter_match_key_t * match_key)440 static void dat_converter_match_key_free(dat_converter_match_key_t* match_key)
441 {
442    if (!match_key)
443       return;
444 
445    free(match_key->value);
446 
447    while (match_key)
448    {
449       dat_converter_match_key_t* next = match_key->next;
450       free(match_key);
451       match_key = next;
452    }
453 }
454 
dat_converter_get_match(dat_converter_list_t * list,dat_converter_match_key_t * match_key)455 static const char* dat_converter_get_match(
456       dat_converter_list_t* list,
457       dat_converter_match_key_t* match_key)
458 {
459    int i;
460 
461    retro_assert(match_key);
462 
463    if (list->type != DAT_CONVERTER_MAP_LIST)
464       return NULL;
465 
466    for (i = 0; i < list->count; i++)
467    {
468       if (list->values[i].map.hash == match_key->hash)
469       {
470          retro_assert(string_is_equal(list->values[i].map.key, match_key->value));
471 
472          if (match_key->next)
473             return dat_converter_get_match(
474                   list->values[i].map.value.list, match_key->next);
475 
476          if ((list->values[i].map.type == DAT_CONVERTER_STRING_MAP))
477             return list->values[i].map.value.string;
478 
479          return NULL;
480 
481       }
482    }
483    return NULL;
484 }
485 
dat_converter_parser(dat_converter_list_t * target,dat_converter_list_t * lexer_list,dat_converter_match_key_t * match_key)486 static dat_converter_list_t* dat_converter_parser(
487       dat_converter_list_t* target,
488       dat_converter_list_t* lexer_list,
489       dat_converter_match_key_t* match_key)
490 {
491    dat_converter_map_t map;
492    dat_converter_list_item_t* current = lexer_list->values;
493    bool skip                          = true;
494    bool warning_displayed             = false;
495 
496    map.key                            = NULL;
497    map.type                           = DAT_CONVERTER_LIST_MAP;
498 
499    if (!target)
500    {
501       dat_converter_map_t map;
502       target = dat_converter_list_create(DAT_CONVERTER_MAP_LIST);
503       map.key = NULL;
504       map.type = DAT_CONVERTER_LIST_MAP;
505       map.value.list = NULL;
506       dat_converter_list_append(target, &map);
507    }
508 
509    while (current->token.label)
510    {
511       if (!map.key)
512       {
513          if (string_is_equal(current->token.label, "game"))
514             skip = false;
515          map.key = current->token.label;
516          current++;
517       }
518       else
519       {
520          if (string_is_equal(current->token.label, "("))
521          {
522             current++;
523             map.value.list = dat_parser_table(&current);
524             if (!skip)
525             {
526                if (match_key)
527                {
528                   map.key = dat_converter_get_match(map.value.list, match_key);
529                   // If the key is not found, report, and mark it to be skipped.
530                   if (!map.key)
531                   {
532                      if (warning_displayed == false)
533                      {
534                         printf("    - Missing match key '");
535                         while (match_key->next)
536                         {
537                            printf("%s.", match_key->value);
538                            match_key = match_key->next;
539                         }
540                         printf("%s' on line %d\n", match_key->value, current->token.line_no);
541                         warning_displayed = true;
542                      }
543                      skip = true;
544                   }
545                }
546                else
547                   map.key = NULL;
548 
549                // If we are still not to skip the entry, append it to the list.
550                if (!skip) {
551                   dat_converter_list_append(target, &map);
552                   skip = true;
553                }
554             }
555             else
556                dat_converter_list_free(map.value.list);
557 
558             map.key = NULL;
559          }
560          else
561          {
562             printf("%s:%d:%d: fatal error: Expected '(' found '%s'\n",
563                    current->token.fname,
564                    current->token.line_no,
565                    current->token.column,
566                    current->token.label);
567             dat_converter_exit(1);
568          }
569       }
570    }
571    return target;
572 }
573 
574 typedef enum
575 {
576    DAT_CONVERTER_RDB_TYPE_STRING,
577    DAT_CONVERTER_RDB_TYPE_UINT,
578    DAT_CONVERTER_RDB_TYPE_BINARY,
579    DAT_CONVERTER_RDB_TYPE_HEX
580 } dat_converter_rdb_format_enum;
581 
582 typedef struct
583 {
584    const char* dat_key;
585    const char* rdb_key;
586    dat_converter_rdb_format_enum format;
587 } dat_converter_rdb_mappings_t;
588 
589 dat_converter_rdb_mappings_t rdb_mappings[] =
590 {
591    {"name",           "name",           DAT_CONVERTER_RDB_TYPE_STRING},
592    {"description",    "description",    DAT_CONVERTER_RDB_TYPE_STRING},
593    {"genre",          "genre",          DAT_CONVERTER_RDB_TYPE_STRING},
594    {"rom.name",       "rom_name",       DAT_CONVERTER_RDB_TYPE_STRING},
595    {"rom.size",       "size",           DAT_CONVERTER_RDB_TYPE_UINT},
596    {"users",          "users",          DAT_CONVERTER_RDB_TYPE_UINT},
597    {"releasemonth",   "releasemonth",   DAT_CONVERTER_RDB_TYPE_UINT},
598    {"releaseyear",    "releaseyear",    DAT_CONVERTER_RDB_TYPE_UINT},
599    {"rumble",         "rumble",         DAT_CONVERTER_RDB_TYPE_UINT},
600    {"analog",         "analog",         DAT_CONVERTER_RDB_TYPE_UINT},
601 
602    {"famitsu_rating", "famitsu_rating", DAT_CONVERTER_RDB_TYPE_UINT},
603    {"edge_rating",    "edge_rating",    DAT_CONVERTER_RDB_TYPE_UINT},
604    {"edge_issue",     "edge_issue",     DAT_CONVERTER_RDB_TYPE_UINT},
605    {"edge_review",    "edge_review",    DAT_CONVERTER_RDB_TYPE_STRING},
606 
607    {"enhancement_hw", "enhancement_hw", DAT_CONVERTER_RDB_TYPE_STRING},
608    {"barcode",        "barcode",        DAT_CONVERTER_RDB_TYPE_STRING},
609    {"esrb_rating",    "esrb_rating",    DAT_CONVERTER_RDB_TYPE_STRING},
610    {"elspa_rating",   "elspa_rating",   DAT_CONVERTER_RDB_TYPE_STRING},
611    {"pegi_rating",    "pegi_rating",    DAT_CONVERTER_RDB_TYPE_STRING},
612    {"cero_rating",    "cero_rating",    DAT_CONVERTER_RDB_TYPE_STRING},
613    {"franchise",      "franchise",      DAT_CONVERTER_RDB_TYPE_STRING},
614 
615    {"developer",      "developer",      DAT_CONVERTER_RDB_TYPE_STRING},
616    {"publisher",      "publisher",      DAT_CONVERTER_RDB_TYPE_STRING},
617    {"origin",         "origin",         DAT_CONVERTER_RDB_TYPE_STRING},
618 
619    {"coop",           "coop",           DAT_CONVERTER_RDB_TYPE_UINT},
620    {"tgdb_rating",    "tgdb_rating",    DAT_CONVERTER_RDB_TYPE_UINT},
621 
622    {"rom.crc",        "crc",            DAT_CONVERTER_RDB_TYPE_HEX},
623    {"rom.md5",        "md5",            DAT_CONVERTER_RDB_TYPE_HEX},
624    {"rom.sha1",       "sha1",           DAT_CONVERTER_RDB_TYPE_HEX},
625    {"serial",         "serial",         DAT_CONVERTER_RDB_TYPE_BINARY},
626    {"rom.serial",     "serial",         DAT_CONVERTER_RDB_TYPE_BINARY}
627 };
628 
629 dat_converter_match_key_t* rdb_mappings_mk[(sizeof(rdb_mappings)
630       / sizeof(*rdb_mappings))] = {0};
631 
dat_converter_value_provider_init(void)632 static void dat_converter_value_provider_init(void)
633 {
634    int i;
635    for (i = 0; i < (sizeof(rdb_mappings) / sizeof(*rdb_mappings)); i++)
636       rdb_mappings_mk[i] = dat_converter_match_key_create(rdb_mappings[i].dat_key);
637 }
dat_converter_value_provider_free(void)638 static void dat_converter_value_provider_free(void)
639 {
640    int i;
641    for (i = 0; i < (sizeof(rdb_mappings) / sizeof(*rdb_mappings)); i++)
642    {
643       dat_converter_match_key_free(rdb_mappings_mk[i]);
644       rdb_mappings_mk[i] = NULL;
645    }
646 }
dat_converter_value_provider(dat_converter_list_item_t ** current_item,struct rmsgpack_dom_value * out)647 static int dat_converter_value_provider(
648       dat_converter_list_item_t** current_item, struct rmsgpack_dom_value* out)
649 {
650    int i;
651    struct rmsgpack_dom_pair* current = NULL;
652 
653    out->type          = RDT_MAP;
654    out->val.map.len   = 0;
655    out->val.map.items = calloc((sizeof(rdb_mappings) / sizeof(*rdb_mappings)),
656          sizeof(struct rmsgpack_dom_pair));
657 
658    (*current_item)--;
659 
660    retro_assert((*current_item)->map.type == DAT_CONVERTER_LIST_MAP);
661 
662    dat_converter_list_t* list = (*current_item)->map.value.list;
663 
664    if (!list)
665       return 1;
666 
667    retro_assert(list->type == DAT_CONVERTER_MAP_LIST);
668 
669    current = out->val.map.items;
670 
671    for (i = 0; i < (sizeof(rdb_mappings) / sizeof(*rdb_mappings)); i++)
672    {
673       const char* value = dat_converter_get_match(list, rdb_mappings_mk[i]);
674       if (!value)
675          continue;
676 
677       current->key.type = RDT_STRING;
678       current->key.val.string.len = strlen(rdb_mappings[i].rdb_key);
679       current->key.val.string.buff = strdup(rdb_mappings[i].rdb_key);
680 
681       switch (rdb_mappings[i].format)
682       {
683       case DAT_CONVERTER_RDB_TYPE_STRING:
684          current->value.type = RDT_STRING;
685          current->value.val.string.len = strlen(value);
686          current->value.val.string.buff = strdup(value);
687          break;
688       case DAT_CONVERTER_RDB_TYPE_UINT:
689          current->value.type = RDT_UINT;
690          if (value[strlen(value) - 1] == '\?')
691          {
692             free(current->key.val.string.buff);
693             continue;
694          }
695          current->value.val.uint_ = (uint64_t)atoll(value);
696          break;
697       case DAT_CONVERTER_RDB_TYPE_BINARY:
698          current->value.type = RDT_BINARY;
699          current->value.val.binary.len = strlen(value);
700          current->value.val.binary.buff = strdup(value);
701          break;
702       case DAT_CONVERTER_RDB_TYPE_HEX:
703          current->value.type = RDT_BINARY;
704          current->value.val.binary.len  = strlen(value) / 2;
705          current->value.val.binary.buff =
706             malloc(current->value.val.binary.len);
707          {
708             const char* hex_char = value;
709             char* out_buff       = current->value.val.binary.buff;
710             while (*hex_char && *(hex_char + 1))
711             {
712                char val = 0;
713                if (*hex_char >= 'A' && *hex_char <= 'F')
714                   val = *hex_char + 0xA - 'A';
715                else if (*hex_char >= 'a' && *hex_char <= 'f')
716                   val = *hex_char + 0xA - 'a';
717                else if (*hex_char >= '0' && *hex_char <= '9')
718                   val = *hex_char - '0';
719                else
720                   val = 0;
721                val <<= 4;
722                hex_char++;
723                if (*hex_char >= 'A' && *hex_char <= 'F')
724                   val |= *hex_char + 0xA - 'A';
725                else if (*hex_char >= 'a' && *hex_char <= 'f')
726                   val |= *hex_char + 0xA - 'a';
727                else if (*hex_char >= '0' && *hex_char <= '9')
728                   val |= *hex_char - '0';
729 
730                *out_buff++ = val;
731                hex_char++;
732             }
733          }
734          break;
735       default:
736          retro_assert(0);
737          break;
738       }
739       current++;
740    }
741 
742    out->val.map.len = current - out->val.map.items;
743    return 0;
744 }
745 
main(int argc,char ** argv)746 int main(int argc, char** argv)
747 {
748    const char* rdb_path;
749    dat_converter_match_key_t* match_key = NULL;
750    RFILE* rdb_file;
751 
752    if (argc < 2)
753    {
754       printf("usage:\n%s <db file> [args ...]\n", *argv);
755       dat_converter_exit(1);
756    }
757    argc--;
758    argv++;
759 
760    rdb_path  = *argv;
761    argc--;
762    argv++;
763 
764    if (argc > 1 &&** argv)
765    {
766       match_key = dat_converter_match_key_create(*argv);
767       argc--;
768       argv++;
769    }
770 
771    int dat_count                         = argc;
772    char** dat_buffers                    = (char**)
773       malloc(dat_count * sizeof(*dat_buffers));
774    char** dat_buffer                     = dat_buffers;
775    dat_converter_list_t* dat_parser_list = NULL;
776 
777    while (argc)
778    {
779       size_t dat_file_size;
780       dat_converter_list_t* dat_lexer_list = NULL;
781       FILE* dat_file                       = fopen(*argv, "r");
782 
783       if (!dat_file)
784       {
785          printf("  could not open dat file '%s': %s\n",
786                *argv, strerror(errno));
787          dat_converter_exit(1);
788       }
789 
790       fseek(dat_file, 0, SEEK_END);
791       dat_file_size = ftell(dat_file);
792       fseek(dat_file, 0, SEEK_SET);
793       *dat_buffer = (char*)malloc(dat_file_size + 1);
794       fread(*dat_buffer, 1, dat_file_size, dat_file);
795       fclose(dat_file);
796       (*dat_buffer)[dat_file_size] = '\0';
797 
798       printf("  %s\n", *argv);
799       dat_lexer_list  = dat_converter_lexer(*dat_buffer, *argv);
800       dat_parser_list = dat_converter_parser(
801             dat_parser_list, dat_lexer_list, match_key);
802 
803       dat_converter_list_free(dat_lexer_list);
804 
805       argc--;
806       argv++;
807       dat_buffer++;
808    }
809 
810    rdb_file = filestream_open(rdb_path,
811          RETRO_VFS_FILE_ACCESS_WRITE,
812          RETRO_VFS_FILE_ACCESS_HINT_NONE);
813 
814    if (!rdb_file)
815    {
816       printf(
817          "Could not open destination file '%s': %s\n",
818          rdb_path,
819          strerror(errno)
820       );
821       dat_converter_exit(1);
822    }
823 
824    dat_converter_list_item_t* current_item =
825       &dat_parser_list->values[dat_parser_list->count];
826 
827    dat_converter_value_provider_init();
828    libretrodb_create(rdb_file,
829          (libretrodb_value_provider)&dat_converter_value_provider,
830          &current_item);
831    dat_converter_value_provider_free();
832 
833    filestream_close(rdb_file);
834 
835    dat_converter_list_free(dat_parser_list);
836 
837    while (dat_count--)
838       free(dat_buffers[dat_count]);
839    free(dat_buffers);
840 
841    dat_converter_match_key_free(match_key);
842 
843    return 0;
844 }
845