omi-eikyo/src/g_objdef.c

612 lines
15 KiB
C

// Copyright © 2017 Project Golan, all rights reserved.
#include "g_object.h"
#include "m_token.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_dodTokBeg (4)
#define G_dodTokEnd (10)
#define G_ofmap_GetKey(o) ( (o)->name)
#define G_ofmap_GetNext(o) (&(o)->next)
#define G_ofmap_GetPrev(o) (&(o)->prev)
#define G_ofmap_HashObj(o) ( (o)->keyhash)
#define G_ofmap_HashKey(k) (M_StrHash(k))
#define G_ofmap_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);
static M_token *G_ObjDef_getToken(struct G_dodst *st);
static M_token *G_ObjDef_unget (struct G_dodst *st);
static M_token *G_ObjDef_reget (struct G_dodst *st);
static bool G_ObjDef_drop (struct G_dodst *st, M_tokty t);
[[__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_ofmap, char const *, struct G_ofdcl)
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 throw {call: G_ObjDef_throw (this)}
__prop getToken {call: G_ObjDef_getToken(this)}
__prop unget {call: G_ObjDef_unget (this)}
__prop drop {call: G_ObjDef_drop (this)}
__prop expect {call: G_ObjDef_expect (this)}
__prop getArgs {call: G_ObjDef_getArgs (this)}
__prop reget {call: G_ObjDef_reget (this)}
jmp_buf ejmp;
int lines;
char const *fname;
FILE *fp;
int tpos, tend;
M_token toks[G_dodTokEnd];
} 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_ofdcl
{
DGE_CallbackType fptr;
char const *name;
size_t keyhash;
struct G_ofdcl *next, **prev;
} G_ofdcl;
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);
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_ofmap G_objfuncs;
static G_etmap G_enttypes;
static G_frmap G_frametab;
static G_anmap G_animsmap;
M_Vec_defn(G_ofdcl, G_ofvec, 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_ofmap, char const *, G_ofdcl)
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_getToken
//
static M_token *G_ObjDef_getToken(G_dodst *st)
{
if(++st->tpos < st->tend)
return &st->toks[st->tpos];
memmove(&st->toks[0], &st->toks[st->tend - G_dodTokBeg],
sizeof(M_token) * G_dodTokBeg);
for(st->tpos = st->tend = G_dodTokBeg; st->tend < G_dodTokEnd; st->tend++)
{
skip:
M_Tk_Parse(st->fp, &st->toks[st->tend]);
st->toks[st->tend].line = st->lines;
switch(st->toks[st->tend].type) {
default: break;
case tok_eof: goto done;
case tok_lnend: st->lines++;
case tok_cmtlin: goto skip;
}
}
/*
for(int i = 0; i < st->tend; i++)
printf("%i: %s%s\n", st->toks[i].type, st->toks[i].text, i == st->tpos ? " <-- cursor is here" : "");
*/
done:
return &st->toks[st->tpos];
}
//
// G_ObjDef_unget
//
static M_token *G_ObjDef_unget(G_dodst *st)
{
return &st->toks[st->tpos--];
}
//
// G_ObjDef_reget
//
static M_token *G_ObjDef_reget(G_dodst *st)
{
return &st->toks[st->tpos];
}
//
// G_ObjDef_drop
//
static bool G_ObjDef_drop(G_dodst *st, M_tokty t)
{
if(st->getToken()->type != t) {
st->unget();
return false;
} else {
return true;
}
}
//
// 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")->text, __VA_ARGS__); \
break
CaseN('u', u, strtoui, NULL, 0);
CaseN('i', i, strtoi, NULL, 0);
CaseN('k', k, strtofxhk, NULL);
CaseN('r', r, strtofxk, 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 alias[M_countof(decl->name)];
M_strbufcpy(alias, st->expect(tok_identi, "alias name")->text);
st->expect(tok_eq, "'='");
G_andcl *an = G_animsmap.find(st->expect(tok_identi, "anim name")->text);
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 = &decl->animsV[decl->animsC++];
M_strbufcpy(anim->name, alias);
anim->keyhash = M_StrHash(anim->name);
anim->anim = (G_anima){an->frameV, an->frameC};
decl->anim.insert(anim);
}
//
// G_ObjDef_getEntProp
//
static void G_ObjDef_getEntProp(G_dodst *st, G_entty *type, char const *id)
{
if(strcmp(id, "task") == 0)
{
G_ofdcl *fn = G_objfuncs.find(st->expect(tok_identi, "function")->text);
if(!fn) st->throw("invalid function name");
type->task = fn->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;
} else {
type->sx = a.argv[0].k;
type->sy = a.argv[1].k;
if(a.argc > 2) type->sz = a.argv[2].k;
}
}
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")->text));
}
// 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); bufV[bufC++] = *fmt; continue;}
switch(*++fmt)
{
default:
case '%': M_Vec_grow(buf, 1); bufV[bufC++] = *fmt; break;
case 'c': M_Vec_grow(buf, 1); bufV[bufC++] = 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));
free(bufV);
return id;
}
//
// G_ObjDef_parseFrameDef
//
static void G_ObjDef_parseFrameDef(G_dodst *st)
{
char name[32], frame[32], fmt[32];
M_strbufcpy(name, st->expect(tok_identi, "name")->text);
M_strbufcpy(frame, st->expect(tok_identi, "frames")->text);
M_strbufcpy(fmt, st->expect(tok_string, "format")->text);
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 = &G_frvecV[G_frvecC++]) = (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 name[32], frames[32];
M_strbufcpy(name, st->expect(tok_identi, "name")->text);
M_strbufcpy(frames, st->expect(tok_identi, "frame")->text);
int time = strtoi(st->expect(tok_number, "time")->text, NULL, 0);
M_Vec_growN(decl->frame, strlen(frames), 0);
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));
decl->frameV[decl->frameC++] = (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")->text;)
{
if((decl = G_animsmap.find(name)) == NULL) {
M_Vec_grow(G_anvec, 1);
*(decl = &G_anvecV[G_anvecC++]) = (G_andcl){};
M_strbufcpy(decl->name, name);
} else {
ins = false;
}
}
st->expect(tok_braceo, "'{'");
while(!st->drop(tok_bracec))
G_ObjDef_parseAnim(st, decl);
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")->text;)
{
if((decl = G_enttypes.find(name)) == NULL) {
M_Vec_grow(G_etvec, 1);
*(decl = &G_etvecV[G_etvecC++]) = (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")->text);
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->text, "anim") == 0)
G_ObjDef_getEntAnim(st, decl);
else
G_ObjDef_getEntProp(st, &decl->type, tok->text);
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_ofmap_ctor(&G_objfuncs, 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 = {.fp = fopen(fname, "r"), .fname = fname, .lines = 1};
if(!st.fp) {
fprintf(stderr, "ObjDef: couldn't open file '%s'\n", fname);
goto done;
}
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->text, "entity") == 0)
G_ObjDef_parseEntDef(&st);
else if(strcmp(tok->text, "anim") == 0)
G_ObjDef_parseAnimDef(&st);
else if(strcmp(tok->text, "frame") == 0)
G_ObjDef_parseFrameDef(&st);
else if(strcmp(tok->text, "include") == 0)
G_ObjDef_Load(st.expect(tok_string, "string")->text);
else
st.throw("unknown identifier in toplevel");
continue;
default:
st.throw("expected toplevel definition");
}
printf("ObjDef: '%s' loaded.\n", fname);
done:
fclose(st.fp);
}
//
// G_ObjDef_LoadFunc
//
void G_ObjDef_LoadFunc(char const *name, DGE_CallbackType fptr)
{
M_Vec_grow(G_ofvec, 1);
G_ofvecV[G_ofvecC] = (G_ofdcl){fptr, name, M_StrHash(name)};
G_objfuncs.insert(&G_ofvecV[G_ofvecC++]);
}
//
// 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_GetAnim
//
G_anima const *G_ObjDef_GetAnim(G_entty const *type, char const *name)
{
G_manim *anim = ((G_etdcl *)type)->anim.find(name);
return anim ? &anim->anim : NULL;
}
// EOF