// Copyright © 2017 Project Golan, all rights reserved. #include "g_object.h" #include "m_tokbuf.h" #include "m_str.h" #include "m_darray.h" #include #include #include #include #include #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); [[__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 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_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_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, 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 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 = (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")->textV); 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")->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)); free(bufV); 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_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)); 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); 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_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 = {.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_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(&M_Vec_next(G_ofvec)); } // // 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