commit c83721c6593c8ee66d1b99fe3abc0a6d3b0e6ea4
parent e49272570ff63ab0afbd4a74f3a97ab4f150fb07
Author: hhvn <dev@hhvn.uk>
Date: Sun, 30 Oct 2022 11:55:04 +0000
Load systems using dbgettree()
Diffstat:
8 files changed, 278 insertions(+), 128 deletions(-)
diff --git a/src/body.c b/src/body.c
@@ -1,5 +1,6 @@
#include <stdlib.h>
#include <raylib.h>
+#include <math.h>
#include "main.h"
static char *bodytype_names[BODY_LAST] = {
@@ -39,6 +40,20 @@ bodytype_strify(Body *body) {
(body->parent ? body->parent->type : 0)];
}
+Body *
+body_init(char *name) {
+ Body *ret;
+
+ ret = malloc(sizeof(Body));
+ if (!ret) return NULL;
+ ret->name = nstrdup(name);
+ ret->t = NULL;
+ ret->parent = NULL;
+ ret->polar = (Polar) { INFINITY, INFINITY };
+
+ return ret;
+}
+
int
body_cmp(Body *b1, Body *b2) {
float v1, v2;
diff --git a/src/db.c b/src/db.c
@@ -82,47 +82,124 @@ dbgetfloat(char *dir, char *group, char *key) {
return ret;
}
+static int
+dbtree_concat(char *path[TREEMAX], size_t depth, char *group, int index) {
+ size_t sl = PATH_MAX;
+ size_t i, len;
+ char *sp = group;
+
+ for (i = 0; i <= depth; i++) {
+ len = strlen(path[i]);
+ if (len + (index ? strlen("index") : 0) >= sl) {
+ warning("insufficient space to concatenate tree path\n");
+ return -1;
+ }
+
+ memcpy(sp, path[i], len);
+ if (i != depth || index)
+ sp[len] = '/';
+ else
+ sp[len] = '\0';
+ sp += len + 1;
+ sl -= len + 1;
+ }
+ if (index) {
+ len = strlen("index");
+ memcpy(sp, "index", len + 1);
+ }
+
+ return 0;
+}
+
int
dbsettree(char *dir, Tree *t, Treesetter func) {
char *path[TREEMAX];
char group[PATH_MAX];
- char *sp;
- size_t sl, i, len;
int depth;
Tree *p;
int ret = 0;
for (p = NULL; tree_iter(t, TREEMAX, &p, &depth) != -1; ) {
-next:
path[depth] = p->name;
if (p->data) {
- sl = sizeof(group);
- sp = group;
- for (i = 0; i <= depth; i++) {
- len = strlen(path[i]);
- if (len + (p->d ? strlen("index") : 0) >= sl) {
- ret = -1;
- warning("insufficient space to concatenate tree path\n");
- goto next;
- }
-
- memcpy(sp, path[i], len);
- if (i != depth || p->d)
- sp[len] = '/';
- else
- sp[len] = '\0';
- sp += len + 1;
- sl -= len + 1;
- }
- if (p->d) {
- len = strlen("index");
- memcpy(sp, "index", len + 1);
+ if (dbtree_concat(path, depth, group, p->d != NULL) == -1) {
+ ret = -1;
+ continue;
}
-
- func(dir, group, depth, p);
+ func(dir, group, p->name, depth, p);
}
}
return ret;
}
+
+int
+dbgettree(char *dir, Tree *r, Treegetter func) {
+ char *ppath, *path;
+ char *psplit[TREEMAX], *split[TREEMAX];
+ char **groups;
+ char group[PATH_MAX];
+ char *name;
+ size_t glen, pslen, slen;
+ size_t common, up;
+ int index;
+ int i, j;
+ Tree *t, *p;
+
+ glen = dblistgroups(&groups, dir);
+ slen = 0;
+ memset(&split, 0, sizeof(split));
+
+ for (i = 0, ppath = path = NULL, t = r; i < glen; i++) {
+ free(ppath);
+ ppath = path;
+ path = strdup(groups[i]);
+
+ memcpy(&psplit, split, sizeof(split));
+ pslen = slen;
+ slen = strsplit(path, "/", split, TREEMAX);
+
+ if (streq(split[slen - 1], "index")) {
+ index = 1;
+ slen--;
+ } else {
+ index = 0;
+ }
+
+ common = strlistcmp(psplit, pslen, split, slen);
+ up = pslen - common;
+
+ if (up && (float)up <= (float)((float)pslen / 2.0f)) {
+ for (j = 0; j < up; j++)
+ t = t->u;
+ j = common;
+ } else if (common == pslen) {
+ j = common;
+ } else {
+ t = r;
+ j = 0;
+ }
+
+ for (; j < slen; j++) {
+nchild:
+ name = split[j];
+
+ for (p = t->d; p; p = p->n) {
+ if (streq(p->name, name)) {
+ t = p;
+ goto nchild;
+ }
+ }
+
+ /* Since name is a part of split[], it will be
+ * corrupted later. The Treegetter function is
+ * therefore responsible for allocating the name
+ * and returning it */
+ t = tree_add_child(t, name, 0, NULL, NULL);
+ dbtree_concat(split, j, group, index || j != slen - 1);
+ if (!t->data)
+ t->name = func(dir, group, name, j + 1, t);
+ }
+ }
+}
diff --git a/src/main.h b/src/main.h
@@ -37,6 +37,8 @@ int strprefix(char *str, char *prefix);
char * strsuffix(char *str, char *suffix);
int strlistpos(char *str, char **list, size_t len);
float strnum(char *str);
+size_t strlistcmp(char **l1, size_t s1, char **l2, size_t s2);
+size_t strsplit(char *str, char *sep, char **list, size_t len);
void edittrunc(wchar_t *str, int *len, int *cur);
void editrm(wchar_t *str, int *len, int *cur);
void editins(wchar_t *str, int *len, int *cur, int size, wchar_t c);
@@ -144,14 +146,16 @@ Polar sys_sum_polar(Polar absolute, Polar relative);
Vector2 sys_get_vector(Body *body);
Polar sys_get_polar(Body *body);
System *sys_init(char *name);
-System *sys_load(System *s, char *name);
-void sys_tree_setter(char *dir, char *group, int depth, Tree *t);
+void sys_tree_load(void);
+char * sys_tree_getter(char *dir, char *group, char *name, int depth, Tree *t);
+void sys_tree_setter(char *dir, char *group, char *name, int depth, Tree *t);
System *sys_get(char *name);
System *sys_default(void);
/* body.c */
int bodytype_enumify(char *name);
char * bodytype_strify(Body *body);
+Body * body_init(char *name);
int body_cmp(Body *b1, Body *b2);
void body_sort(Body **bodies, size_t n);
@@ -187,6 +191,7 @@ int vdbgetf(char *dir, char *group, char *key, char *fmt, va_list args);
int dbgetf(char *dir, char *group, char *key, char *fmt, ...);
int dbgetint(char *dir, char *group, char *key);
float dbgetfloat(char *dir, char *group, char *key);
+int dbgettree(char *dir, Tree *t, Treegetter func);
int dbsettree(char *dir, Tree *t, Treesetter func);
/* loading.c */
diff --git a/src/save.c b/src/save.c
@@ -43,8 +43,9 @@ save_read(char *name) {
save->db.races = smprintf("%s/Races", dir);
save->db.systems = smprintf("%s/Systems", dir);
save->db.fleets = smprintf("%s/Fleets", dir);
- if ((str = dbget(save->db.dir, "index", "homesystem")))
- save->homesys = sys_get(str);
+ /* if ((str = dbget(save->db.dir, "index", "homesystem"))) */
+ /* save->homesys = sys_get(str); */
+ sys_tree_load();
return;
};
diff --git a/src/str.c b/src/str.c
@@ -177,6 +177,36 @@ strnum(char *str) {
return strtof(str, NULL);
}
+size_t
+strlistcmp(char **l1, size_t s1, char **l2, size_t s2) {
+ size_t i;
+
+ for (i = 0; i < s1 && i < s2; i++) {
+ if (!streq(l1[i], l2[i]))
+ break;
+ }
+
+ return i;
+}
+
+size_t
+strsplit(char *str, char *sep, char **list, size_t len) {
+ char *save;
+ size_t i;
+
+ if (!str)
+ return 0;
+
+ for (i = 0; i < len; i++) {
+ list[i] = strtok_r(i == 0 ? str : NULL, sep, &save);
+
+ if (!list[i])
+ break;
+ }
+
+ return i;
+}
+
void
edittrunc(wchar_t *str, int *len, int *cur) {
*len = *cur = 0;
diff --git a/src/struct.h b/src/struct.h
@@ -16,8 +16,8 @@ struct Tree {
Tree *n; /* next */
};
-/* typedef void (*Treegetter)(char *dir, char *group, int depth, Tree *t); */
-typedef void (*Treesetter)(char *dir, char *group, int depth, Tree *t);
+typedef char * (*Treegetter)(char *dir, char *group, char *name, int depth, Tree *t);
+typedef void (*Treesetter)(char *dir, char *group, char *name, int depth, Tree *t);
/* system.c */
enum {
@@ -51,7 +51,10 @@ enum BodyType {
typedef struct Body Body;
struct Body {
Tree *t;
- Body *parent;
+ union {
+ Body *parent;
+ char *pname; /* used during sys_tree_load() */
+ };
Polar polar;
Vector2 vector;
Vector2 pxloc; /* used by ui functions */
diff --git a/src/system.c b/src/system.c
@@ -96,41 +96,96 @@ filter_bodyinsystem(void *data, char *path) {
return 0;
}
-/* If s is true, ignore name and load the system.
- * If s is NULL, call sys_init(name); */
-System *
-sys_load(System *s, char *name) {
- char *dir, *tmp;
- Body **bodies;
- char **bname;
- char **bparent;
- size_t blen, i;
- int pos;
-
- if (!s) s = sys_init(name);
-
- s->lypos.x = dbgetfloat(save->db.systems, name, "x");
- s->lypos.y = dbgetfloat(save->db.systems, name, "y");
-
- dir = smprintf("%s/", s->name);
- blen = dblistgroups_f(&bname, save->db.systems, &filter_bodyinsystem, dir);
- if (!bname) return NULL;
- bparent = malloc(sizeof(char *) * blen);
- bodies = malloc(sizeof(Body *) * blen);
-
- if (!bparent || !bodies)
- return NULL;
-
- /* first pass: init bodies and parents */
- for (i = 0; i < blen; i++) {
- bodies[i] = malloc(sizeof(Body));
- bodies[i]->name = nstrdup(bname[i] + strlen(dir));
- bparent[i] = nstrdup(dbget(save->db.systems, bname[i], "parent"));
-
- tmp = dbget(save->db.systems, bname[i], "type");
- bodies[i]->type = bodytype_enumify(tmp);
-
- switch (bodies[i]->type) {
+void
+sys_tree_load(void) {
+ Tree *t, *bt;
+ System *s;
+ char **bn;
+ Body **bp;
+ int bl, i, n, pos;
+
+ /* initialize systems and bodies */
+ dbgettree(save->db.systems, &save->systems, sys_tree_getter);
+
+ /* bn & bp are only free'd at the end to avoid excess (re)allocations */
+ bl = 10;
+ bn = malloc(bl * sizeof(char *));
+ bp = malloc(bl * sizeof(char *));
+
+ for (t = save->systems.d; t; t = t->n) {
+ s = t->data;
+
+ /* first pass: init names & pointer arrays */
+ for (i = 0, bt = t->d; bt; bt = bt->n, i++) {
+ if (i == bl - 1) {
+ bl += 10;
+ bn = realloc(bn, bl * sizeof(char *));
+ bp = realloc(bp, bl * sizeof(char *));
+ }
+
+ bn[i] = bt->name;
+ bp[i] = bt->data;
+ }
+
+ n = i;
+
+ /* second pass: assign parent pointer */
+ for (i = 0; i < n; i++) {
+ if ((pos = strlistpos(bp[i]->pname, bn, n)) != -1) {
+ free(bp[i]->pname);
+ bp[i]->parent = bp[pos];
+ } else {
+ bp[i]->parent = NULL;
+ }
+ }
+
+ /* third pass: get coords (needs parent ptr from second pass) */
+ for (i = 0; i < n; i++) {
+ sys_get_polar(bp[i]); /* Builds the cache for us: this
+ is more efficient as it can
+ cache the parent too */
+ bp[i]->vector = sys_vectorize(bp[i]->polar);
+
+ /* This could deal with moons, but that's probably not
+ * useful. What about multiple stars in a system? That
+ * may need to be addressed in future */
+ if (bp[i]->parent && bp[i]->parent->type == BODY_STAR && (!s->furthest_body ||
+ (bp[i]->type == BODY_COMET ? bp[i]->maxdist : bp[i]->dist) >
+ (s->furthest_body->type == BODY_COMET ? s->furthest_body->maxdist : s->furthest_body->dist)))
+ s->furthest_body = bp[i];
+ }
+
+ body_sort(bp, n);
+ }
+
+ free(bp);
+ free(bn);
+}
+
+char *
+sys_tree_getter(char *dir, char *group, char *name, int depth, Tree *t) {
+ System *s;
+ Body *b;
+
+ if (depth == 1) {
+ s = sys_init(name);
+ s->t = t;
+
+ s->lypos.x = dbgetfloat(dir, group, "x");
+ s->lypos.y = dbgetfloat(dir, group, "y");
+
+ t->type = SYSTREE_SYS;
+ t->data = s;
+ return s->name;
+ } else {
+ s = t->u->data; /* parent should be a system */
+ b = body_init(name);
+ b->t = t;
+
+ b->pname = nstrdup(dbget(dir, group, "parent"));
+
+ b->type = bodytype_enumify(dbget(dir, group, "type"));
+ switch (b->type) {
case BODY_STAR: s->num.stars++; break;
case BODY_PLANET: s->num.planets++; break;
case BODY_DWARF: s->num.dwarfs++; break;
@@ -139,74 +194,31 @@ sys_load(System *s, char *name) {
case BODY_MOON: s->num.moons++; break;
}
- bodies[i]->radius = strnum(dbget(save->db.systems, bname[i], "radius"));
- bodies[i]->mass = strnum(dbget(save->db.systems, bname[i], "mass"));
- bodies[i]->orbdays = strnum(dbget(save->db.systems, bname[i], "orbdays"));
- if (bodies[i]->type == BODY_COMET) {
+ b->radius = dbgetfloat(dir, group, "radius");
+ b->mass = dbgetfloat(dir, group, "mass");
+ b->orbdays = dbgetfloat(dir, group, "orbdays");
+ if (b->type == BODY_COMET) {
/* mindist is on opposite side of parent */
- bodies[i]->mindist = 0 - strnum(dbget(save->db.systems, bname[i], "mindist"));
- bodies[i]->maxdist = strnum(dbget(save->db.systems, bname[i], "maxdist"));
- bodies[i]->curdist = strnum(dbget(save->db.systems, bname[i], "curdist"));
- bodies[i]->theta = strnum(dbget(save->db.systems, bname[i], "theta"));
- bodies[i]->inward = strnum(dbget(save->db.systems, bname[i], "inward"));
+ b->mindist = 0 - dbgetfloat(dir, group, "mindist");
+ b->maxdist = dbgetfloat(dir, group, "maxdist");
+ b->curdist = dbgetfloat(dir, group, "curdist");
+ b->theta = dbgetfloat(dir, group, "theta");
+ b->inward = dbgetfloat(dir, group, "inward");
} else {
- bodies[i]->dist = strnum(dbget(save->db.systems, bname[i], "dist"));
- bodies[i]->curtheta = strnum(dbget(save->db.systems, bname[i], "curtheta"));
+ b->dist = dbgetfloat(dir, group, "dist");
+ b->curtheta = dbgetfloat(dir, group, "curtheta");
}
- /* so sys_get_polar() knows if it's usable */
- bodies[i]->polar = (Polar) { INFINITY, INFINITY };
+ t->type = SYSTREE_BODY;
+ t->data = b;
+ return b->name;
}
-
- /* second pass: assign parents (needs bparent[] from first pass) */
- for (i = 0; i < blen; i++) {
- tmp = smprintf("%s%s", dir, bparent[i]);
- if ((pos = strlistpos(tmp, bname, blen)) != -1)
- bodies[i]->parent = bodies[pos];
- else
- bodies[i]->parent = NULL;
- free(tmp);
- }
-
- /* third pass: get coords (needs parent ptr from second pass) */
- for (i = 0; i < blen; i++) {
- sys_get_polar(bodies[i]); /* Builds the cache for us: this is more
- efficient as it can cache the parent too */
- bodies[i]->vector = sys_vectorize(bodies[i]->polar);
-
- /* This could deal with moons, but that's probably not useful.
- * What about multiple stars in a system? That may need to be
- * addressed in future */
- if (bodies[i]->parent && bodies[i]->parent->type == BODY_STAR && (!s->furthest_body ||
- (bodies[i]->type == BODY_COMET ? bodies[i]->maxdist : bodies[i]->dist) >
- (s->furthest_body->type == BODY_COMET ? s->furthest_body->maxdist : s->furthest_body->dist)))
- s->furthest_body = bodies[i];
- }
-
- for (i = 0; i < blen; i++) {
- free(bparent[i]);
- }
- free(bparent);
- dblistfree(bname, blen);
- free(dir);
-
- body_sort(bodies, blen);
-
- tree_add_child(&save->systems, s->name, SYSTREE_SYS, s, &s->t);
- for (i = 0; i < blen; i++)
- tree_add_child(s->t, bodies[i]->name, SYSTREE_BODY, bodies[i], &bodies[i]->t);
-
- /* The bodies are attached to the systree now, so don't need to be freed */
- free(bodies);
-
- return s;
}
void
-sys_tree_setter(char *dir, char *group, int depth, Tree *t) {
+sys_tree_setter(char *dir, char *group, char *name, int depth, Tree *t) {
System *s;
Body *b;
- char *parent;
switch (t->type) {
case SYSTREE_SYS:
@@ -237,13 +249,19 @@ sys_tree_setter(char *dir, char *group, int depth, Tree *t) {
System *
sys_get(char *name) {
- /* For now, call sys_load. In future, get the system via save. */
- return sys_load(NULL, name);
+ Tree *t;
+
+ for (t = save->systems.d; t; t = t->n)
+ if (streq(t->name, name))
+ return t->data;
+
+ return NULL;
}
System *
sys_default(void) {
char *str;
+
if (view_main.sys)
return view_main.sys;
else if ((str = dbget(save->db.dir, "index", "selsystem")))
diff --git a/src/tree.c b/src/tree.c
@@ -20,6 +20,7 @@ tree_add_child(Tree *t, char *name, int type, void *data, Tree **ptr) {
e->type = type;
e->data = data;
e->u = t;
+ e->collapsed = 0;
if (!t->d) {
t->d = e;