eco2d/code/foundation/src/models/database.c

192 lines
5.0 KiB
C

#include "models/database.h"
#include "models/assets.h"
#include "3rd_sqlite3.h"
#include "zpl.h"
#include "world/blocks.h"
#include "models/crafting.h"
#include "models/items.h"
#ifndef ECO2D_DB
#define ECO2D_DB "eco2d.db"
#endif
static sqlite3 *db;
static zpl_array(zpl_csv_object) csv_stack;
static
void sql_asset(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
if (argc == 1) {
const char *name = (const char *)sqlite3_value_text(argv[0]);
if (name) {
sqlite3_result_int(ctx, atoi(db_get(zpl_bprintf("SELECT id FROM assets WHERE name = '%s'", name), false)));
return;
}
}
sqlite3_result_null(ctx);
}
bool db_init() {
bool new_db = !zpl_fs_exists(ECO2D_DB);
sqlite3_open(ECO2D_DB, &db);
assert(db && "Failed to open database.");
zpl_array_init(csv_stack, zpl_heap());
sqlite3_create_function(db, "asset", 1, SQLITE_UTF8, NULL, sql_asset, NULL, NULL);
if (new_db) {
zpl_printf("[INFO] Creating new database...\n");
db_exec_file("art/queries/tables.sql");
assets_db_init();
db_exec_file("art/queries/resources.sql");
db_exec_file("art/queries/blocks.sql");
db_exec_file("art/queries/recipes.sql");
db_exec_file("art/queries/items.sql");
}
// initialise models db
zpl_printf("[INFO] Loading models from database...\n");
assets_db(); zpl_printf("[INFO] Assets loaded.\n");
blocks_db(); zpl_printf("[INFO] Blocks loaded.\n");
craft_db(); zpl_printf("[INFO] Recipes loaded.\n");
item_db(); zpl_printf("[INFO] Items loaded.\n");
return new_db;
}
void db_shutdown() {
sqlite3_close(db);
}
char *db_get(char *query, bool header) {
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(db, query, -1, &stmt, 0);
if (rc) {
zpl_printf("[ERROR] Failed to prepare query: %s\n", sqlite3_errmsg(db));
return 0;
}
zpl_string buf = zpl_string_make(zpl_heap(), "");
bool is_first=header;
int cols = sqlite3_column_count(stmt);
while (sqlite3_step(stmt) == SQLITE_ROW) {
if (is_first) {
is_first = 0;
for (int i = 0; i < cols; i++) {
buf = zpl_string_appendc(buf, (const char *)sqlite3_column_name(stmt, i));
if (i < cols - 1) {
buf = zpl_string_appendc(buf, ",");
}
}
buf = zpl_string_appendc(buf, "\n");
}
for (int i = 0; i < cols; i++) {
const char *val = (const char *)sqlite3_column_text(stmt, i);
if (val == NULL)
val = "\"(null)\"";
buf = zpl_string_appendc(buf, val);
if (i < cols - 1) {
buf = zpl_string_appendc(buf, ",");
}
}
buf = zpl_string_appendc(buf, "\n");
}
sqlite3_finalize(stmt);
#if 0
char *data = zpl_bprintf("%s", buf);
zpl_string_free(buf);
#else //@leak
char *data = (char *)buf;
#endif
return data;
}
char *db_row(char *table, char *name) {
int kind = atoi(db_get(zpl_bprintf("SELECT id FROM assets WHERE name='%s';", name), 0));
return db_get(zpl_bprintf("SELECT * FROM %s WHERE kind='%d';", table, kind), 1);
}
bool db_csv(zpl_csv_object *csv, char *query) {
zpl_zero_item(csv);
char *res = db_get(query, 1);
assert(res);
return zpl_csv_parse(csv, res, zpl_heap(), 1);
}
bool db_row_csv(zpl_csv_object *csv, char *table, char *name) {
zpl_zero_item(csv);
char *res = db_row(table, name);
assert(res);
return zpl_csv_parse(csv, res, zpl_heap(), 1);
}
bool db_push(char *query) {
zpl_csv_object csv;
if (db_csv(&csv, query)) {
return false;
}
zpl_array_append(csv_stack, csv);
return true;
}
bool db_row_push(char *table, char *name) {
zpl_csv_object csv;
if (db_row_csv(&csv, table, name)) {
return false;
}
zpl_array_append(csv_stack, csv);
return true;
}
zpl_adt_node *db_field(char *field, int row) {
zpl_csv_object *csv = db_last();
for (int i = 0; i < zpl_array_count(csv->nodes); i++) {
if (!zpl_strcmp(csv->nodes[i].name, field)) {
return &csv->nodes[i].nodes[row];
}
}
assert(0 && "Field not found");
return NULL;
}
size_t db_rows() {
zpl_csv_object *csv = db_last();
return zpl_array_count(csv->nodes[0].nodes);
}
zpl_csv_object *db_last() {
assert(zpl_array_count(csv_stack) > 0);
return zpl_array_end(csv_stack);
}
void db_pop() {
assert(zpl_array_count(csv_stack) > 0);
zpl_csv_free(zpl_array_end(csv_stack));
zpl_array_pop(csv_stack);
}
bool db_exec_file(const char *sql) {
zpl_file_contents contents = zpl_file_read_contents(zpl_heap(), true, sql);
if (!db_exec(contents.data)) {
assert(0 && "Failed to create tables");
return false;
}
zpl_file_free_contents(&contents);
return true;
}
bool db_exec(const char *query) {
bool ok = !sqlite3_exec(db, query, 0, 0, 0);
if (!ok) {
zpl_printf("[ERROR] Failed to execute query: %s\n", sqlite3_errmsg(db));
}
return ok;
}