omi-eikyo/src/g_objdef.c

580 lines
14 KiB
C

// Copyright © 2017 Project Golan, all rights reserved.
#include "g_objdef.h"
#include "m_tokbuf.h"
#include "m_str.h"
#include "m_darray.h"
#include <Doominati.h>
#include <GDCC/HashMap.h>
#include <setjmp.h>
#include <string.h>
#include <stdlib.h>
#define G_otmap_GetKey(o) ( (o)->name)
#define G_otmap_GetNext(o) (&(o)->next)
#define G_otmap_GetPrev(o) (&(o)->prev)
#define G_otmap_HashObj(o) ( (o)->keyhash)
#define G_otmap_HashKey(k) (M_StrHash(k))
#define G_otmap_KeyCmp(l, r) (strcmp(l, r))
#define G_etmap_GetKey(o) ( (o)->name)
#define G_etmap_GetNext(o) (&(o)->next)
#define G_etmap_GetPrev(o) (&(o)->prev)
#define G_etmap_HashObj(o) ( (o)->keyhash)
#define G_etmap_HashKey(k) (M_StrHash(k))
#define G_etmap_KeyCmp(l, r) (strcmp(l, r))
#define G_frmap_GetKey(o) ( (o)->name)
#define G_frmap_GetNext(o) (&(o)->next)
#define G_frmap_GetPrev(o) (&(o)->prev)
#define G_frmap_HashObj(o) ( (o)->keyhash)
#define G_frmap_HashKey(k) (M_StrHash(k))
#define G_frmap_KeyCmp(l, r) (strcmp(l, r))
#define G_anmap_GetKey(o) ( (o)->name)
#define G_anmap_GetNext(o) (&(o)->next)
#define G_anmap_GetPrev(o) (&(o)->prev)
#define G_anmap_HashObj(o) ( (o)->keyhash)
#define G_anmap_HashKey(k) (M_StrHash(k))
#define G_anmap_KeyCmp(l, r) (strcmp(l, r))
#define G_anims_GetKey(o) ( (o)->name)
#define G_anims_GetNext(o) (&(o)->next)
#define G_anims_GetPrev(o) (&(o)->prev)
#define G_anims_HashObj(o) ( (o)->keyhash)
#define G_anims_HashKey(k) (M_StrHash(k))
#define G_anims_KeyCmp(l, r) (strcmp(l, r))
// Static Functions ----------------------------------------------------------|
_Noreturn static void G_ObjDef_throw (struct G_dodst *st, char const *emsg);
[[__optional_args(1)]]
static M_token *G_ObjDef_expect (struct G_dodst *st, M_tokty t, char const *exp);
[[__optional_args(1)]]
static struct G_odarg G_ObjDef_getArgs (struct G_dodst *st, char const *arg);
// Types ---------------------------------------------------------------------|
GDCC_HashMap_Decl(G_otmap, char const *, struct G_otdcl)
GDCC_HashMap_Decl(G_etmap, char const *, struct G_etdcl)
GDCC_HashMap_Decl(G_frmap, char const *, struct G_frdcl)
GDCC_HashMap_Decl(G_anmap, char const *, struct G_andcl)
GDCC_HashMap_Decl(G_anims, char const *, struct G_manim)
typedef struct G_dodst
{
__prop getToken {call: M_TokBuf_Get (->tbPtr)}
__prop unget {call: M_TokBuf_UnGet (->tbPtr)}
__prop reget {call: M_TokBuf_ReGet (->tbPtr)}
__prop drop {call: M_TokBuf_Drop (->tbPtr)}
__prop expect {call: G_ObjDef_expect (this)}
__prop throw {call: G_ObjDef_throw (this)}
__prop getArgs {call: G_ObjDef_getArgs(this)}
jmp_buf ejmp;
int lines;
char const *fname;
FILE *fp;
M_tkbuf tb, *tbPtr;
} G_dodst;
typedef union G_odprm
{
mword u;
integ i;
fixed k;
lfrac r;
} G_odprm;
typedef struct G_odarg
{
G_odprm argv[8];
int argc;
} G_odarg;
typedef struct G_otdcl
{
mword subt;
mword extm;
DGE_CallbackType fptr;
char const *name;
size_t keyhash;
struct G_otdcl *next, **prev;
} G_otdcl;
typedef struct G_etdcl
{
G_entty type;
G_anims anim;
M_Vec_decl(struct G_manim, anims);
char name[32];
size_t keyhash;
struct G_etdcl *next, **prev;
} G_etdcl;
typedef struct G_frdcl
{
M_texid sprite;
char name[32];
size_t keyhash;
struct G_frdcl *next, **prev;
} G_frdcl;
typedef struct G_andcl
{
M_Vec_decl(G_frame, frame);
G_anima anim;
char name[32];
size_t keyhash;
struct G_andcl *next, **prev;
} G_andcl;
typedef struct G_manim
{
G_anima anim;
char name[32];
size_t keyhash;
struct G_manim *next, **prev;
} G_manim;
// Static Objects ------------------------------------------------------------|
static G_otmap G_objtypes;
static G_etmap G_enttypes;
static G_frmap G_frametab;
static G_anmap G_animsmap;
M_Vec_defn(G_otdcl, G_otvec, static);
M_Vec_defn(G_etdcl, G_etvec, static);
M_Vec_defn(G_frdcl, G_frvec, static);
M_Vec_defn(G_andcl, G_anvec, static);
// Static Functions ----------------------------------------------------------|
GDCC_HashMap_Defn(G_otmap, char const *, G_otdcl)
GDCC_HashMap_Defn(G_etmap, char const *, G_etdcl)
GDCC_HashMap_Defn(G_frmap, char const *, G_frdcl)
GDCC_HashMap_Defn(G_anmap, char const *, G_andcl)
GDCC_HashMap_Defn(G_anims, char const *, G_manim)
//
// G_ObjDef_throw
//
_Noreturn static void G_ObjDef_throw(G_dodst *st, char const *emsg)
{
fprintf(stderr, "ObjDef (%s:%i): %s\n", st->fname, st->reget()->line, emsg);
longjmp(st->ejmp, 1);
}
//
// G_ObjDef_tokProcess
//
static enum M_tkprc G_ObjDef_tokProcess(M_token *tok, void *udata)
{
G_dodst *st = (G_dodst *)udata;
tok->line = st->lines;
switch(tok->type) {
default: return tokproc_next;
case tok_eof: return tokproc_done;
case tok_lnend: st->lines++;
case tok_cmtlin: return tokproc_skip;
}
}
//
// G_ObjDef_expect
//
static M_token *G_ObjDef_expect(G_dodst *st, M_tokty t, char const *exp)
{
M_token *tok;
if((tok = st->getToken())->type != t) {
if(exp) st->throw(M_StrFmt("expected %s", exp));
else st->throw("unexpected token");
}
return tok;
}
//
// G_ObjDef_getArgs
//
[[__optional_args(1)]]
static G_odarg G_ObjDef_getArgs(G_dodst *st, char const *arg)
{
G_odarg a = {};
bool done = false;
int i = 0;
for(;;)
{
char ch = arg[i++];
if(!done && (arg[i] == '\0' || arg[i] == '|'))
done = true;
switch(ch)
{
#define CaseN(c, t, f, ...) \
case c: \
a.argv[a.argc++].t = \
f(st->expect(tok_number, "number")->textV, __VA_ARGS__); \
break
CaseN('u', u, strtoui, NULL, 0);
CaseN('i', i, strtoi, NULL, 0);
CaseN('k', k, strtofxhk, NULL);
CaseN('r', r, strtofxlk, NULL);
// TODO: get david to implement fracts
//CaseN('r', r, strtofxlr, NULL);
#undef CaseN
case '|': done = true; continue;
case '\0': st->throw("too many arguments");
default: st->throw("unexpected argument type");
}
if(!st->drop(tok_comma)) break;
}
if(!done)
st->throw("not enough arguments");
return a;
}
//
// G_ObjDef_getEntAnim
//
static void G_ObjDef_getEntAnim(G_dodst *st, G_etdcl *decl)
{
char const *alias = st->expect(tok_identi, "alias name")->textV;
st->expect(tok_eq, "'='");
G_andcl *an = G_animsmap.find(st->expect(tok_identi, "anim name")->textV);
if(an == NULL) st->throw("anim not defined");
if(decl->anim.find(alias)) st->throw("alias redefinition");
M_Vec_grow(decl->anims, 1);
G_manim *anim = &M_Vec_next(decl->anims);
M_strbufcpy(anim->name, alias);
anim->keyhash = M_StrHash(anim->name);
anim->anim = an->anim;
decl->anim.insert(anim);
}
//
// G_ObjDef_getEntProp
//
static void G_ObjDef_getEntProp(G_dodst *st, G_entty *type, char const *id)
{
if(strcmp(id, "subtype") == 0)
{
G_otdcl *ot = G_objtypes.find(st->expect(tok_identi, "subtype")->textV);
if(!ot) st->throw("invalid subtype name");
type->subtype = ot->subt;
type->ext = ot->extm;
type->task = ot->fptr;
}
else if(strcmp(id, "size") == 0)
{
G_odarg a = st->getArgs("k|kk");
if(a.argc == 1) {
type->sx = type->sy = a.argv[0].k;
type->sz = 16;
} else {
type->sx = a.argv[0].k;
type->sy = a.argv[1].k;
if(a.argc > 2) type->sz = a.argv[2].k;
else type->sz = 16;
}
}
else if(strcmp(id, "drawsize") == 0)
{
G_odarg a = st->getArgs("k|k");
if(a.argc == 1) {
type->rsx = type->rsy = a.argv[0].k;
} else {
type->rsx = a.argv[0].k;
type->rsy = a.argv[1].k;
}
}
else if(strcmp(id, "sprite") == 0)
{
type->sprite = DGE_Texture_Get(
M_StrCreate(st->expect(tok_string, "sprite name")->textV));
}
// TODO: change this when access through rvalues is fixed
#define Prop(name, t) \
else if(strcmp(id, #name) == 0) \
__with(G_odarg a = st->getArgs(#t);) type->name = a.argv[0].t
Prop(health, i);
Prop(friction, k);
Prop(mass, k);
#undef Prop
else
st->throw("unknown identifier in entity definition");
}
//
// G_ObjDef_getFrameSprite
//
static M_texid G_ObjDef_getFrameSprite(char const *fmt, char frame, int i)
{
M_Vec_defn(char, buf);
for(; *fmt; fmt++)
{
if(*fmt != '%') {M_Vec_grow(buf, 1); M_Vec_next(buf) = *fmt; continue;}
switch(*++fmt)
{
default:
case '%': M_Vec_grow(buf, 1); M_Vec_next(buf) = *fmt; break;
case 'c': M_Vec_grow(buf, 1); M_Vec_next(buf) = frame; break;
case 'i':
M_Vec_grow(buf, 20);
bufC += snprintf(&bufV[bufC], 20, "%i", i);
break;
}
}
if(!bufV) return 0;
M_texid id = DGE_Texture_Get(DGE_String_Create(bufV, bufC));
M_Vec_clear(buf);
return id;
}
//
// G_ObjDef_parseFrameDef
//
static void G_ObjDef_parseFrameDef(G_dodst *st)
{
char const *name = st->expect(tok_identi, "name")->textV;
char const *frame = st->expect(tok_identi, "frames")->textV;
char const *fmt = st->expect(tok_string, "format")->textV;
for(int i = 0; frame[i]; i++)
{
if(frame[i] < 'A' || frame[i] > 'Z')
st->throw("invalid frame");
char const *key = M_StrFmt("%s%c", name, frame[i]);
G_frdcl *decl = G_frametab.find(key);
if(!decl)
{
M_Vec_grow(G_frvec, 1);
*(decl = &M_Vec_next(G_frvec)) = (G_frdcl){};
M_strbufcpy(decl->name, key);
decl->keyhash = M_StrHash(decl->name);
G_frametab.insert(decl);
}
decl->sprite = G_ObjDef_getFrameSprite(fmt, 'A' + i, i + 1);
}
}
//
// G_ObjDef_parseAnim
//
static void G_ObjDef_parseAnim(G_dodst *st, G_andcl *decl)
{
char const *name = st->expect(tok_identi, "name")->textV;
char const *frames = st->expect(tok_identi, "frame")->textV;
int time = strtoi(st->expect(tok_number, "time")->textV, NULL, 0);
M_Vec_grow(decl->frame, strlen(frames));
for(char const *frame = frames; *frame; frame++) {
char *key = M_StrFmt("%s%c", name, *frame);
G_frdcl *fr = G_frametab.find(key);
if(!fr) st->throw(M_StrFmt("undefined frame '%s'", key));
M_Vec_next(decl->frame) = (G_frame){time, fr->sprite};
}
}
//
// G_ObjDef_parseAnimDef
//
static void G_ObjDef_parseAnimDef(G_dodst *st)
{
bool ins = true;
G_andcl *decl;
__with(char const *name = st->expect(tok_identi, "anim name")->textV;)
{
if((decl = G_animsmap.find(name)) == NULL) {
M_Vec_grow(G_anvec, 1);
*(decl = &M_Vec_next(G_anvec)) = (G_andcl){};
M_strbufcpy(decl->name, name);
} else {
ins = false;
}
}
st->expect(tok_braceo, "'{'");
while(!st->drop(tok_bracec))
G_ObjDef_parseAnim(st, decl);
decl->anim = (G_anima){decl->frameV, decl->frameC};
if(ins) {
decl->keyhash = M_StrHash(decl->name);
G_animsmap.insert(decl);
}
}
//
// G_ObjDef_parseEntDef
//
static void G_ObjDef_parseEntDef(G_dodst *st)
{
bool ins = true;
G_etdcl *decl;
__with(char const *name = st->expect(tok_identi, "entity name")->textV;)
{
if((decl = G_enttypes.find(name)) == NULL) {
M_Vec_grow(G_etvec, 1);
*(decl = &M_Vec_next(G_etvec)) = (G_etdcl){};
M_strbufcpy(decl->name, name);
G_anims_ctor(&decl->anim, 8, 8);
} else {
ins = false;
}
}
if(st->drop(tok_lt)) {
G_etdcl *base =
G_enttypes.find(st->expect(tok_identi, "base entity name")->textV);
if(!base) st->throw("base type not found");
decl->type = base->type;
}
st->expect(tok_braceo, "'{'");
for(M_token *tok; (tok = st->getToken())->type != tok_bracec;)
switch(tok->type)
{
case tok_identi:
if(strcmp(tok->textV, "anim") == 0)
G_ObjDef_getEntAnim(st, decl);
else
G_ObjDef_getEntProp(st, &decl->type, tok->textV);
break;
case tok_semico:
continue;
default:
st->throw("expected '}'");
}
if(ins) {
decl->keyhash = M_StrHash(decl->name);
G_enttypes.insert(decl);
}
}
// Extern Functions ----------------------------------------------------------|
//
// G_ObjDef_Init
//
void G_ObjDef_Init(void)
{
G_otmap_ctor(&G_objtypes, 16, 16);
G_etmap_ctor(&G_enttypes, 16, 16);
G_frmap_ctor(&G_frametab, 16, 16);
G_anmap_ctor(&G_animsmap, 16, 16);
}
//
// G_ObjDef_Load
//
void G_ObjDef_Load(char const *fname)
{
G_dodst st = {.fname = fname, .lines = 1, .tbPtr = &st.tb,
.tb = {.bbeg = 4, .bend = 10, .tokProcess = G_ObjDef_tokProcess,
.udata = &st}};
st.fp = fopen(fname, "r");
st.tb.fp = st.fp;
if(!st.fp) {
fprintf(stderr, "ObjDef: couldn't open file '%s'\n", fname);
goto done;
}
st.tb.ctor();
if(setjmp(st.ejmp) == 1)
goto done;
for(M_token *tok; (tok = st.getToken())->type != tok_eof;)
switch(tok->type)
{
case tok_identi:
if(strcmp(tok->textV, "entity") == 0)
G_ObjDef_parseEntDef(&st);
else if(strcmp(tok->textV, "anim") == 0)
G_ObjDef_parseAnimDef(&st);
else if(strcmp(tok->textV, "frame") == 0)
G_ObjDef_parseFrameDef(&st);
else if(strcmp(tok->textV, "include") == 0)
G_ObjDef_Load(st.expect(tok_string, "string")->textV);
else
st.throw("unknown identifier in toplevel");
continue;
default:
st.throw("expected toplevel definition");
}
printf("ObjDef: '%s' loaded.\n", fname);
done:
st.tb.dtor();
fclose(st.fp);
}
//
// G_ObjDef_AddType
//
void G_ObjDef_AddType(char const *name, mword subtype, mword ext, DGE_CallbackType fptr)
{
M_Vec_grow(G_otvec, 1);
G_otvecV[G_otvecC] = (G_otdcl){subtype, ext, fptr, name, M_StrHash(name)};
G_objtypes.insert(&M_Vec_next(G_otvec));
}
//
// G_ObjDef_GetType
//
G_entty const *G_ObjDef_GetType(char const *name)
{
G_etdcl const *decl = G_enttypes.find(name);
return decl ? &decl->type : NULL;
}
//
// G_ObjDef_GetTAni
//
G_anima const *G_ObjDef_GetTAni(G_entty const *type, char const *name)
{
G_manim *anim = ((G_etdcl *)type)->anim.find(name);
return anim ? &anim->anim : NULL;
}
//
// G_ObjDef_GetAnim
//
G_anima const *G_ObjDef_GetAnim(char const *name)
{
G_andcl *an = G_animsmap.find(name);
return an ? &an->anim : NULL;
}
// EOF