swfc initial revision.
authorkramm <kramm>
Thu, 6 Feb 2003 12:11:32 +0000 (12:11 +0000)
committerkramm <kramm>
Thu, 6 Feb 2003 12:11:32 +0000 (12:11 +0000)
src/parser.h [new file with mode: 0644]
src/parser.lex [new file with mode: 0644]
src/q.c [new file with mode: 0644]
src/q.h [new file with mode: 0644]
src/swfc.c [new file with mode: 0644]

diff --git a/src/parser.h b/src/parser.h
new file mode 100644 (file)
index 0000000..32d468f
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __parser_h__
+#define __parser_h__
+
+enum type_t {
+    TWIP=0, 
+    NUMBER=1,
+    COMMAND=2,
+    STRING=3,
+    ASSIGNMENT=4,
+    IDENTIFIER=5,
+    LABEL=6,
+    END=7
+};
+
+extern char*type_names[];
+
+struct token_t {
+    enum type_t type;
+    char* text;
+    int line;
+    int column;
+};
+
+extern struct token_t* generateTokens(char*filename);
+extern void freeTokens(struct token_t*);
+
+#endif
diff --git a/src/parser.lex b/src/parser.lex
new file mode 100644 (file)
index 0000000..f29926f
--- /dev/null
@@ -0,0 +1,193 @@
+%{
+
+#include <string.h>
+#include "q.h"
+#include "parser.h"
+
+//RVALUE        {NUMBER}|{PERCENT}|{NAME}|\"{STRING}\"|{DIM}
+//<a>.                {printf("<a>%s\n", yytext);}
+// %x: exclusive, %s: inclusive
+char*type_names[] = {"twip","number","command","string","assignment","identifier","label","end"};
+static int line=1;
+static int column=1;
+
+mem_t strings;
+mem_t tokens;
+
+static void count(char*text, int len, int condition)
+{
+    int t;
+    for(t=0;t<len;t++) {
+       if(text[t]=='\n') {
+           line++;
+           column=1;
+       } else {
+           column++;
+       }
+    }
+}
+
+static char*prefix = 0;
+
+static void store(enum type_t type, int line, int column, char*text, int length)
+{
+    struct token_t token;
+    string_t tmp;
+    token.type = type;
+    token.line = line;
+    token.column = column;
+    //printf("->%d(%s) %s\n", type, type_names[type], text);fflush(stdout);
+
+    token.text = 0;
+    switch(type) {
+       case END:
+           string_set2(&tmp, "", 0);
+           token.text = (char*)mem_putstring(&strings, tmp);
+       break;
+       case STRING:
+           string_set2(&tmp, text+1, length-2);
+           token.text = (char*)mem_putstring(&strings, tmp);
+       break;
+       case TWIP: 
+       case NUMBER: 
+       case IDENTIFIER:
+           string_set2(&tmp, text, length);
+           if(prefix) {
+               //strcat
+               token.text = (char*)mem_put(&strings, prefix, strlen(prefix));
+               mem_putstring(&strings, tmp);
+           } else {
+               token.text = (char*)mem_putstring(&strings, tmp);
+           }
+           prefix = 0;
+       break;
+       case LABEL:
+           string_set2(&tmp, text, length-1);
+           token.text = (char*)mem_putstring(&strings, tmp);
+       break;
+       case COMMAND:
+           string_set2(&tmp, text+1, length-1);
+           token.text = (char*)mem_putstring(&strings, tmp);
+       break;
+       case ASSIGNMENT: {
+           char*x = &text[length-1];
+           if(x[-1] == '-' || x[-1] == '+')
+               x--;
+           do{x--;} while(*x==32 || *x==10 || *x==13 || *x=='\t');
+           x++; //first space
+           string_set2(&tmp, text, x-text);
+           token.text = (char*)mem_putstring(&strings, tmp);
+           /*char*y,*x = strchr(text, '=');
+           if(!x) exit(1);
+           y=x;
+           do{y--;} while(*y==32 || *y==10 || *y==13 || *y=='\t');
+           do{x++;} while(*x==32 || *x==10 || *x==13 || *x=='\t');
+           token.text1 = (char*)put(&strings, text, y-text + 1, 1);
+           token.text2 = (char*)put(&strings, x, length-(x-text), 1);*/
+       } break;
+    }
+
+    mem_put(&tokens, &token, sizeof(struct token_t));
+    prefix = 0;
+}
+
+#define c() {count(yytext, yyleng, YY_START);}
+#define s(type) {store(type, line, column, yytext, yyleng);}
+%}
+
+%s R
+%x BINARY
+
+NAME    [a-zA-Z_./](-*[a-zA-Z0-9_./])*
+TWIP    ([0-9]+(\.([0-9]([05])?)?)?)
+NUMBER  [0-9]+(\.[0-9]*)?
+PERCENT         {NUMBER}%
+STRING   (\\.|[^\\"\n])*
+S       [ \n\r\t]
+RVALUE  \"{STRING}\"|([^ \n\r\t]+)
+
+%%
+
+<BINARY>\] {c();BEGIN(0);}
+<BINARY>.  {c();}
+<BINARY>\n {c();}
+{TWIP}/[ \n\r\t]            {s(TWIP);c();BEGIN(0);}
+{NUMBER}/[ \n\r\t]         {s(NUMBER);c();BEGIN(0);}
+^#[^\n]*\n                  {c();}
+[ \t\r]#[^\n]*\n            {c();}
+\"{STRING}\"               {s(STRING);c();BEGIN(0);}
+\"{STRING}$                {c();printf("unterminated string in line %d: %s\n", line, yytext);exit(1);yyterminate();}
+{NAME}{S}*\+=              {s(ASSIGNMENT);prefix="<plus>";c();BEGIN(R);}
+{NAME}{S}*-=               {s(ASSIGNMENT);prefix="<minus>";c();BEGIN(R);}
+{NAME}{S}*=                {s(ASSIGNMENT);c();BEGIN(R);}
+<R>{ /* values which appear only on the right-hand side of assignments, like: x=50% */
+    [^ \n\t\r]*                    {s(IDENTIFIER);c();BEGIN(0);}
+}
+\.{NAME}                   {s(COMMAND);c();}
+{NAME}{S}*:                 {s(LABEL);c();}
+{NAME}                      {s(IDENTIFIER);c();}
+"["                        {c();BEGIN(BINARY);}
+{S}                        {c();}
+.                          {char c,c1=0;
+                            printf("Syntax error in line %d, %d: %s", line, column, yytext);
+                            while(1) {
+                                c=input();
+                                if(!c1) c1=c;
+                                if(c=='\n' || c==EOF) 
+                                    break;
+                               printf("%c", c);
+                            }
+                            if(c1>='0' && c1<='9')
+                                printf(" (identifiers must not start with a digit)");
+                            printf("\n");
+                            exit(1);
+                            yyterminate();
+                           }
+<<EOF>>                            {c();s(END);yyterminate();}
+%%
+
+int yywrap()
+{
+    return 1;
+}
+
+void freeTokens(struct token_t*file)
+{
+    mem_clear(&strings);
+    mem_clear(&tokens);
+}
+
+struct token_t* generateTokens(char*filename)
+{
+    FILE*fi = fopen(filename, "rb");
+    int t;
+    struct token_t*result;
+    int num;
+    if(!fi) {
+       printf("Couldn't find file %s\n", filename);
+       return 0;
+    }
+    yyin = fi;
+
+    mem_init(&strings);
+    mem_init(&tokens);
+    mem_put(&strings, &t, 1); //hack- make all valid strings start at position >0
+
+    line=1;
+    column=1;
+
+    yylex();
+    yy_delete_buffer(yy_current_buffer);
+
+    result = (struct token_t*)tokens.buffer;
+    num = tokens.pos/sizeof(struct token_t);
+
+    for(t=0;t<tokens.pos/sizeof(struct token_t);t++) {
+       if(result[t].text)
+           result[t].text += (int)strings.buffer;
+    }
+
+    fclose(fi);
+    return result;
+}
+
diff --git a/src/q.c b/src/q.c
new file mode 100644 (file)
index 0000000..9142eec
--- /dev/null
+++ b/src/q.c
@@ -0,0 +1,358 @@
+/* q.c
+
+   Part of the swftools package.
+   
+   Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
+
+   This file is distributed under the GPL, see file COPYING for details */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include "q.h"
+
+// ------------------------------- malloc, alloc routines ---------------------
+
+#ifndef STRNDUP
+char* strndup(const char*str, int size)
+{
+    char*m = (char*)malloc(size+1);
+    memcpy(m, str, size);
+    m[size] = 0;
+    return m;
+}
+#endif
+void* qmalloc_internal(int len)
+{
+    void*val = malloc(len);
+    if(!val) {
+       printf("memory error! Couldn't reserve %d bytes\n", len);
+       fprintf(stderr, "memory error! Couldn't reserve %d bytes\n", len);
+       exit(1);
+    }
+    return val;
+}
+void* qrealloc_internal(void*old, int len)
+{
+    void*val = realloc(old, len);
+    if(!val) {
+       printf("memory error! Couldn't reserve %d bytes\n", len);
+       fprintf(stderr, "memory error! Couldn't reserve %d bytes\n", len);
+       exit(1);
+    }
+    return val;
+}
+void qfree_internal(void*old)
+{
+    free(old);
+}
+char*qstrdup(const char*string)
+{
+    return strdup(string);
+}
+char*qstrndup(const char*string, int len)
+{
+    return strndup(string, len);
+}
+
+// ------------------------------- mem_t --------------------------------------
+
+void mem_init(mem_t*mem)
+{
+    memset(mem, 0, sizeof(mem_t));
+}
+void mem_clear(mem_t*mem)
+{
+    free(mem->buffer);
+}
+void mem_destroy(mem_t*mem)
+{
+    mem_clear(mem);
+    free(mem);
+}
+static int mem_put_(mem_t*m,void*data, int length, int null)
+{
+    int n = m->pos;
+    m->pos += length + (null?1:0);
+    if(m->pos > m->len)
+    { 
+       //m->len += 1024>length?1024:(null?length*2:length);
+
+       m->len *= 2;
+       while(m->len < m->pos)
+           m->len += 64;
+
+       m->buffer = m->buffer?realloc(m->buffer,m->len):malloc(m->len);
+    }
+    memcpy(&m->buffer[n], data, length);
+    if(null)
+       m->buffer[n + length] = 0;
+    return n;
+}
+int mem_put(mem_t*m,void*data, int length)
+{
+    return mem_put_(m, data, length, 0);
+}
+int mem_putstring(mem_t*m,string_t str)
+{
+    return mem_put_(m, str.str, str.len, 1);
+}
+
+// ------------------------------- string_t ------------------------------------
+
+void string_set2(string_t*str, char*text, int len)
+{
+    str->len = len;
+    str->str = text;
+}
+void string_set(string_t*str, char*text)
+{
+    str->len = strlen(text);
+    str->str = text;
+}
+void string_dup2(string_t*str, const char*text, int len)
+{
+    str->len = len;
+    str->str = strndup(text, len);
+}
+void string_dup(string_t*str, const char*text)
+{
+    str->len = strlen(text);
+    str->str = strdup(text);
+}
+int string_equals(string_t*str, const char*text)
+{
+    int l = strlen(text);
+    if(str->len == l && !strncmp(str->str, text, l))
+       return 1;
+    return 0;
+}
+int string_equals2(string_t*str, string_t*str2)
+{
+    if(str->len == str2->len && !strncmp(str->str, str2->str, str->len))
+       return 1;
+    return 0;
+}
+char* string_cstr(string_t*str)
+{
+    return strndup(str->str, str->len);
+}
+
+// ------------------------------- stringarray_t ------------------------------
+
+typedef struct _stringarray_internal_t
+{
+    mem_t data;
+    mem_t pos;
+    int num;
+} stringarray_internal_t;
+void stringarray_init(stringarray_t*sa)
+{
+    stringarray_internal_t*s;
+    sa->internal = (stringarray_internal_t*)malloc(sizeof(stringarray_internal_t)); 
+    memset(sa->internal, 0, sizeof(stringarray_internal_t));
+    s = (stringarray_internal_t*)sa->internal;
+    mem_init(&s->data);
+    mem_init(&s->pos);
+}
+void stringarray_put(stringarray_t*sa, string_t str)
+{
+    stringarray_internal_t*s = (stringarray_internal_t*)sa->internal;
+    int pos;
+    pos = mem_putstring(&s->data, str);
+    mem_put(&s->pos, &pos, sizeof(int));
+    s->num++;
+}
+char* stringarray_at(stringarray_t*sa, int pos)
+{
+    stringarray_internal_t*s = (stringarray_internal_t*)sa->internal;
+    int p;
+    if(pos<0 || pos>=s->num)
+       return 0;
+    p = *(int*)&s->pos.buffer[pos*sizeof(int)];
+    if(p<0)
+       return 0;
+    return &s->data.buffer[p];
+}
+string_t stringarray_at2(stringarray_t*sa, int pos)
+{
+    string_t s;
+    s.str = stringarray_at(sa, pos);
+    s.len = s.str?strlen(s.str):0;
+    return s;
+}
+void stringarray_del(stringarray_t*sa, int pos)
+{
+    stringarray_internal_t*s = (stringarray_internal_t*)sa->internal;
+    *(int*)&s->pos.buffer[pos*sizeof(int)] = -1;
+}
+int stringarray_find(stringarray_t*sa, string_t* str)
+{
+    stringarray_internal_t*s = (stringarray_internal_t*)sa->internal;
+    int t;
+    for(t=0;t<s->num;t++) {
+       string_t s = stringarray_at2(sa, t);
+       if(s.str && string_equals2(&s, str)) {
+           return t;
+       }
+    }
+    return -1;
+}
+void stringarray_clear(stringarray_t*sa)
+{
+    stringarray_internal_t*s = (stringarray_internal_t*)sa->internal;
+    mem_clear(&s->data);
+    mem_clear(&s->pos);
+    free(s);
+}
+void stringarray_destroy(stringarray_t*sa)
+{
+    stringarray_clear(sa);
+    free(sa);
+}
+
+
+// ------------------------------- map_t --------------------------------------
+
+typedef struct _map_internal_t
+{
+    stringarray_t keys;
+    stringarray_t values;
+    int num;
+} map_internal_t;
+
+void map_init(map_t*map)
+{
+    map_internal_t*m;
+    map->internal = (map_internal_t*)malloc(sizeof(map_internal_t));
+    memset(map->internal, 0, sizeof(map_internal_t));
+    m = (map_internal_t*)map->internal;
+    stringarray_init(&m->keys);
+    stringarray_init(&m->values);
+}
+void map_put(map_t*map, string_t t1, string_t t2)
+{
+    map_internal_t*m = (map_internal_t*)map->internal;
+    stringarray_put(&m->keys, t1);
+    stringarray_put(&m->values, t2);
+    m->num++;
+}
+char* map_lookup(map_t*map, const char*name)
+{
+    int s;
+    map_internal_t*m = (map_internal_t*)map->internal;
+    string_t str;
+    string_set(&str, (char*)name);
+    s = stringarray_find(&m->keys, &str);
+    if(s>=0) {
+       string_t s2 = stringarray_at2(&m->values, s);
+       return s2.str;
+    }
+    return 0;
+}
+void map_dump(map_t*map, FILE*fi, const char*prefix)
+{
+    int t;
+    map_internal_t*m = (map_internal_t*)map->internal;
+    for(t=0;t<m->num;t++) {
+       string_t s1 = stringarray_at2(&m->keys, t);
+       string_t s2 = stringarray_at2(&m->values, t);
+       fprintf(fi, "%s%s=%s\n", prefix, s1.str, s2.str);
+    }
+}
+void map_clear(map_t*map)
+{
+    map_internal_t*m = (map_internal_t*)map->internal;
+    stringarray_clear(&m->keys);
+    stringarray_clear(&m->values);
+    free(m);
+}
+void map_destroy(map_t*map)
+{
+    map_clear(map);
+    free(map);
+}
+
+// ------------------------------- dictionary_t --------------------------------------
+
+typedef struct _dictionary_internal_t
+{
+    stringarray_t keys;
+    mem_t values;
+    int num;
+} dictionary_internal_t;
+
+void dictionary_init(dictionary_t*dict)
+{
+    dictionary_internal_t*d;
+    dict->internal = (dictionary_internal_t*)malloc(sizeof(dictionary_internal_t));
+    memset(dict->internal, 0, sizeof(dictionary_internal_t));
+    d = (dictionary_internal_t*)dict->internal;
+    stringarray_init(&d->keys);
+    mem_init(&d->values);
+}
+void dictionary_put(dictionary_t*dict, string_t t1, void* t2)
+{
+    dictionary_internal_t*d = (dictionary_internal_t*)dict->internal;
+    int s=0;
+    s = stringarray_find(&d->keys, &t1);
+    if(s>=0) {
+       /* replace */
+       *(void**)(&d->values.buffer[s*sizeof(void*)]) = t2;
+    } else {
+       stringarray_put(&d->keys, t1);
+       mem_put(&d->values, &t2, sizeof(void*));
+       d->num++;
+    }
+}
+void dictionary_put2(dictionary_t*dict, const char*t1, void* t2)
+{
+    string_t s;
+    string_set(&s, (char*)t1);
+    dictionary_put(dict, s, t2);
+}
+void* dictionary_lookup(dictionary_t*dict, const char*name)
+{
+    int s;
+    dictionary_internal_t*d = (dictionary_internal_t*)dict->internal;
+    string_t str;
+    string_set(&str, (char*)name);
+    s = stringarray_find(&d->keys, &str);
+    if(s>=0) {
+       return *(void**)&d->values.buffer[sizeof(void*)*s];
+    }
+    return 0;
+}
+void dictionary_dump(dictionary_t*dict, FILE*fi, const char*prefix)
+{
+    dictionary_internal_t*d = (dictionary_internal_t*)dict->internal;
+    int t;
+    for(t=0;t<d->num;t++) {
+       string_t s1 = stringarray_at2(&d->keys, t);
+       fprintf(fi, "%s%s=%08x\n", prefix, s1.str, *(void**)&d->values.buffer[sizeof(void*)*t]);
+    }
+}
+void dictionary_del(dictionary_t*dict, const char* name)
+{
+    dictionary_internal_t*d = (dictionary_internal_t*)dict->internal;
+    int s;
+    string_t str;
+    string_set(&str, (char*)name);
+    s = stringarray_find(&d->keys, &str);
+    if(s>=0) {
+       *(void**)(&d->values.buffer[s*sizeof(void*)]) = 0;
+       stringarray_del(&d->keys, s);
+    }
+}
+void dictionary_clear(dictionary_t*dict)
+{
+    dictionary_internal_t*d = (dictionary_internal_t*)dict->internal;
+    stringarray_clear(&d->keys);
+    mem_clear(&d->values);
+    free(d);
+}
+void dictionary_destroy(dictionary_t*dict)
+{
+    dictionary_clear(dict);
+    free(dict);
+}
diff --git a/src/q.h b/src/q.h
new file mode 100644 (file)
index 0000000..cffd63b
--- /dev/null
+++ b/src/q.h
@@ -0,0 +1,90 @@
+/* q.h
+   Header file for q.c.
+
+   Part of the swftools package.
+   
+   Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
+
+   This file is distributed under the GPL, see file COPYING for details */
+
+#ifndef __q_h__
+#define __q_h__
+
+#include <stdio.h>
+
+/* dynamically growing mem section */
+typedef struct _mem_t {
+    char*buffer;
+    int len;
+    int pos;
+} mem_t;
+
+/* non-nul terminated string */
+typedef struct _string_t {
+    char*str;
+    int len;
+} string_t;
+
+/* key/value pairs of strings */
+typedef struct _map_t {
+    void*internal;
+} map_t;
+
+/* (void*)s referenced by strings */
+typedef struct _dictionary_t {
+    void*internal;
+} dictionary_t;
+
+/* array of strings */
+typedef struct stringarray_t
+{
+    void*internal;
+} stringarray_t;
+
+void mem_init(mem_t*mem);
+int mem_put(mem_t*m, void*data, int length);
+int mem_putstring(mem_t*m, string_t str);
+void mem_clear(mem_t*mem);
+void mem_destroy(mem_t*mem);
+
+void string_set(string_t*str, char*text);
+void string_set2(string_t*str, char*text, int len);
+void string_dup(string_t*str, const char*text);
+void string_dup2(string_t*str, const char*text, int len);
+int string_equals(string_t*str, const char*text);
+
+void stringarray_init(stringarray_t*sa);
+void stringarray_put(stringarray_t*sa, string_t str);
+char* stringarray_at(stringarray_t*sa, int pos);
+string_t stringarray_at2(stringarray_t*sa, int pos);
+int stringarray_find(stringarray_t*sa, string_t*str);
+void stringarray_clear(stringarray_t*sa);
+void stringarray_destroy(stringarray_t*sa);
+
+void map_init(map_t*map);
+void map_put(map_t*map, string_t t1, string_t t2);
+char* map_lookup(map_t*map, const char*name);
+void map_dump(map_t*map, FILE*fi, const char*prefix);
+void map_clear(map_t*map);
+void map_destroy(map_t*map);
+
+void dictionary_init(dictionary_t*dict);
+void dictionary_put(dictionary_t*dict, string_t t1, void* t2);
+void dictionary_put2(dictionary_t*dict, const char* t1, void* t2);
+void* dictionary_lookup(dictionary_t*dict, const char*name);
+void dictionary_dump(dictionary_t*dict, FILE*fi, const char*prefix);
+void dictionary_del(dictionary_t*dict, const char* name);
+void dictionary_clear(dictionary_t*dict);
+void dictionary_destroy(dictionary_t*dict);
+
+char* strndup(const char*str, int size);
+
+void* qmalloc_internal(int len);
+void* qrealloc_internal(void*old, int len);
+void qfree_internal(void*old);
+
+#define qmalloc(len) qmalloc_internal(len)
+#define qrealloc(old, len) qmalloc_internal(old, len)
+#define qfree(old) qmalloc_internal(old)
+
+#endif //__q_h__
diff --git a/src/swfc.c b/src/swfc.c
new file mode 100644 (file)
index 0000000..c0e8ec1
--- /dev/null
@@ -0,0 +1,1861 @@
+/* swfc.c
+   Compiles swf code (.sc) files into .swf files.
+
+   Part of the swftools package.
+
+   Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
+
+   This file is distributed under the GPL, see file COPYING for details */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#define logf logarithmf // logf is also used by ../lib/log.h
+#include <math.h>
+#undef logf
+#include "../config.h"
+#include "../lib/rfxswf.h"
+#include "../lib/log.h"
+#include "../lib/args.h"
+#include "q.h"
+#include "parser.h"
+
+//#define DEBUG
+
+static char * filename = 0;
+static char * outputname = "output.swf";
+static int verbose = 2;
+
+static struct options_t options[] =
+{
+ {"o","output"},
+ {"v","verbose"},
+ {"V","version"},
+ {0,0}
+};
+    
+int args_callback_option(char*name,char*val)
+{
+    if(!strcmp(name, "V")) {
+        printf("swfc - part of %s %s\n", PACKAGE, VERSION);
+        exit(0);
+    }
+    else if(!strcmp(name, "o")) {
+       outputname = val;
+       return 1;
+    }
+    else if(!strcmp(name, "v")) {
+       verbose ++;
+       return 0;
+    }
+    else {
+        printf("Unknown option: -%s\n", name);
+       exit(1);
+    }
+    return 0;
+}
+int args_callback_longoption(char*name,char*val)
+{
+    return args_long2shortoption(options, name, val);
+}
+void args_callback_usage(char*name)
+{
+    printf("Usage: %s [-o filename] file.wav\n", name);
+    printf("\t-v , --verbose\t\t\t Be more verbose\n");
+    printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
+    printf("\t-V , --version\t\t\t Print program version and exit\n");
+}
+int args_callback_command(char*name,char*val)
+{
+    if(filename) {
+        fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
+                 filename, name);
+    }
+    filename = name;
+    return 0;
+}
+
+static struct token_t* file;
+
+static int pos;
+static char*text;
+static int textlen;
+static int type;
+static int line;
+static int column;
+
+static void syntaxerror(char*format, ...)
+{
+    char buf[1024];
+    va_list arglist;
+    va_start(arglist, format);
+    vsprintf(buf, format, arglist);
+    va_end(arglist);
+    printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
+    exit(1);
+}
+
+static void warning(char*format, ...)
+{
+    char buf[1024];
+    va_list arglist;
+    va_start(arglist, format);
+    vsprintf(buf, format, arglist);
+    va_end(arglist);
+    printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
+}
+   
+static void readToken()
+{
+    type = file[pos].type;
+    if(type == END) {
+       syntaxerror("unexpected end of file");
+    }
+    text = file[pos].text;
+    textlen = strlen(text);
+    line = file[pos].line;
+    column = file[pos].column;
+    pos++;
+    //printf("---> %d(%s) %s\n", type, type_names[type], text);
+}
+
+static void pushBack()
+{
+    int p;
+    if(!pos) syntaxerror("internal error 3");
+    pos--;
+    p = pos;
+    if(p) p--;
+    text = file[p].text;
+    textlen = strlen(text);
+    type = file[p].type;
+    line = file[p].line;
+    column = file[p].column;
+}
+
+static int noMoreTokens()
+{
+    if(file[pos].type == END)
+       return 1;
+    return 0;
+}
+
+// ------------------------------ swf routines ----------------------------
+struct _character;
+static struct level
+{
+   int type; //0=swf, 1=sprite, 2=clip
+
+   /* for swf (0): */
+   SWF*swf;
+   char*filename; 
+
+   /* for sprites (1): */
+   TAG*tag;
+   U16 id;
+   char*name;
+   U16 olddepth;
+   int oldframe;
+   dictionary_t oldinstances;
+   SRECT oldrect;
+
+} stack[256];
+static int stackpos = 0;
+
+static dictionary_t characters;
+static char idmap[65536];
+static TAG*tag = 0; //current tag
+
+static int id; //current character id
+static int currentframe; //current frame in current level
+static SRECT currentrect; //current bounding box in current level
+static U16 currentdepth;
+static dictionary_t instances;
+static dictionary_t fonts;
+
+typedef struct _parameters {
+    int x,y; 
+    float scalex, scaley; 
+    CXFORM cxform;
+    float rotate;
+    SPOINT pivot;
+    SPOINT pin;
+} parameters_t;
+
+typedef struct _character {
+    TAG*definingTag;
+    U16 id;
+    SRECT size;
+} character_t;
+
+typedef struct _instance {
+    character_t*character;
+    U16 depth;
+    parameters_t parameters;
+    TAG* lastTag; //last tag which set the object
+    U16 lastFrame; //frame lastTag is in
+} instance_t;
+
+static void character_init(character_t*c)
+{
+    memset(c, 0, sizeof(character_t));
+}
+static character_t* character_new()
+{
+    character_t*c;
+    c = (character_t*)malloc(sizeof(character_t));
+    character_init(c);
+    return c;
+}
+static void instance_init(instance_t*i)
+{
+    memset(i, 0, sizeof(instance_t));
+}
+static instance_t* instance_new()
+{
+    instance_t*c;
+    c = (instance_t*)malloc(sizeof(instance_t));
+    instance_init(c);
+    return c;
+}
+
+static void incrementid()
+{
+    while(idmap[++id]) {
+       if(id==65535)
+           syntaxerror("Out of character ids.");
+    }
+    idmap[id] = 1;
+}
+
+static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
+{
+    character_t* c = character_new();
+    
+    c->definingTag = ctag;
+    c->id = id;
+    c->size = r;
+    if(dictionary_lookup(&characters, name))
+       syntaxerror("character %s defined twice", name);
+    dictionary_put2(&characters, name, c);
+
+    tag = swf_InsertTag(tag, ST_NAMECHARACTER);
+    swf_SetU16(tag, id);
+    swf_SetString(tag, name);
+    tag = swf_InsertTag(tag, ST_EXPORTASSETS);
+    swf_SetU16(tag, 1);
+    swf_SetU16(tag, id);
+    swf_SetString(tag, name);
+}
+static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
+{
+    instance_t* i = instance_new();
+    i->character = c;
+    i->depth = depth;
+    //swf_GetMatrix(0, &i->matrix);
+    if(dictionary_lookup(&instances, name))
+       syntaxerror("object %s defined twice", name);
+    dictionary_put2(&instances, name, i);
+    return i;
+}
+
+static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, SPOINT pivot, SPOINT pin, CXFORM cxform)
+{
+    p->x = x; p->y = y; 
+    p->scalex = scalex; p->scaley = scaley;
+    p->pin    = pin; p->pivot = pivot;
+    p->rotate = rotate; p->cxform = cxform;
+}
+
+static void parameters_clear(parameters_t*p)
+{
+    p->x = 0; p->y = 0; 
+    p->scalex = 1.0; p->scaley = 1.0;
+    p->pin.x = 1; p->pin.y = 0;
+    p->pivot.x = 0; p->pivot.y = 0;
+    p->rotate = 0; 
+    swf_GetCXForm(0, &p->cxform, 1);
+}
+
+static void makeMatrix(MATRIX*m, parameters_t*p)
+{
+    SPOINT h;
+    m->sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979)*65535;
+    m->r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)*65535;
+    m->r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979)*65535;
+    m->sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)*65535;
+    m->tx = m->ty = 0;
+
+    h = swf_TurnPoint(p->pin, m);
+    m->tx = p->x - h.x;
+    m->ty = p->y - h.y;
+}
+
+static MATRIX s_instancepos(instance_t*i, parameters_t*p)
+{
+    MATRIX m;
+    SRECT r;
+    makeMatrix(&m, p);
+    r = swf_TurnRect(i->character->size, &m);
+    if(currentrect.xmin == 0 && currentrect.ymin == 0 && 
+       currentrect.xmax == 0 && currentrect.ymax == 0)
+       currentrect = r;
+    else
+       swf_ExpandRect2(&currentrect, &r);
+    return m;
+}
+
+void s_swf(char*name, SRECT r, int version, int fps, int compress)
+{
+    SWF*swf = (SWF*)malloc(sizeof(SWF));
+    RGBA rgb;
+
+    if(stackpos)
+       syntaxerror(".swf blocks can't be nested");
+
+    memset(swf, 0, sizeof(swf));
+    swf->fileVersion = version;
+    swf->movieSize = r;
+    swf->frameRate = fps;
+    swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
+    swf->compressed = compress;
+    rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
+    swf_SetRGB(tag,&rgb);
+    
+    if(stackpos==sizeof(stack)/sizeof(stack[0]))
+       syntaxerror("too many levels of recursion");
+    
+    dictionary_init(&characters);
+    dictionary_init(&instances);
+    dictionary_init(&fonts);
+
+    memset(&stack[stackpos], 0, sizeof(stack[0]));
+    stack[stackpos].type = 0;
+    stack[stackpos].filename = strdup(name);
+    stack[stackpos].swf = swf;
+    stack[stackpos].oldframe = -1;
+    stackpos++;
+    id = 0;
+
+    currentframe = 0;
+    memset(&currentrect, 0, sizeof(currentrect));
+    currentdepth = 1;
+    
+    memset(idmap, 0, sizeof(idmap));
+    incrementid();
+}
+
+void s_sprite(char*name)
+{
+    tag = swf_InsertTag(tag, ST_DEFINESPRITE);
+    swf_SetU16(tag, id); //id
+    swf_SetU16(tag, 0); //frames
+
+    memset(&stack[stackpos], 0, sizeof(stack[0]));
+    stack[stackpos].type = 1;
+    stack[stackpos].oldframe = currentframe;
+    stack[stackpos].olddepth = currentdepth;
+    stack[stackpos].oldrect = currentrect;
+    stack[stackpos].oldinstances = instances;
+    stack[stackpos].tag = tag;
+    stack[stackpos].id = id;
+    stack[stackpos].name = strdup(name);
+   
+    /* FIXME: those four fields should be bundled together */
+    dictionary_init(&instances);
+    currentframe = 0;
+    currentdepth = 1;
+    memset(&currentrect, 0, sizeof(currentrect));
+
+    stackpos++;
+    incrementid();
+}
+
+static void s_endSprite()
+{
+    SRECT r = currentrect;
+    stackpos--;
+   
+    /* TODO: before clearing, prepend "<spritename>." to names and
+             copy into old instances dict */
+    dictionary_clear(&instances);
+
+    currentframe = stack[stackpos].oldframe;
+    currentrect = stack[stackpos].oldrect;
+    currentdepth = stack[stackpos].olddepth;
+    instances = stack[stackpos].oldinstances;
+    tag = swf_InsertTag(tag, ST_END);
+
+    tag = stack[stackpos].tag;
+    swf_FoldSprite(tag);
+    if(tag->next != 0)
+       syntaxerror("internal error(7)");
+
+    s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
+    free(stack[stackpos].name);
+}
+
+static void s_endSWF()
+{
+    int fi;
+    SWF* swf;
+    char*filename;
+    stackpos--;
+
+    swf = stack[stackpos].swf;
+    filename = stack[stackpos].filename;
+    
+    tag = swf_InsertTag(tag, ST_SHOWFRAME);
+    tag = swf_InsertTag(tag, ST_END);
+
+    swf_OptimizeTagOrder(swf);
+
+    if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
+       swf->movieSize = currentrect; /* "autocrop" */
+
+    fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+    if(fi<0) {
+       syntaxerror("couldn't create output file %s", filename);
+    }
+    if(swf->compressed) 
+       {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
+    else
+       {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
+
+    close(fi);
+    
+    dictionary_clear(&instances);
+    dictionary_clear(&characters);
+    dictionary_clear(&fonts);
+
+    swf_FreeTags(swf);
+    free(swf);
+    free(filename);
+}
+void s_close()
+{
+    if(stackpos) {
+       if(stack[stackpos-1].type == 0)
+           syntaxerror("End of file encountered in .swf block");
+       if(stack[stackpos-1].type == 1)
+           syntaxerror("End of file encountered in .sprite block");
+       if(stack[stackpos-1].type == 2)
+           syntaxerror("End of file encountered in .clip block");
+    }
+}
+
+int s_getframe()
+{
+    return currentframe;
+}
+
+void s_frame(int nr)
+{
+    int t;
+    for(t=currentframe;t<nr;t++) {
+       tag = swf_InsertTag(tag, ST_SHOWFRAME);
+    }
+    currentframe = nr;
+}
+       
+RGBA black={r:0,g:0,b:0,a:0};
+void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill)
+{
+    SRECT r;
+    SHAPE* s;
+    int ls1,fs1=0;
+    tag = swf_InsertTag(tag, ST_DEFINESHAPE);
+    swf_ShapeNew(&s);
+    ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    if(dofill)
+       fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
+    swf_SetU16(tag,id);
+    r.xmin = -linewidth-linewidth/2;
+    r.ymin = -linewidth-linewidth/2;
+    r.xmax = width+linewidth+linewidth/2;
+    r.ymax = height+linewidth+linewidth/2;
+    swf_SetRect(tag,&r);
+    swf_SetShapeHeader(tag,s);
+    swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
+    swf_ShapeSetLine(tag,s,width,0);
+    swf_ShapeSetLine(tag,s,0,height);
+    swf_ShapeSetLine(tag,s,-width,0);
+    swf_ShapeSetLine(tag,s,0,-height);
+    swf_ShapeSetEnd(tag);
+    swf_ShapeFree(s);
+   
+    s_addcharacter(name, id, tag, r);
+    incrementid();
+}
+
+void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill)
+{
+    SRECT rect;
+    SHAPE* s;
+    int ls1,fs1=0;
+    tag = swf_InsertTag(tag, ST_DEFINESHAPE);
+    swf_ShapeNew(&s);
+    ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    if(dofill)
+       fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
+    swf_SetU16(tag,id);
+    rect.xmin = -linewidth-linewidth/2;
+    rect.ymin = -linewidth-linewidth/2;
+    rect.xmax = 2*r+linewidth+linewidth/2;
+    rect.ymax = 2*r+linewidth+linewidth/2;
+
+    swf_SetRect(tag,&rect);
+    swf_SetShapeHeader(tag,s);
+    swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
+    swf_ShapeSetCircle(tag, s, r,r,r,r);
+    swf_ShapeSetEnd(tag);
+    swf_ShapeFree(s);
+   
+    s_addcharacter(name, id, tag, rect);
+    incrementid();
+}
+
+void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill)
+{
+    SRECT rect;
+    SHAPE* s;
+    int ls1,fs1=0;
+    int g;
+    U8*text = (U8*)_text;
+
+    SWFFONT*font;
+    font = dictionary_lookup(&fonts, fontname);
+    if(!font)
+       syntaxerror("font \"%s\" not known!", fontname);
+    if(!dofill)
+       syntaxerror("textshapes must be filled", fontname);
+
+    if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
+       warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
+       s_box(name, 0, 0, black, 20, black, 0);
+       return;
+    }
+    g = font->ascii2glyph[text[0]];
+    rect = font->layout->bounds[g];
+
+    swf_ShapeNew(&s);
+    ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    if(dofill)
+       fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
+     
+    tag = swf_InsertTag(tag, ST_DEFINESHAPE);
+    swf_SetU16(tag,id);
+    swf_SetRect(tag, &rect);
+    swf_SetShapeStyles(tag, s);
+    swf_SetSimpleShape(tag, font->glyph[g].shape);
+    swf_ShapeFree(s);
+    
+    s_addcharacter(name, id, tag, rect);
+    incrementid();
+}
+void s_text(char*name, char*fontname, char*text, int size, RGBA color)
+{
+    SRECT r;
+
+    SWFFONT*font;
+    font = dictionary_lookup(&fonts, fontname);
+    if(!font)
+       syntaxerror("font \"%s\" not known!", fontname);
+    
+    tag = swf_InsertTag(tag, ST_DEFINETEXT2);
+    swf_SetU16(tag, id);
+    if(!font->numchars) {
+       s_box(name, 0, 0, black, 20, black, 0);
+       return;
+    }
+    r = swf_SetDefineText(tag, font, &color, text, size);
+   
+    s_addcharacter(name, id, tag, r);
+    incrementid();
+}
+
+void dumpSWF(SWF*swf)
+{
+    TAG* tag = swf->firstTag;
+    printf("vvvvvvvvvvvvvvvvvvvvv\n");
+    while(tag) {
+       printf("%8d %s\n", tag->len, swf_TagGetName(tag));
+       tag = tag->next;
+    }
+    printf("^^^^^^^^^^^^^^^^^^^^^\n");
+}
+    
+void s_font(char*name, char*filename)
+{
+    int f;
+    SWF swf;
+    SWFFONT* font;
+    f = open(filename,O_RDONLY);
+    if (f<0) { 
+       warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
+       font = (SWFFONT*)malloc(sizeof(SWFFONT));
+       memset(font, 0, sizeof(SWFFONT));
+       dictionary_put2(&fonts, name, font);
+       return;
+    }
+    font = 0;
+    if (swf_ReadSWF(f,&swf)>=0) { 
+       swf_FontExtract(&swf, 0x4e46, &font);
+       swf_FreeTags(&swf);
+    }
+    close(f);
+    if (font==0) { 
+       syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
+    }
+
+    if(0)
+    {
+       /* fix the layout. Only needed for old fonts */
+       int t;
+       for(t=0;t<font->numchars;t++) {
+           font->glyph[t].advance = 0;
+       }
+       font->layout = 0;
+       swf_FontCreateLayout(font);
+    }
+
+    font->id = id;
+    tag = swf_InsertTag(tag, ST_DEFINEFONT2);
+    swf_FontSetDefine2(tag, font);
+    incrementid();
+
+    if(dictionary_lookup(&fonts, name))
+       syntaxerror("font %s defined twice", name);
+    dictionary_put2(&fonts, name, font);
+}
+
+void s_shape(char*name, char*filename)
+{
+    int f;
+    SWF swf;
+    TAG* ftag;
+    SRECT r;
+    TAG* s;
+    int level = 0;
+    U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
+    f = open(filename,O_RDONLY);
+    if (f<0) { 
+       warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
+       s_box(name, 0, 0, black, 20, black, 0);
+       return;
+    }
+    if (swf_ReadSWF(f,&swf)<0) { 
+       warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
+       s_box(name, 0, 0, black, 20, black, 0);
+       return;
+    }
+    close(f);
+
+    /* FIXME: The following sets the bounding Box for the character. 
+              It is wrong for two reasons:
+             a) It may be too small (in case objects in the movie clip at the borders)
+             b) it may be too big (because the poor movie never got autocropped)
+    */
+    r = swf.movieSize;
+    
+    s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
+    swf_SetU16(tag, id);
+    swf_SetU16(tag, 0);
+
+    swf_Relocate(&swf, idmap);
+
+    ftag = swf.firstTag;
+    level = 1;
+    while(ftag) {
+       int t;
+       for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
+           if(cutout[t] == ftag->id) {
+               ftag = ftag->next;
+               continue;
+           }
+       if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
+           level++;
+       if(ftag->id == ST_END)
+           level--;
+       if(!level)
+           break;
+       /* We simply dump all tags right after the sprite
+          header, relying on the fact that swf_OptimizeTagOrder() will
+          sort things out for us later. 
+          We also rely on the fact that the imported SWF is well-formed.
+        */
+       tag = swf_InsertTag(tag, ftag->id);
+       swf_SetBlock(tag, ftag->data, ftag->len);
+       ftag = ftag->next;
+    }
+    if(!ftag)
+       syntaxerror("Included file %s contains errors", filename);
+    tag = swf_InsertTag(tag, ST_END);
+
+    swf_FreeTags(&swf);
+
+    s_addcharacter(name, id, tag, r);
+    incrementid();
+}
+SRECT s_getCharBBox(char*name)
+{
+    character_t* c = dictionary_lookup(&characters, name);
+    if(!c) syntaxerror("character '%s' unknown(2)", name);
+    return c->size;
+}
+SRECT s_getInstanceBBox(char*name)
+{
+    instance_t * i = dictionary_lookup(&instances, name);
+    character_t * c;
+    if(!i) syntaxerror("instance '%s' unknown(4)", name);
+    c = i->character;
+    if(!c) syntaxerror("internal error(5)");
+    return c->size;
+}
+parameters_t s_getParameters(char*name)
+{
+    instance_t * i = dictionary_lookup(&instances, name);
+    if(!i) syntaxerror("instance '%s' unknown(10)", name);
+    return i->parameters;
+}
+void s_startclip(char*instance, char*character, parameters_t p)
+{
+    character_t* c = dictionary_lookup(&characters, character);
+    instance_t* i;
+    MATRIX m;
+    if(!c) {
+       syntaxerror("character %s not known", character);
+    }
+    i = s_addinstance(instance, c, currentdepth);
+    i->parameters = p;
+    m = s_instancepos(i, &p);
+    
+    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+    /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
+    swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
+    i->lastTag = tag;
+    i->lastFrame= currentframe;
+
+    stack[stackpos].tag = tag;
+    stack[stackpos].type = 2;
+    stackpos++;
+
+    currentdepth++;
+}
+void s_endClip()
+{
+    SWFPLACEOBJECT p;
+    stackpos--;
+    swf_SetTagPos(stack[stackpos].tag, 0);
+    swf_GetPlaceObject(stack[stackpos].tag, &p);
+    p.clipdepth = currentdepth;
+    swf_ClearTag(stack[stackpos].tag);
+    swf_SetPlaceObject(stack[stackpos].tag, &p);
+    currentdepth++;
+}
+
+void s_put(char*instance, char*character, parameters_t p)
+{
+    character_t* c = dictionary_lookup(&characters, character);
+    instance_t* i;
+    MATRIX m;
+    if(!c) {
+       syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
+    }
+    
+    i = s_addinstance(instance, c, currentdepth);
+    i->parameters = p;
+    m = s_instancepos(i, &p);
+    
+    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+    swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
+    i->lastTag = tag;
+    i->lastFrame = currentframe;
+    currentdepth++;
+}
+
+void s_jump(char*instance, parameters_t p)
+{
+    instance_t* i = dictionary_lookup(&instances, instance);
+    MATRIX m;
+    if(!i) {
+       syntaxerror("instance %s not known", instance);
+    }
+
+    i->parameters = p;
+    m = s_instancepos(i, &p);
+
+    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+    swf_ObjectMove(tag, i->depth, &m, &p.cxform);
+    i->lastTag = tag;
+    i->lastFrame = currentframe;
+}
+
+parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
+{
+    parameters_t p;
+    float ratio;
+    if(num==0 || num==1)
+       return *p1;
+    ratio = (float)pos/(float)num;
+    
+    p.x = (p2->x-p1->x)*ratio + p1->x;
+    p.y = (p2->y-p1->y)*ratio + p1->y;
+    p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
+    p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
+    p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
+
+    p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
+    p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
+    p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
+    p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
+
+    p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
+    p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
+    p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
+    p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
+
+    p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
+    p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
+    p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
+    p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
+    return p;
+}
+
+void s_change(char*instance, parameters_t p2)
+{
+    instance_t* i = dictionary_lookup(&instances, instance);
+    MATRIX m;
+    parameters_t p1;
+    TAG*t;
+    int frame, allframes;
+    if(!i) {
+       syntaxerror("instance %s not known", instance);
+    }
+    p1 = i->parameters;
+    
+    allframes = currentframe - i->lastFrame - 1;
+    if(!allframes) {
+       warning(".change ignored. can only .put/.change an object once per frame.");
+       return;
+    }
+    
+    m = s_instancepos(i, &p2);
+    tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+    swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
+    i->parameters = p2;
+
+    /* o.k., we got the start and end point set. Now iterate though all the
+       tags in between, inserting object changes after each new frame */
+    t = i->lastTag;
+    i->lastTag = tag;
+    if(!t) syntaxerror("internal error(6)");
+    frame = 0;
+    while(frame < allframes) {
+       if(t->id == ST_SHOWFRAME) {
+           parameters_t p;
+           MATRIX m;
+           TAG*lt;
+           frame ++;
+           p = s_interpolate(&p1, &p2, frame, allframes);
+           m = s_instancepos(i, &p); //needed?
+           lt = swf_InsertTag(t, ST_PLACEOBJECT2);
+           i->lastFrame = currentframe;
+           swf_ObjectMove(lt, i->depth, &m, &p.cxform);
+           t = lt;
+           if(frame == allframes)
+               break;
+       }
+       t = t->next;
+       if(!t) 
+           syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
+    }
+}
+
+void s_delinstance(char*instance)
+{
+    instance_t* i = dictionary_lookup(&instances, instance);
+    if(!i) {
+       syntaxerror("instance %s not known", instance);
+    }
+    tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
+    swf_SetU16(tag, i->depth);
+    dictionary_del(&instances, instance);
+}
+
+void s_qchange(char*instance, parameters_t p)
+{
+}
+
+void s_end()
+{
+    if(!stackpos)
+       syntaxerror(".end unexpected");
+    if(stack[stackpos-1].type == 0)
+       s_endSWF();
+    else if(stack[stackpos-1].type == 1)
+       s_endSprite();
+    else if(stack[stackpos-1].type == 2)
+       s_endClip();
+    else syntaxerror("internal error 1");
+}
+
+// ------------------------------------------------------------------------
+
+typedef int command_func_t(map_t*args);
+
+SRECT parseBox(char*str)
+{
+    SRECT r;
+    float xmin, xmax, ymin, ymax;
+    char*x = strchr(str, 'x');
+    char*d1=0,*d2=0;
+    if(!strcmp(str, "autocrop")) {
+       r.xmin = r.ymin = r.xmax = r.ymax = 0;
+       return r;
+    }
+    if(!x) goto error;
+    d1 = strchr(x+1, ':');
+    if(d1)
+       d2 = strchr(d1+1, ':');
+    if(!d1) {
+       if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
+           goto error;
+       xmin = ymin = 0;
+    }
+    else if(d1 && !d2) {
+       if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
+           goto error;
+       xmax += xmin;
+       ymin = 0;
+    }
+    else {
+       if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
+           goto error;
+       xmax += xmin;
+       ymax += ymin;
+    }
+    r.xmin = (SCOORD)(xmin*20);
+    r.ymin = (SCOORD)(ymin*20);
+    r.xmax = (SCOORD)(xmax*20);
+    r.ymax = (SCOORD)(ymax*20);
+    return r;
+error:
+    syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
+    return r;
+}
+float parseFloat(char*str)
+{
+    return atof(str);
+}
+int parseInt(char*str)
+{
+    int t;
+    int l=strlen(str);
+    int s=0;
+    if(str[0]=='+' || str[0]=='-')
+       s++;
+
+    for(t=s;t<l;t++)
+       if(str[t]<'0' || str[t]>'9')
+           syntaxerror("Not an Integer: \"%s\"", str);
+    return atoi(str);
+}
+int parseTwip(char*str)
+{
+    char*dot = strchr(str, '.');
+    if(!dot) {
+       int l=strlen(str);
+       int t;
+       return parseInt(str)*20;
+    } else {
+       int l=strlen(++dot);
+       char*s;
+       for(s=str;s<dot-1;s++)
+           if(*s<'0' || *s>'9')
+               syntaxerror("Not a coordinate: \"%s\"", str);
+       for(s=dot;*s;s++) {
+           if(*s<'0' || *s>'9')
+               syntaxerror("Not a coordinate: \"%s\"", str);
+       }
+       if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
+           warning("precision loss: %s converted to twip", str);
+           dot[2] = 0;
+           l=2;
+       }
+       if(l==0)
+           return atoi(str)*20;
+       if(l==1)
+           return atoi(str)*20+atoi(dot)*2;
+       if(l==2)
+           return atoi(str)*20+atoi(dot)/5;
+    }
+    return 0;
+}
+
+int isPoint(char*str)
+{
+    if(strchr(str, '('))
+       return 1;
+    else
+       return 0;
+}
+
+SPOINT parsePoint(char*str)
+{
+    SPOINT p;
+    char tmp[80];
+    int l = strlen(str);
+    char*comma = strchr(str, ',');
+    if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
+       syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
+    strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
+    p.x = parseTwip(tmp);
+    strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
+    p.y = parseTwip(tmp);
+    return p;
+}
+
+int parseColor2(char*str, RGBA*color)
+{
+    int l = strlen(str);
+    int r,g,b,a;
+    int t;
+    char*names[8] = {"black", "blue", "green", "cyan",
+                    "red", "violet", "yellow", "white"};
+    a=255;
+    r=g=b=0;
+    if(str[0]=='#' && (l==7 || l==9)) {
+       if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
+           return 0;
+       if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
+           return 0;
+       color->r = r; color->g = g; color->b = b; color->a = a;
+       return 1;
+    }
+    for(t=0;t<8;t++)
+       if(!strcmp(str, names[t])) {
+           if(t&1)
+               b = 255;
+           if(t&2)
+               g = 255;
+           if(t&4)
+               r = 255;
+           color->r = r; color->g = g; color->b = b; color->a = a;
+           return 1;
+       }
+    return 0;
+
+}
+RGBA parseColor(char*str)
+{
+    RGBA c;
+    if(!parseColor2(str, &c))
+       syntaxerror("Expression '%s' is not a color", str);
+    return c;
+}
+
+typedef struct _muladd {
+    S16 mul;
+    S16 add;
+} MULADD;
+
+MULADD parseMulAdd(char*str)
+{
+    float add, mul;
+    char* str2 = (char*)malloc(strlen(str)+5);
+    int i;
+    MULADD m;
+    strcpy(str2, str);
+    strcat(str2, " 0");
+    add = 0;
+    mul = 1.0;
+    if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
+    else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
+    else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
+    else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
+    else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
+    else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
+    else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
+    else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
+    else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
+    else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
+    else {
+       syntaxerror("'%s' is not a valid color transform expression", str);
+    }
+    m.add = (int)(add*256);
+    m.mul = (int)(mul*256);
+    free(str2);
+    return m;
+}
+
+MULADD mergeMulAdd(MULADD m1, MULADD m2)
+{
+    int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
+    double m = ((double)m1.mul*(double)m2.mul)/256.0;
+    MULADD r;
+    if(a<-32768) a=-32768;
+    if(a>32767) a=32767;
+    if(m<-32768) m=-32768;
+    if(m>32767) m=32767;
+    r.add = a;
+    r.mul = (int)m;
+    return r;
+}
+
+float parsePercent(char*str)
+{
+    int l = strlen(str);
+    if(!l)
+       return 1.0;
+    if(str[l-1]=='%') {
+       return atoi(str)/100.0;
+    }
+    syntaxerror("Expression '%s' is not a percentage", str);
+    return 0;
+}
+int isPercent(char*str)
+{
+    return str[strlen(str)-1]=='%';
+}
+int parseNewSize(char*str, int size)
+{
+    if(isPercent(str))
+       return parsePercent(str)*size;
+    else
+       return (int)(atof(str)*20);
+}
+
+int isColor(char*str)
+{
+    RGBA c;
+    return parseColor2(str, &c);
+}
+
+static char* lu(map_t* args, char*name)
+{
+    char* value = map_lookup(args, name);
+    if(!value) {
+       syntaxerror("internal error 2: value %s should be set", name);
+    }
+    return value;
+}
+
+static int c_swf(map_t*args) 
+{
+    char* name = lu(args, "name");
+    char* compressstr = lu(args, "compress");
+    SRECT bbox = parseBox(lu(args, "bbox"));
+    int version = parseInt(lu(args, "version"));
+    int fps = (int)(parseFloat(lu(args, "fps"))*256);
+    int compress = 0;
+    if(!strcmp(name, "!default!"))
+       name = outputname;
+    
+    if(!strcmp(compressstr, "default"))
+       compress = version==6;
+    else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
+       compress = 1;
+    else if(!strcmp(compressstr, "no"))
+       compress = 0;
+    else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
+
+    s_swf(name, bbox, version, fps, compress);
+    return 0;
+}
+int isRelative(char*str)
+{
+    return !strncmp(str, "<plus>", 6) ||
+          !strncmp(str, "<minus>", 7);
+}
+char* getOffset(char*str)
+{
+    if(!strncmp(str, "<plus>", 6))
+       return str+6;
+    if(!strncmp(str, "<minus>", 7))
+       return str+7;
+    syntaxerror("internal error (347)");
+    return 0;
+}
+int getSign(char*str)
+{
+    if(!strncmp(str, "<plus>", 6))
+       return 1;
+    if(!strncmp(str, "<minus>", 7))
+       return -1;
+    syntaxerror("internal error (348)");
+    return 0;
+}
+static dictionary_t points;
+static mem_t mpoints;
+int points_initialized = 0;
+
+SPOINT getPoint(SRECT r, char*name)
+{
+    int l;
+    if(!strcmp(name, "center")) {
+       SPOINT p;
+       p.x = (r.xmin + r.xmax)/2;
+       p.y = (r.ymin + r.ymax)/2;
+       return p;
+    }
+
+    l = (int)dictionary_lookup(&points, name);
+    if(l==0) {
+       syntaxerror("Couldn't find point \"%s\".", name);
+    }
+    l--;
+    return *(SPOINT*)&mpoints.buffer[l];
+}
+static int c_point(map_t*args) 
+{
+    char*name = lu(args, "name");
+    int pos;
+    string_t s1;
+    SPOINT p;
+    if(!points_initialized) {
+       dictionary_init(&points);
+       mem_init(&mpoints);
+       points_initialized = 1;
+    }
+    p.x = parseTwip(lu(args, "x"));
+    p.y = parseTwip(lu(args, "y"));
+    pos = mem_put(&mpoints, &p, sizeof(p));
+    string_set(&s1, name);
+    pos++;
+    dictionary_put(&points, s1, (void*)pos);
+    return 0;
+}
+static int c_placement(map_t*args, int type)
+{
+    char*instance = lu(args, (type==0||type==4)?"instance":"name");
+    char*character = 0;
+
+    char* luminancestr = lu(args, "luminance");
+    char* scalestr = lu(args, "scale");
+    char* scalexstr = lu(args, "scalex");
+    char* scaleystr = lu(args, "scaley");
+    char* rotatestr = lu(args, "rotate");
+    char* xstr="", *pivotstr="";
+    char* ystr="", *anglestr="";
+    char*above = lu(args, "above"); /*FIXME*/
+    char*below = lu(args, "below");
+    char* rstr = lu(args, "red");
+    char* gstr = lu(args, "green");
+    char* bstr = lu(args, "blue");
+    char* astr = lu(args, "alpha");
+    char* pinstr = lu(args, "pin");
+    MULADD r,g,b,a;
+    float oldwidth;
+    float oldheight;
+    SRECT oldbbox;
+    MULADD luminance;
+    parameters_t p;
+
+    if(type==5) {
+       pivotstr = lu(args, "pivot");
+       anglestr = lu(args, "angle");
+    } else {
+       xstr = lu(args, "x");
+       ystr = lu(args, "y");
+    }
+    if(luminancestr[0])
+       luminance = parseMulAdd(luminancestr);
+    else {
+       luminance.add = 0;
+       luminance.mul = 256;
+    }
+
+    if(scalestr[0]) {
+       if(scalexstr[0]||scaleystr[0])
+           syntaxerror("scalex/scaley and scale cannot both be set");
+       scalexstr = scaleystr = scalestr;
+    }
+    
+    if(type == 0 || type == 4)  {
+       // put or startclip
+       character = lu(args, "character");
+       parameters_clear(&p);
+    } else {
+       p = s_getParameters(instance);
+    }
+
+    /* x,y position */
+    if(xstr[0]) {
+       if(isRelative(xstr)) {
+           if(type == 0 || type == 4)
+               syntaxerror("relative x values not allowed for initial put or startclip");
+           p.x += parseTwip(getOffset(xstr))*getSign(xstr);
+       } else {
+           p.x = parseTwip(xstr);
+       }
+    }
+    if(ystr[0]) {
+       if(isRelative(ystr)) {
+           if(type == 0 || type == 4)
+               syntaxerror("relative y values not allowed for initial put or startclip");
+           p.y += parseTwip(getOffset(ystr))*getSign(ystr);
+       } else {
+           p.y = parseTwip(ystr);
+       }
+    }
+
+    /* scale, scalex, scaley */
+    if(character) {
+       oldbbox = s_getCharBBox(character);
+    } else {
+       oldbbox = s_getInstanceBBox(instance);
+    }
+    oldwidth = oldbbox.xmax - oldbbox.xmin;
+    oldheight = oldbbox.ymax - oldbbox.ymin;
+    if(scalexstr[0]) {
+       if(oldwidth==0) p.scalex = 1.0;
+       else {      
+           if(scalexstr[0])
+               p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
+       }
+    }
+    if(scaleystr[0]) {
+       if(oldheight==0) p.scaley = 1.0;
+       else {
+           if(scaleystr[0])
+               p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
+       }
+    }
+   
+    /* rotation */
+    if(rotatestr[0]) {
+       if(isRelative(rotatestr)) {
+           p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
+       } else {
+           p.rotate = parseFloat(rotatestr);
+       }
+    }
+
+    if(pivotstr[0]) {
+       if(isPoint(pivotstr)) 
+           p.pivot = parsePoint(pivotstr);
+       else 
+           p.pivot = getPoint(oldbbox, pivotstr);
+    }
+    if(pinstr[0]) {
+       if(isPoint(pinstr))
+           p.pin = parsePoint(pinstr);
+       else
+           p.pin = getPoint(oldbbox, pinstr);
+    }
+       
+    /* color transform */
+
+    if(rstr[0] || luminancestr[0]) {
+       MULADD r;
+       if(rstr[0])
+           r = parseMulAdd(rstr);
+       else {
+           r.add = p.cxform.r0;
+           r.mul = p.cxform.r1;
+       }
+       r = mergeMulAdd(r, luminance);
+       p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
+    }
+    if(gstr[0] || luminancestr[0]) {
+       MULADD g;
+       if(gstr[0])
+           g = parseMulAdd(gstr);
+       else {
+           g.add = p.cxform.g0;
+           g.mul = p.cxform.g1;
+       }
+       g = mergeMulAdd(g, luminance);
+       p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
+    }
+    if(bstr[0] || luminancestr[0]) {
+       MULADD b;
+       if(bstr[0])
+           b = parseMulAdd(bstr);
+       else {
+           b.add = p.cxform.b0;
+           b.mul = p.cxform.b1;
+       }
+       b = mergeMulAdd(b, luminance);
+       p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
+    }
+    if(astr[0]) {
+       MULADD a = parseMulAdd(astr);
+       p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
+    }
+
+    if(type == 0)
+       s_put(instance, character, p);
+    if(type == 1)
+       s_change(instance, p);
+    if(type == 2)
+       s_qchange(instance, p);
+    if(type == 3)
+       s_jump(instance, p);
+    if(type == 4)
+       s_startclip(instance, character, p);
+    return 0;
+}
+static int c_put(map_t*args) 
+{
+    c_placement(args, 0);
+    return 0;
+}
+static int c_change(map_t*args) 
+{
+    c_placement(args, 1);
+    return 0;
+}
+static int c_qchange(map_t*args) 
+{
+    c_placement(args, 2);
+    return 0;
+}
+static int c_arcchange(map_t*args) 
+{
+    c_placement(args, 2);
+    return 0;
+}
+static int c_jump(map_t*args) 
+{
+    c_placement(args, 3);
+    return 0;
+}
+static int c_startclip(map_t*args) 
+{
+    c_placement(args, 4);
+    return 0;
+}
+static int c_del(map_t*args) 
+{
+    char*instance = lu(args, "name");
+    s_delinstance(instance);
+    return 0;
+}
+static int c_end(map_t*args) 
+{
+    s_end();
+    return 0;
+}
+static int c_sprite(map_t*args) 
+{
+    char* name = lu(args, "name");
+    s_sprite(name);
+    return 0;
+}
+static int c_frame(map_t*args) 
+{
+    char*framestr = lu(args, "n");
+    int frame;
+    if(framestr[0]=='+') {
+       frame = s_getframe();
+       frame += parseInt(framestr+1);
+    }
+    else {
+       frame = parseInt(framestr);
+       if(s_getframe() >= frame
+               && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
+           syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
+    }
+    s_frame(frame);
+    return 0;
+}
+static int c_primitive(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*command = lu(args, "commandname");
+    int width=0, height=0, r=0;
+    int linewidth = parseTwip(lu(args, "line"));
+    char*colorstr = lu(args, "color");
+    RGBA color = parseColor(colorstr);
+    char*fillstr = lu(args, "fill");
+    int dofill = 1;
+    int type=0;
+    char* font;
+    char* text;
+    RGBA fill;
+    if(!strcmp(command, "circle"))
+       type = 1;
+    else if(!strcmp(command, "textshape"))
+       type = 2;
+   
+    if(type==0) {
+       width = parseTwip(lu(args, "width"));
+       height = parseTwip(lu(args, "height"));
+    } else if (type==1) {
+       r = parseTwip(lu(args, "r"));
+    } else if (type==2) {
+       text = lu(args, "text");
+       font = lu(args, "font");
+    }
+
+    if(!strcmp(fillstr, "fill"))
+       fillstr = colorstr;
+    if(!strcmp(fillstr, "none"))
+       dofill = 0;
+    if(width<0 || height<0 || linewidth<0 || r<0)
+       syntaxerror("values width, height, line, r must be positive");
+    if(!dofill || isColor(fillstr)) {
+       if(dofill) 
+           fill = parseColor(fillstr);
+    } else {
+       /* FIXME - texture fill */
+       fill.r = fill.g = 0;
+       fill.b = fill.a = 255;
+       warning("texture fill not supported yet. Filling with black.");
+    }
+    if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
+    else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
+    else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
+    return 0;
+}
+
+static int c_shape(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*filename = lu(args, "filename");
+    s_shape(name, filename);
+    return 0;
+}
+
+static int c_font(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*filename = lu(args, "filename");
+    s_font(name, filename);
+    return 0;
+}
+
+static int c_text(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*text = lu(args, "text");
+    char*font = lu(args, "font");
+    float size = parsePercent(lu(args, "size"));
+    RGBA color = parseColor(lu(args, "color"));
+    s_text(name, font, text, (int)(size*100), color);
+    return 0;
+}
+
+int fakechar(map_t*args)
+{
+    char*name = lu(args, "name");
+    s_box(name, 0, 0, black, 20, black, 0);
+    return 0;
+}
+static int c_circle(map_t*args) {return fakechar(args);}
+
+static int c_egon(map_t*args) {return fakechar(args);}
+static int c_button(map_t*args) {return fakechar(args);}
+static int c_edittext(map_t*args) {return fakechar(args);}
+
+static int c_morphshape(map_t*args) {return fakechar(args);}
+static int c_image(map_t*args) {return fakechar(args);}
+static int c_movie(map_t*args) {return fakechar(args);}
+static int c_sound(map_t*args) {return fakechar(args);}
+
+static int c_play(map_t*args) {return 0;}
+static int c_stop(map_t*args) {return 0;}
+
+static int c_soundtrack(map_t*args) {return 0;}
+static int c_buttonsounds(map_t*args) {return 0;}
+static int c_buttonput(map_t*args) {return 0;}
+static int c_texture(map_t*args) {return 0;}
+static int c_action(map_t*args) {return 0;}
+
+static struct {
+    char*command;
+    command_func_t* func;
+    char*arguments;
+} arguments[] =
+{{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
+ {"frame", c_frame, "n=+1"},
+
+    // "import" type stuff
+ {"shape", c_shape, "name filename"},
+ {"morphshape", c_morphshape, "name start end"},
+ {"jpeg", c_image, "name filename quality=80%"},
+ {"png", c_image, "name filename"},
+ {"movie", c_movie, "name filename"},
+ {"sound", c_sound, "name filename"},
+ {"font", c_font, "name filename"},
+ {"soundtrack", c_soundtrack, "filename"},
+
+    // character generators
+ {"box", c_primitive, "name width height color=white line=1 @fill=none"},
+ {"circle", c_primitive, "name r color=white line=1 @fill=none"},
+ {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
+ {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
+ {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
+ {"text", c_text, "name text font size=100% color=white"},
+ {"edittext", c_edittext, "name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
+ {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
+
+    // control tags
+ {"play", c_play, "sound loop=0 @nomultiple=0"},
+ {"stop", c_stop, "sound"},
+
+    // object placement tags
+ {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
+ {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
+ {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
+ {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
+ {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
+ {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
+ {"del", c_del, "name"},
+    // virtual object placement
+ {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% rotate=0 above= below="},
+ {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% rotate=0"},
+ {"point", c_point, "name x=0 y=0"},
+
+    // commands which start a block
+//startclip (see above)
+ {"sprite", c_sprite, "name"},
+ {"action", c_action, ""},
+
+ {"end", c_end, ""}
+};
+
+
+static map_t parseArguments(char*command, char*pattern)
+{
+    char*x;
+    char*d,*e;
+   
+    string_t name[64];
+    string_t value[64];
+    int set[64];
+    int isboolean[64];
+    int pos;
+    int len;
+    int t;
+    string_t t1,t2;
+    map_t result;
+    map_init(&result);
+
+    string_set(&t1, "commandname");
+    string_set(&t2, command);
+    map_put(&result, t1, t2);
+
+    if(!pattern || !*pattern)
+       return result;
+
+    x = pattern;
+
+    pos = 0;
+
+    if(!strncmp("<i> ", x, 3)) {
+       readToken();
+       if(type == COMMAND || type == LABEL) {
+           pushBack();
+           syntaxerror("character name expected");
+       }
+       name[pos].str = "instance";
+       name[pos].len = 8;
+       value[pos].str = text;
+       value[pos].len = strlen(text);
+       set[pos] = 1;
+       pos++;
+
+       if(type == ASSIGNMENT)
+           readToken();
+
+       name[pos].str = "character";
+       name[pos].len = 9;
+       value[pos].str = text;
+       value[pos].len = strlen(text);
+       set[pos] = 1;
+       pos++;
+
+       x+=4;
+    }
+
+    while(*x) {
+       isboolean[pos] = (x[0] =='@');
+       if(isboolean[pos])
+           x++;
+
+       d = strchr(x, ' ');
+       e = strchr(x, '=');
+       if(!d)
+           d=&x[strlen(x)];
+       set[pos] = 0;
+
+       if(!e || d<e) { 
+           // no default
+           name[pos].str = x;
+           name[pos].len = d-x;
+           value[pos].str = 0;
+           value[pos].len = 0;
+       } else {
+           name[pos].str = x;
+           name[pos].len = e-x;
+           value[pos].str = e+1;
+           value[pos].len = d-e-1;
+       }
+       pos++;
+       if(!*d) break;
+       x=d+1;
+    }
+    len = pos;
+
+/*    for(t=0;t<len;t++) {
+       printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
+               isboolean[t]?"(boolean)":"");
+    }*/
+
+    while(1) {
+       readToken();
+       if(type == LABEL || type == COMMAND) {
+           pushBack();
+           break;
+       }
+
+       // first, search for boolean arguments
+       for(pos=0;pos<len;pos++)
+       {
+           if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
+               set[pos] = 1;
+               if(type == ASSIGNMENT)
+                   readToken();
+               value[pos].str = text;
+               value[pos].len = strlen(text);
+               /*printf("setting boolean parameter %s (to %s)\n",
+                       strndup(name[pos], namelen[pos]),
+                       strndup(value[pos], valuelen[pos]));*/
+               break;
+           }
+       }
+
+       // second, search for normal arguments
+       if(pos==len)
+       for(pos=0;pos<len;pos++)
+       {
+           if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
+              (type != ASSIGNMENT && !set[pos])) {
+               if(set[pos]) {
+                   syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
+               }
+               if(type == ASSIGNMENT)
+                   readToken();
+               set[pos] = 1;
+               value[pos].str = text;
+               value[pos].len = strlen(text);
+#if 0//def DEBUG
+               printf("setting parameter %s (to %s)\n",
+                       strndup(name[pos].str, name[pos].len),
+                       strndup(value[pos].str, value[pos].len));
+#endif
+               break;
+           }
+       }
+       if(pos==len) {
+           syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
+       }
+    }
+#if 0//def DEBUG
+    for(t=0;t<len;t++) {
+       printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
+    }
+#endif
+    for(t=0;t<len;t++) {
+       if(value[t].str && value[t].str[0] == '*') {
+           //relative default- take value from some other parameter
+           int s;
+           for(s=0;s<len;s++) {
+               if(value[s].len == value[t].len-1 &&
+                  !strncmp(&value[t].str[1], value[s].str, value[s].len))
+                   value[t].str = value[s].str;
+           }
+       }
+       if(value[t].str == 0) {
+           pushBack();
+           syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
+       }
+    }
+
+    /* ok, now construct the dictionary from the parameters */
+
+    for(t=0;t<len;t++) 
+    {
+       map_put(&result, name[t], value[t]);
+    }
+    return result;
+}
+static void parseArgumentsForCommand(char*command)
+{
+    int t;
+    map_t args;
+    int nr = -1;
+    for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
+       if(!strcmp(arguments[t].command, command)) {
+           args = parseArguments(command, arguments[t].arguments);
+           nr = t;
+       }
+    }
+    if(nr<0)
+       syntaxerror("command %s not known", command);
+
+#ifdef DEBUG
+    printf(".%s\n", command);fflush(stdout);
+    map_dump(&args, stdout, "\t");fflush(stdout);
+#endif
+
+    (*arguments[nr].func)(&args);
+
+    if(!strcmp(command, "button") ||
+       !strcmp(command, "action")) {
+       while(1) {
+           readToken();
+           if(type == COMMAND) {
+               if(!strcmp(text, "end"))
+                   break;
+               else {
+                   pushBack();
+                   break;
+               }
+           }
+       }
+    }
+
+    map_clear(&args);
+    return;
+}
+
+int main (int argc,char ** argv)
+{ 
+    int t;
+    processargs(argc, argv);
+    initLog(0,-1,0,0,-1,verbose);
+
+    if(!filename) {
+       args_callback_usage(argv[0]);
+       exit(1);
+    }
+    file = generateTokens(filename);
+    if(!file) {
+       printf("parser returned error.\n");
+       return 1;
+    }
+    pos=0;
+
+    t=0;
+
+    while(!noMoreTokens()) {
+       readToken();
+       if(type != COMMAND)
+           syntaxerror("command expected");
+       parseArgumentsForCommand(text);
+    }
+
+    s_close();
+    freeTokens(file);
+
+    return 0;
+}
+