+ /* modelgen / spiritgen commands */
+ if (ch == '$') {
+ const char *v;
+ size_t frame;
+
+ ch = lex_getch(lex);
+ if (!isident_start(ch)) {
+ lexerror(lex, "hanging '$' modelgen/spritegen command line");
+ return lex_do(lex);
+ }
+ if (!lex_tokench(lex, ch))
+ return (lex->tok->ttype = TOKEN_FATAL);
+ if (!lex_finish_ident(lex))
+ return (lex->tok->ttype = TOKEN_ERROR);
+ if (!lex_endtoken(lex))
+ return (lex->tok->ttype = TOKEN_FATAL);
+ /* skip the known commands */
+ v = lex->tok->value;
+
+ if (!strcmp(v, "frame") || !strcmp(v, "framesave"))
+ {
+ /* frame/framesave command works like an enum
+ * similar to fteqcc we handle this in the lexer.
+ * The reason for this is that it is sensitive to newlines,
+ * which the parser is unaware of
+ */
+ if (!lex_finish_frames(lex))
+ return (lex->tok->ttype = TOKEN_ERROR);
+ return lex_do(lex);
+ }
+
+ if (!strcmp(v, "framevalue"))
+ {
+ ch = lex_getch(lex);
+ while (ch != EOF && isspace(ch) && ch != '\n')
+ ch = lex_getch(lex);
+
+ if (!isdigit(ch)) {
+ lexerror(lex, "$framevalue requires an integer parameter");
+ return lex_do(lex);
+ }
+
+ token_delete(lex->tok);
+ lex->tok = token_new();
+ lex->tok->ttype = lex_finish_digit(lex, ch);
+ if (!lex_endtoken(lex))
+ return (lex->tok->ttype = TOKEN_FATAL);
+ if (lex->tok->ttype != TOKEN_INTCONST) {
+ lexerror(lex, "$framevalue requires an integer parameter");
+ return lex_do(lex);
+ }
+ lex->framevalue = lex->tok->constval.i;
+ return lex_do(lex);
+ }
+
+ if (!strcmp(v, "framerestore"))
+ {
+ int rc;
+
+ token_delete(lex->tok);
+ lex->tok = token_new();
+
+ rc = lex_parse_frame(lex);
+
+ if (rc > 0) {
+ lexerror(lex, "$framerestore requires a framename parameter");
+ return lex_do(lex);
+ }
+ if (rc < 0)
+ return (lex->tok->ttype = TOKEN_FATAL);
+
+ v = lex->tok->value;
+ for (frame = 0; frame < lex->frames_count; ++frame) {
+ if (!strcmp(v, lex->frames[frame].name)) {
+ lex->framevalue = lex->frames[frame].value;
+ return lex_do(lex);
+ }
+ }
+ lexerror(lex, "unknown framename `%s`", v);
+ return lex_do(lex);
+ }
+
+ if (!strcmp(v, "modelname"))
+ {
+ int rc;
+
+ token_delete(lex->tok);
+ lex->tok = token_new();
+
+ rc = lex_parse_frame(lex);
+
+ if (rc > 0) {
+ lexerror(lex, "$framerestore requires a framename parameter");
+ return lex_do(lex);
+ }
+ if (rc < 0)
+ return (lex->tok->ttype = TOKEN_FATAL);
+
+ v = lex->tok->value;
+ if (lex->modelname) {
+ frame_macro m;
+ m.value = lex->framevalue;
+ m.name = lex->modelname;
+ lex->modelname = NULL;
+ if (!lex_file_frames_add(lex, m)) {
+ lexerror(lex, "out of memory");
+ return (lex->tok->ttype = TOKEN_FATAL);
+ }
+ }
+ lex->modelname = lex->tok->value;
+ lex->tok->value = NULL;
+ for (frame = 0; frame < lex->frames_count; ++frame) {
+ if (!strcmp(v, lex->frames[frame].name)) {
+ lex->framevalue = lex->frames[frame].value;
+ break;
+ }
+ }
+ return lex_do(lex);
+ }
+
+ if (!strcmp(v, "flush"))
+ {
+ size_t frame;
+ for (frame = 0; frame < lex->frames_count; ++frame)
+ mem_d(lex->frames[frame].name);
+ MEM_VECTOR_CLEAR(lex, frames);
+ /* skip line (fteqcc does it too) */
+ ch = lex_getch(lex);
+ while (ch != EOF && ch != '\n')
+ ch = lex_getch(lex);
+ return lex_do(lex);
+ }
+
+ if (!strcmp(v, "cd") ||
+ !strcmp(v, "origin") ||
+ !strcmp(v, "base") ||
+ !strcmp(v, "flags") ||
+ !strcmp(v, "scale") ||
+ !strcmp(v, "skin"))
+ {
+ /* skip line */
+ ch = lex_getch(lex);
+ while (ch != EOF && ch != '\n')
+ ch = lex_getch(lex);
+ return lex_do(lex);
+ }
+
+ for (frame = 0; frame < lex->frames_count; ++frame) {
+ if (!strcmp(v, lex->frames[frame].name)) {
+ lex->tok->constval.i = lex->frames[frame].value;
+ return (lex->tok->ttype = TOKEN_INTCONST);
+ }
+ }
+
+ lexerror(lex, "invalid frame macro");
+ return lex_do(lex);
+ }
+