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(¤t);
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(¤t);
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 ¤t_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