void rmsgpack_dom_value_free(struct rmsgpack_dom_value *v) { unsigned i; switch (v->type) { case RDT_STRING: free(v->val.string.buff); break; case RDT_BINARY: free(v->val.binary.buff); break; case RDT_MAP: for (i = 0; i < v->val.map.len; i++) { rmsgpack_dom_value_free(&v->val.map.items[i].key); rmsgpack_dom_value_free(&v->val.map.items[i].value); } free(v->val.map.items); break; case RDT_ARRAY: for (i = 0; i < v->val.array.len; i++) rmsgpack_dom_value_free(&v->val.array.items[i]); free(v->val.array.items); break; case RDT_NULL: case RDT_INT: case RDT_BOOL: case RDT_UINT: /* Do nothing */ break; } }
int libretrodb_cursor_read_item(libretrodb_cursor_t *cursor, struct rmsgpack_dom_value *out) { int rv; if (cursor->eof) return EOF; retry: rv = rmsgpack_dom_read(cursor->fd, out); if (rv < 0) return rv; if (out->type == RDT_NULL) { cursor->eof = 1; return EOF; } if (cursor->query) { if (!libretrodb_query_filter(cursor->query, out)) { rmsgpack_dom_value_free(out); goto retry; } } return 0; }
int libretrodb_create(RFILE *fd, libretrodb_value_provider value_provider, void *ctx) { int rv; libretrodb_metadata_t md; struct rmsgpack_dom_value item; uint64_t item_count = 0; libretrodb_header_t header = {{0}}; ssize_t root = filestream_seek(fd, 0, SEEK_CUR); memcpy(header.magic_number, MAGIC_NUMBER, sizeof(MAGIC_NUMBER)-1); /* We write the header in the end because we need to know the size of * the db first */ filestream_seek(fd, sizeof(libretrodb_header_t), SEEK_CUR); item.type = RDT_NULL; while ((rv = value_provider(ctx, &item)) == 0) { if ((rv = libretrodb_validate_document(&item)) < 0) goto clean; if ((rv = rmsgpack_dom_write(fd, &item)) < 0) goto clean; rmsgpack_dom_value_free(&item); item.type = RDT_NULL; item_count++; } if (rv < 0) goto clean; if ((rv = rmsgpack_dom_write(fd, &sentinal)) < 0) goto clean; header.metadata_offset = swap_if_little64(filestream_seek(fd, 0, SEEK_CUR)); md.count = item_count; libretrodb_write_metadata(fd, &md); filestream_seek(fd, root, SEEK_SET); filestream_write(fd, &header, sizeof(header)); clean: rmsgpack_dom_value_free(&item); return rv; }
static void argument_free(struct argument *arg) { unsigned i; if (arg->type != AT_FUNCTION) { rmsgpack_dom_value_free(&arg->a.value); return; } for (i = 0; i < arg->a.invocation.argc; i++) argument_free(&arg->a.invocation.argv[i]); }
int rmsgpack_dom_read(FILE *fp, struct rmsgpack_dom_value *out) { struct dom_reader_state s = {0}; int rv = 0; s.stack[0] = out; rv = rmsgpack_read(fp, &dom_reader_callbacks, &s); if (rv < 0) rmsgpack_dom_value_free(out); return rv; }
int libretrodb_create(int fd, libretrodb_value_provider value_provider, void * ctx) { int rv; off_t root; libretrodb_metadata_t md; uint64_t item_count = 0; struct rmsgpack_dom_value item = {}; libretrodb_header_t header = {}; memcpy(header.magic_number, MAGIC_NUMBER, sizeof(MAGIC_NUMBER)-1); root = lseek(fd, 0, SEEK_CUR); /* We write the header in the end because we need to know the size of * the db first */ lseek(fd, sizeof(libretrodb_header_t), SEEK_CUR); while ((rv = value_provider(ctx, &item)) == 0) { if ((rv = validate_document(&item)) < 0) goto clean; if ((rv = rmsgpack_dom_write(fd, &item)) < 0) goto clean; item_count++; } if (rv < 0) goto clean; if ((rv = rmsgpack_dom_write(fd, &sentinal)) < 0) goto clean; header.metadata_offset = httobe64(lseek(fd, 0, SEEK_CUR)); md.count = item_count; libretrodb_write_metadata(fd, &md); lseek(fd, root, SEEK_SET); write(fd, &header, sizeof(header)); clean: rmsgpack_dom_value_free(&item); return rv; }
int main(int argc, char ** argv) { int rv; libretrodb_t db; libretrodb_cursor_t cur; libretrodb_query_t *q; struct rmsgpack_dom_value item; const char *command, *path, *query_exp, *error; if (argc < 3) { printf("Usage: %s <db file> <command> [extra args...]\n", argv[0]); printf("Available Commands:\n"); printf("\tlist\n"); printf("\tcreate-index <index name> <field name>\n"); printf("\tfind <query expression>\n"); return 1; } command = argv[2]; path = argv[1]; if ((rv = libretrodb_open(path, &db)) != 0) { printf("Could not open db file '%s': %s\n", path, strerror(-rv)); return 1; } else if (strcmp(command, "list") == 0) { if ((rv = libretrodb_cursor_open(&db, &cur, NULL)) != 0) { printf("Could not open cursor: %s\n", strerror(-rv)); return 1; } if (argc != 3) { printf("Usage: %s <db file> list\n", argv[0]); return 1; } while (libretrodb_cursor_read_item(&cur, &item) == 0) { rmsgpack_dom_value_print(&item); printf("\n"); rmsgpack_dom_value_free(&item); } } else if (strcmp(command, "find") == 0) { if (argc != 4) { printf("Usage: %s <db file> find <query expression>\n", argv[0]); return 1; } query_exp = argv[3]; error = NULL; q = libretrodb_query_compile(&db, query_exp, strlen(query_exp), &error); if (error) { printf("%s\n", error); return 1; } if ((rv = libretrodb_cursor_open(&db, &cur, q)) != 0) { printf("Could not open cursor: %s\n", strerror(-rv)); return 1; } while (libretrodb_cursor_read_item(&cur, &item) == 0) { rmsgpack_dom_value_print(&item); printf("\n"); rmsgpack_dom_value_free(&item); } } else if (strcmp(command, "create-index") == 0) { const char * index_name, * field_name; if (argc != 5) { printf("Usage: %s <db file> create-index <index name> <field name>\n", argv[0]); return 1; } index_name = argv[3]; field_name = argv[4]; libretrodb_create_index(&db, index_name, field_name); } else { printf("Unknown command %s\n", argv[2]); return 1; } libretrodb_close(&db); return 1; }
int rmsgpack_dom_read_into(RFILE *fd, ...) { va_list ap; struct rmsgpack_dom_value map; int rv; const char *key_name; struct rmsgpack_dom_value key; struct rmsgpack_dom_value *value; int64_t *int_value; uint64_t *uint_value; int *bool_value; char *buff_value; uint64_t min_len; int value_type = 0; va_start(ap, fd); rv = rmsgpack_dom_read(fd, &map); (void)value_type; if (rv < 0) { va_end(ap); return rv; } if (map.type != RDT_MAP) goto clean; while (1) { key_name = va_arg(ap, const char *); if (!key_name) goto clean; key.type = RDT_STRING; key.val.string.len = (uint32_t)strlen(key_name); key.val.string.buff = (char *) key_name; value = rmsgpack_dom_value_map_value(&map, &key); switch (value->type) { case RDT_INT: int_value = va_arg(ap, int64_t *); *int_value = value->val.int_; break; case RDT_BOOL: bool_value = va_arg(ap, int *); *bool_value = value->val.bool_; break; case RDT_UINT: uint_value = va_arg(ap, uint64_t *); *uint_value = value->val.uint_; break; case RDT_BINARY: buff_value = va_arg(ap, char *); uint_value = va_arg(ap, uint64_t *); *uint_value = value->val.binary.len; min_len = (value->val.binary.len > *uint_value) ? *uint_value : value->val.binary.len; memcpy(buff_value, value->val.binary.buff, (size_t)min_len); break; case RDT_STRING: buff_value = va_arg(ap, char *); uint_value = va_arg(ap, uint64_t *); min_len = (value->val.string.len + 1 > *uint_value) ? *uint_value : value->val.string.len + 1; *uint_value = min_len; memcpy(buff_value, value->val.string.buff, (size_t)min_len); break; default: goto clean; } } clean: va_end(ap); rmsgpack_dom_value_free(&map); return 0; }
static int database_cursor_iterate(libretrodb_cursor_t *cur, database_info_t *db_info) { unsigned i; struct rmsgpack_dom_value item; const char* str = NULL; if (libretrodb_cursor_read_item(cur, &item) != 0) return -1; if (item.type != RDT_MAP) { rmsgpack_dom_value_free(&item); return 1; } db_info->analog_supported = -1; db_info->rumble_supported = -1; db_info->coop_supported = -1; for (i = 0; i < item.val.map.len; i++) { struct rmsgpack_dom_value *key = &item.val.map.items[i].key; struct rmsgpack_dom_value *val = &item.val.map.items[i].value; const char *val_string = NULL; if (!key || !val) continue; val_string = val->val.string.buff; str = key->val.string.buff; if (string_is_equal(str, "publisher")) { if (!string_is_empty(val_string)) db_info->publisher = strdup(val_string); } else if (string_is_equal(str, "developer")) { if (!string_is_empty(val_string)) db_info->developer = string_split(val_string, "|"); } else if (string_is_equal(str, "serial")) { if (!string_is_empty(val_string)) db_info->serial = strdup(val_string); } else if (string_is_equal(str, "rom_name")) { if (!string_is_empty(val_string)) db_info->rom_name = strdup(val_string); } else if (string_is_equal(str, "name")) { if (!string_is_empty(val_string)) db_info->name = strdup(val_string); } else if (string_is_equal(str, "description")) { if (!string_is_empty(val_string)) db_info->description = strdup(val_string); } else if (string_is_equal(str, "genre")) { if (!string_is_empty(val_string)) db_info->genre = strdup(val_string); } else if (string_is_equal(str, "origin")) { if (!string_is_empty(val_string)) db_info->origin = strdup(val_string); } else if (string_is_equal(str, "franchise")) { if (!string_is_empty(val_string)) db_info->franchise = strdup(val_string); } else if (string_is_equal(str, "bbfc_rating")) { if (!string_is_empty(val_string)) db_info->bbfc_rating = strdup(val_string); } else if (string_is_equal(str, "esrb_rating")) { if (!string_is_empty(val_string)) db_info->esrb_rating = strdup(val_string); } else if (string_is_equal(str, "elspa_rating")) { if (!string_is_empty(val_string)) db_info->elspa_rating = strdup(val_string); } else if (string_is_equal(str, "cero_rating")) { if (!string_is_empty(val_string)) db_info->cero_rating = strdup(val_string); } else if (string_is_equal(str, "pegi_rating")) { if (!string_is_empty(val_string)) db_info->pegi_rating = strdup(val_string); } else if (string_is_equal(str, "enhancement_hw")) { if (!string_is_empty(val_string)) db_info->enhancement_hw = strdup(val_string); } else if (string_is_equal(str, "edge_review")) { if (!string_is_empty(val_string)) db_info->edge_magazine_review = strdup(val_string); } else if (string_is_equal(str, "edge_rating")) db_info->edge_magazine_rating = (unsigned)val->val.uint_; else if (string_is_equal(str, "edge_issue")) db_info->edge_magazine_issue = (unsigned)val->val.uint_; else if (string_is_equal(str, "famitsu_rating")) db_info->famitsu_magazine_rating = (unsigned)val->val.uint_; else if (string_is_equal(str, "tgdb_rating")) db_info->tgdb_rating = (unsigned)val->val.uint_; else if (string_is_equal(str, "users")) db_info->max_users = (unsigned)val->val.uint_; else if (string_is_equal(str, "releasemonth")) db_info->releasemonth = (unsigned)val->val.uint_; else if (string_is_equal(str, "releaseyear")) db_info->releaseyear = (unsigned)val->val.uint_; else if (string_is_equal(str, "rumble")) db_info->rumble_supported = (int)val->val.uint_; else if (string_is_equal(str, "coop")) db_info->coop_supported = (int)val->val.uint_; else if (string_is_equal(str, "analog")) db_info->analog_supported = (int)val->val.uint_; else if (string_is_equal(str, "size")) db_info->size = (unsigned)val->val.uint_; else if (string_is_equal(str, "crc")) db_info->crc32 = swap_if_little32( *(uint32_t*)val->val.binary.buff); else if (string_is_equal(str, "sha1")) db_info->sha1 = bin_to_hex_alloc( (uint8_t*)val->val.binary.buff, val->val.binary.len); else if (string_is_equal(str, "md5")) db_info->md5 = bin_to_hex_alloc( (uint8_t*)val->val.binary.buff, val->val.binary.len); else { RARCH_LOG("Unknown key: %s\n", str); } } rmsgpack_dom_value_free(&item); return 0; }
int libretrodb_create_index(libretrodb_t *db, const char *name, const char *field_name) { int rv; struct node_iter_ctx nictx; struct rmsgpack_dom_value key; libretrodb_index_t idx; struct rmsgpack_dom_value item; struct rmsgpack_dom_value * field; struct bintree tree; libretrodb_cursor_t cur; uint64_t idx_header_offset; void * buff = NULL; uint64_t * buff_u64 = NULL; uint8_t field_size = 0; uint64_t item_loc = libretrodb_tell(db); bintree_new(&tree, node_compare, &field_size); if (libretrodb_cursor_open(db, &cur, NULL) != 0) { rv = -1; goto clean; } key.type = RDT_STRING; key.string.len = strlen(field_name); /* We know we aren't going to change it */ key.string.buff = (char *) field_name; while (libretrodb_cursor_read_item(&cur, &item) == 0) { if (item.type != RDT_MAP) { rv = -EINVAL; printf("Only map keys are supported\n"); goto clean; } field = rmsgpack_dom_value_map_value(&item, &key); if (!field) { rv = -EINVAL; printf("field not found in item\n"); goto clean; } if (field->type != RDT_BINARY) { rv = -EINVAL; printf("field is not binary\n"); goto clean; } if (field->binary.len == 0) { rv = -EINVAL; printf("field is empty\n"); goto clean; } if (field_size == 0) field_size = field->binary.len; else if (field->binary.len != field_size) { rv = -EINVAL; printf("field is not of correct size\n"); goto clean; } buff = malloc(field_size + sizeof(uint64_t)); if (!buff) { rv = -ENOMEM; goto clean; } memcpy(buff, field->binary.buff, field_size); buff_u64 = (uint64_t *)buff + field_size; memcpy(buff_u64, &item_loc, sizeof(uint64_t)); if (bintree_insert(&tree, buff) != 0) { printf("Value is not unique: "); rmsgpack_dom_value_print(field); printf("\n"); rv = -EINVAL; goto clean; } buff = NULL; rmsgpack_dom_value_free(&item); item_loc = libretrodb_tell(db); } (void)rv; (void)idx_header_offset; idx_header_offset = lseek(db->fd, 0, SEEK_END); strncpy(idx.name, name, 50); idx.name[49] = '\0'; idx.key_size = field_size; idx.next = db->count * (field_size + sizeof(uint64_t)); libretrodb_write_index_header(db->fd, &idx); nictx.db = db; nictx.idx = &idx; bintree_iterate(&tree, node_iter, &nictx); bintree_free(&tree); clean: rmsgpack_dom_value_free(&item); if (buff) free(buff); if (cur.is_valid) libretrodb_cursor_close(&cur); return 0; }
static int database_cursor_iterate(libretrodb_cursor_t *cur, database_info_t *db_info) { unsigned i; struct rmsgpack_dom_value item; const char* str = NULL; if (libretrodb_cursor_read_item(cur, &item) != 0) return -1; if (item.type != RDT_MAP) { rmsgpack_dom_value_free(&item); return 1; } db_info->analog_supported = -1; db_info->rumble_supported = -1; db_info->coop_supported = -1; for (i = 0; i < item.val.map.len; i++) { uint32_t value = 0; struct rmsgpack_dom_value *key = &item.val.map.items[i].key; struct rmsgpack_dom_value *val = &item.val.map.items[i].value; if (!key || !val) continue; str = key->val.string.buff; value = msg_hash_calculate(str); switch (value) { case DB_CURSOR_SERIAL: db_info->serial = strdup(val->val.string.buff); break; case DB_CURSOR_ROM_NAME: db_info->rom_name = strdup(val->val.string.buff); break; case DB_CURSOR_NAME: db_info->name = strdup(val->val.string.buff); break; case DB_CURSOR_DESCRIPTION: db_info->description = strdup(val->val.string.buff); break; case DB_CURSOR_GENRE: db_info->genre = strdup(val->val.string.buff); break; case DB_CURSOR_PUBLISHER: db_info->publisher = strdup(val->val.string.buff); break; case DB_CURSOR_DEVELOPER: db_info->developer = string_split(val->val.string.buff, "|"); break; case DB_CURSOR_ORIGIN: db_info->origin = strdup(val->val.string.buff); break; case DB_CURSOR_FRANCHISE: db_info->franchise = strdup(val->val.string.buff); break; case DB_CURSOR_BBFC_RATING: db_info->bbfc_rating = strdup(val->val.string.buff); break; case DB_CURSOR_ESRB_RATING: db_info->esrb_rating = strdup(val->val.string.buff); break; case DB_CURSOR_ELSPA_RATING: db_info->elspa_rating = strdup(val->val.string.buff); break; case DB_CURSOR_CERO_RATING: db_info->cero_rating = strdup(val->val.string.buff); break; case DB_CURSOR_PEGI_RATING: db_info->pegi_rating = strdup(val->val.string.buff); break; case DB_CURSOR_ENHANCEMENT_HW: db_info->enhancement_hw = strdup(val->val.string.buff); break; case DB_CURSOR_EDGE_MAGAZINE_REVIEW: db_info->edge_magazine_review = strdup(val->val.string.buff); break; case DB_CURSOR_EDGE_MAGAZINE_RATING: db_info->edge_magazine_rating = val->val.uint_; break; case DB_CURSOR_EDGE_MAGAZINE_ISSUE: db_info->edge_magazine_issue = val->val.uint_; break; case DB_CURSOR_FAMITSU_MAGAZINE_RATING: db_info->famitsu_magazine_rating = val->val.uint_; break; case DB_CURSOR_TGDB_RATING: db_info->tgdb_rating = val->val.uint_; break; case DB_CURSOR_MAX_USERS: db_info->max_users = val->val.uint_; break; case DB_CURSOR_RELEASEDATE_MONTH: db_info->releasemonth = val->val.uint_; break; case DB_CURSOR_RELEASEDATE_YEAR: db_info->releaseyear = val->val.uint_; break; case DB_CURSOR_RUMBLE_SUPPORTED: db_info->rumble_supported = val->val.uint_; break; case DB_CURSOR_COOP_SUPPORTED: db_info->coop_supported = val->val.uint_; break; case DB_CURSOR_ANALOG_SUPPORTED: db_info->analog_supported = val->val.uint_; break; case DB_CURSOR_SIZE: db_info->size = val->val.uint_; break; case DB_CURSOR_CHECKSUM_CRC32: db_info->crc32 = swap_if_little32(*(uint32_t*)val->val.binary.buff); break; case DB_CURSOR_CHECKSUM_SHA1: db_info->sha1 = bin_to_hex_alloc((uint8_t*)val->val.binary.buff, val->val.binary.len); break; case DB_CURSOR_CHECKSUM_MD5: db_info->md5 = bin_to_hex_alloc((uint8_t*)val->val.binary.buff, val->val.binary.len); break; default: RARCH_LOG("Unknown key: %s\n", str); break; } } rmsgpack_dom_value_free(&item); return 0; }