added missing O_BINARY to open().
[swftools.git] / src / swfc.c
index 49897ef..cb7aaff 100644 (file)
@@ -42,12 +42,12 @@ static char * outputname = "output.swf";
 static int verbose = 2;
 static int override_outputname = 0;
 
-static struct options_t options[] =
-{
- {"o","output"},
- {"v","verbose"},
- {"V","version"},
- {0,0}
+static struct options_t options[] = {
+{"h", "help"},
+{"V", "version"},
+{"v", "verbose"},
+{"o", "output"},
+{0,0}
 };
     
 int args_callback_option(char*name,char*val)
@@ -75,12 +75,16 @@ int args_callback_longoption(char*name,char*val)
 {
     return args_long2shortoption(options, name, val);
 }
-void args_callback_usage(char*name)
+void args_callback_usage(char *name)
 {
-    printf("Usage: %s [-o filename] file.sc\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");
+    printf("\n");
+    printf("Usage: %s [-o file.swf] file.sc\n", name);
+    printf("\n");
+    printf("-h , --help                    Print short help message and exit\n");
+    printf("-V , --version                 Print version info and exit\n");
+    printf("-v , --verbose                 Increase verbosity. \n");
+    printf("-o , --output <filename>       Set output file to <filename>.\n");
+    printf("\n");
 }
 int args_callback_command(char*name,char*val)
 {
@@ -161,7 +165,7 @@ static int noMoreTokens()
 struct _character;
 static struct level
 {
-   int type; //0=swf, 1=sprite, 2=clip
+   int type; //0=swf, 1=sprite, 2=clip, 3=button
 
    /* for swf (0): */
    SWF*swf;
@@ -316,7 +320,8 @@ 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->pin.x = 0;  //1??
+    p->pin.y = 0;
     p->pivot.x = 0; p->pivot.y = 0;
     p->rotate = 0; 
     p->shear = 0; 
@@ -343,18 +348,18 @@ static void makeMatrix(MATRIX*m, parameters_t*p)
     m->sy = (int)(sy*65536+0.5);
 
     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)
+static MATRIX s_instancepos(SRECT rect, parameters_t*p)
 {
     MATRIX m;
     SRECT r;
     makeMatrix(&m, p);
-    r = swf_TurnRect(i->character->size, &m);
+    r = swf_TurnRect(rect, &m);
     if(currentrect.xmin == 0 && currentrect.ymin == 0 && 
        currentrect.xmax == 0 && currentrect.ymax == 0)
        currentrect = r;
@@ -431,6 +436,151 @@ void s_sprite(char*name)
     incrementid();
 }
 
+typedef struct _buttonrecord
+{
+    U16 id;
+    MATRIX matrix;
+    CXFORM cxform;
+    char set;
+} buttonrecord_t;
+
+typedef struct _button
+{
+    int endofshapes;
+    int nr_actions;
+    buttonrecord_t records[4];
+} button_t;
+
+static button_t mybutton;
+
+void s_button(char*name)
+{
+    tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
+    swf_SetU16(tag, id); //id
+    swf_ButtonSetFlags(tag, 0); //menu=no
+
+    memset(&mybutton, 0, sizeof(mybutton));
+
+    memset(&stack[stackpos], 0, sizeof(stack[0]));
+    stack[stackpos].type = 3;
+    stack[stackpos].tag = tag;
+    stack[stackpos].id = id;
+    stack[stackpos].name = strdup(name);
+    stack[stackpos].oldrect = currentrect;
+    memset(&currentrect, 0, sizeof(currentrect));
+
+    stackpos++;
+    incrementid();
+}
+void s_buttonput(char*character, char*as, parameters_t p)
+{
+    character_t* c = dictionary_lookup(&characters, character);
+    MATRIX m;
+    int flags = 0;
+    char*o = as,*s = as;
+    buttonrecord_t r;
+    if(!stackpos || (stack[stackpos-1].type != 3))  {
+       syntaxerror(".show may only appear in .button");
+    }
+    if(!c) {
+       syntaxerror("character %s not known (in .shape %s)", character, character);
+    }
+    if(mybutton.endofshapes) {
+       syntaxerror("a .do may not precede a .show", character, character);
+    }
+   
+    m = s_instancepos(c->size, &p);
+
+    r.id = c->id;
+    r.matrix = m;
+    r.cxform = p.cxform;
+    r.set = 1;
+
+    while(1) {
+       if(*s==',' || *s==0) {
+           if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
+           else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
+           else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
+           else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
+           else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
+           else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
+       }
+       if(!*s)
+           break;
+       s++;
+    }
+}
+static void setbuttonrecords(TAG*tag)
+{
+    int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
+    if(!mybutton.endofshapes) {
+       int t;
+               
+       if(!mybutton.records[3].set) {
+           memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
+       }
+
+       for(t=0;t<4;t++) {
+           if(mybutton.records[t].set) {
+               swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
+           }
+       }
+       swf_SetU8(tag,0); // end of button records
+       mybutton.endofshapes = 1;
+    }
+}
+
+void s_buttonaction(int flags, char*action)
+{
+    ActionTAG* a = 0;
+    if(flags==0) {
+       return;
+    }
+    setbuttonrecords(stack[stackpos-1].tag);
+    
+    a = swf_ActionCompile(text, stack[0].swf->fileVersion);
+    if(!a) {
+       syntaxerror("Couldn't compile ActionScript");
+    }
+
+    swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
+    swf_ActionSet(stack[stackpos-1].tag, a);
+    mybutton.nr_actions++;
+
+    swf_ActionFree(a);
+}
+
+static void setactionend(TAG*tag)
+{
+    if(!mybutton.nr_actions) {
+       /* no actions means we didn't have an actionoffset,
+          which means we can't signal the end of the
+          buttonaction records, so, *sigh*, we have
+          to insert a dummy record */
+       swf_SetU16(tag, 0); //offset
+       swf_SetU16(tag, 0); //condition
+       swf_SetU8(tag, 0); //action
+    }
+}
+
+static void s_endButton()
+{
+    SRECT r;
+    setbuttonrecords(stack[stackpos-1].tag);
+    setactionend(stack[stackpos-1].tag);
+    stackpos--;
+      
+    swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
+
+    r = currentrect;
+
+    tag = stack[stackpos].tag;
+    currentrect = stack[stackpos].oldrect;
+
+    s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
+    free(stack[stackpos].name);
+}
+
 TAG* removeFromTo(TAG*from, TAG*to)
 {
     TAG*save = from->prev;
@@ -492,8 +642,9 @@ static void s_endSWF()
 
     swf_OptimizeTagOrder(swf);
 
-    if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
+    if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
        swf->movieSize = currentrect; /* "autocrop" */
+    }
     
     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
        swf->movieSize.xmax += 20; /* 1 by 1 pixels */
@@ -541,13 +692,17 @@ int s_getframe()
     return currentframe;
 }
 
-void s_frame(int nr, int cut)
+void s_frame(int nr, int cut, char*name)
 {
     int t;
     TAG*now = tag;
 
     for(t=currentframe;t<nr;t++) {
        tag = swf_InsertTag(tag, ST_SHOWFRAME);
+       if(t==nr-1 && name && *name) {
+           tag = swf_InsertTag(tag, ST_FRAMELABEL);
+           swf_SetString(tag, name);
+       }
     }
 
     if(cut) {
@@ -605,7 +760,7 @@ void s_box(char*name, int width, int height, RGBA color, int linewidth, char*tex
     r2.ymin = 0;
     r2.xmax = width;
     r2.ymax = height;
-    tag = swf_InsertTag(tag, ST_DEFINESHAPE);
+    tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
     swf_ShapeNew(&s);
     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
 
@@ -643,7 +798,7 @@ void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*textu
     }
     r2 = outline->bbox;
 
-    tag = swf_InsertTag(tag, ST_DEFINESHAPE);
+    tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
     swf_ShapeNew(&s);
     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
     if(texture)
@@ -675,7 +830,7 @@ void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
     r2.xmax = 2*r;
     r2.ymax = 2*r;
 
-    tag = swf_InsertTag(tag, ST_DEFINESHAPE);
+    tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
     swf_ShapeNew(&s);
     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
     if(texture)
@@ -697,7 +852,7 @@ void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
     incrementid();
 }
 
-void s_textshape(char*name, char*fontname, char*_text)
+void s_textshape(char*name, char*fontname, float size, char*_text)
 {
     int g;
     U8*text = (U8*)_text;
@@ -723,7 +878,7 @@ void s_textshape(char*name, char*fontname, char*_text)
     {
        drawer_t draw;
        swf_Shape11DrawerInit(&draw, 0);
-       swf_DrawText(&draw, font, _text);
+       swf_DrawText(&draw, font, (int)(size*100), _text);
        draw.finish(&draw);
        outline->shape = swf_ShapeDrawerToShape(&draw);
        outline->bbox = swf_ShapeDrawerGetBBox(&draw);
@@ -738,7 +893,7 @@ void s_textshape(char*name, char*fontname, char*_text)
 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
 {
     SRECT r;
-
+    MATRIX _m,*m=0;
     SWFFONT*font;
     font = dictionary_lookup(&fonts, fontname);
     if(!font)
@@ -756,6 +911,32 @@ void s_text(char*name, char*fontname, char*text, int size, RGBA color)
     incrementid();
 }
 
+void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
+{
+    SWFFONT*font;
+    EditTextLayout layout;
+    SRECT r;
+
+    font = dictionary_lookup(&fonts, fontname);
+    if(!font)
+       syntaxerror("font \"%s\" not known!", fontname);
+    tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
+    swf_SetU16(tag, id);
+    layout.align = 0;
+    layout.leftmargin = 0;
+    layout.rightmargin = 0;
+    layout.indent = 0;
+    layout.leading = 0;
+    r.xmin = 0;
+    r.ymin = 0;
+    r.xmax = width;
+    r.ymax = height;
+    swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
+
+    s_addcharacter(name, id, tag, r);
+    incrementid();
+}
+
 /* type: either "jpeg" or "png"
  */
 void s_image(char*name, char*type, char*filename, int quality)
@@ -772,22 +953,30 @@ void s_image(char*name, char*type, char*filename, int quality)
        s_box(name, 0, 0, black, 20, 0);
        return;
     }
-    tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
-    swf_SetU16(tag, imageID);
+    if(type=="jpeg") {
+#ifndef HAVE_LIBJPEG
+       warning("no jpeg support compiled in");
+       s_box(name, 0, 0, black, 20, 0);
+       return;
+#else
+       tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
+       swf_SetU16(tag, imageID);
 
-    if(swf_SetJPEGBits(tag, filename, quality) < 0) {
-       syntaxerror("Image \"%s\" not found, or contains errors", filename);
-    }
+       if(swf_SetJPEGBits(tag, filename, quality) < 0) {
+           syntaxerror("Image \"%s\" not found, or contains errors", filename);
+       }
 
-    swf_GetJPEGSize(filename, &width, &height);
+       swf_GetJPEGSize(filename, &width, &height);
 
-    r.xmin = 0;
-    r.ymin = 0;
-    r.xmax = width*20;
-    r.ymax = height*20;
+       r.xmin = 0;
+       r.ymin = 0;
+       r.xmax = width*20;
+       r.ymax = height*20;
 
-    s_addimage(name, id, tag, r);
-    incrementid();
+       s_addimage(name, id, tag, r);
+       incrementid();
+#endif
+    }
 
     /* step 2: the character */
     tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
@@ -832,6 +1021,8 @@ void s_font(char*name, char*filename)
        font->layout = 0;
        swf_FontCreateLayout(font);
     }
+    /* just in case this thing is used in .edittext later on */
+    swf_FontPrepareForEditText(font);
 
     font->id = id;
     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
@@ -953,6 +1144,9 @@ void s_action(const char*text)
 {
     ActionTAG* a = 0;
     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
+    if(!a) {
+       syntaxerror("Couldn't compile ActionScript");
+    }
 
     tag = swf_InsertTag(tag, ST_DOACTION);
 
@@ -961,6 +1155,27 @@ void s_action(const char*text)
     swf_ActionFree(a);
 }
 
+int s_swf3action(char*name, char*action)
+{
+    ActionTAG* a = 0;
+    instance_t* object = dictionary_lookup(&instances, name);
+    if(!object) {
+       return 0;
+    }
+    a = action_SetTarget(0, name);
+    if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
+    else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
+    else if(!strcmp(action, "stop")) a = action_Stop(a);
+    else if(!strcmp(action, "play")) a = action_Play(a);
+    a = action_SetTarget(a, "");
+    a = action_End(a);
+
+    tag = swf_InsertTag(tag, ST_DOACTION);
+    swf_ActionSet(tag, a);
+    swf_ActionFree(a);
+    return 1;
+}
+
 void s_outline(char*name, char*format, char*source)
 {
     outline_t* outline;
@@ -990,12 +1205,12 @@ void s_outline(char*name, char*format, char*source)
     dictionary_put2(&outlines, name, outline);
 }
 
-void s_playsound(char*name, int loops, int nomultiple, int stop)
+int s_playsound(char*name, int loops, int nomultiple, int stop)
 {
     sound_t* sound = dictionary_lookup(&sounds, name);
     SOUNDINFO info;
     if(!sound)
-       syntaxerror("Don't know anything about sound \"%s\"", name);
+       return 0;
 
     tag = swf_InsertTag(tag, ST_STARTSOUND);
     swf_SetU16(tag, sound->id); //id
@@ -1004,6 +1219,7 @@ void s_playsound(char*name, int loops, int nomultiple, int stop)
     info.loops = loops;
     info.nomultiple = nomultiple;
     swf_SetSoundInfo(tag, &info);
+    return 1;
 }
 
 void s_includeswf(char*name, char*filename)
@@ -1015,7 +1231,7 @@ void s_includeswf(char*name, char*filename)
     TAG* s;
     int level = 0;
     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
-    f = open(filename,O_RDONLY);
+    f = open(filename,O_RDONLY|O_BINARY);
     if (f<0) { 
        warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
        s_box(name, 0, 0, black, 20, 0);
@@ -1105,7 +1321,7 @@ void s_startclip(char*instance, char*character, parameters_t p)
     }
     i = s_addinstance(instance, c, currentdepth);
     i->parameters = p;
-    m = s_instancepos(i, &p);
+    m = s_instancepos(i->character->size, &p);
     
     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
@@ -1126,6 +1342,7 @@ void s_endClip()
     swf_SetTagPos(stack[stackpos].tag, 0);
     swf_GetPlaceObject(stack[stackpos].tag, &p);
     p.clipdepth = currentdepth;
+    p.name = 0;
     swf_ClearTag(stack[stackpos].tag);
     swf_SetPlaceObject(stack[stackpos].tag, &p);
     currentdepth++;
@@ -1142,7 +1359,7 @@ void s_put(char*instance, char*character, parameters_t p)
     
     i = s_addinstance(instance, c, currentdepth);
     i->parameters = p;
-    m = s_instancepos(i, &p);
+    m = s_instancepos(i->character->size, &p);
     
     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
@@ -1160,7 +1377,7 @@ void s_jump(char*instance, parameters_t p)
     }
 
     i->parameters = p;
-    m = s_instancepos(i, &p);
+    m = s_instancepos(i->character->size, &p);
 
     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
     swf_ObjectMove(tag, i->depth, &m, &p.cxform);
@@ -1218,7 +1435,7 @@ void s_change(char*instance, parameters_t p2)
        return;
     }
     
-    m = s_instancepos(i, &p2);
+    m = s_instancepos(i->character->size, &p2);
     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
     swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
     i->parameters = p2;
@@ -1236,7 +1453,7 @@ void s_change(char*instance, parameters_t p2)
            TAG*lt;
            frame ++;
            p = s_interpolate(&p1, &p2, frame, allframes);
-           m = s_instancepos(i, &p); //needed?
+           m = s_instancepos(i->character->size, &p); //needed?
            lt = swf_InsertTag(t, ST_PLACEOBJECT2);
            i->lastFrame = currentframe;
            swf_ObjectMove(lt, i->depth, &m, &p.cxform);
@@ -1275,6 +1492,8 @@ void s_end()
        s_endSprite();
     else if(stack[stackpos-1].type == 2)
        s_endClip();
+    else if(stack[stackpos-1].type == 3)
+       s_endButton();
     else syntaxerror("internal error 1");
 }
 
@@ -1639,7 +1858,7 @@ static int c_point(map_t*args)
 }
 static int c_play(map_t*args) 
 {
-    char*name = lu(args, "sound");
+    char*name = lu(args, "name");
     char*loop = lu(args, "loop");
     char*nomultiple = lu(args, "nomultiple");
     int nm = 0;
@@ -1648,14 +1867,46 @@ static int c_play(map_t*args)
     else
        nm = parseInt(nomultiple);
 
-    s_playsound(name, parseInt(loop), nm, 0);
+    if(s_playsound(name, parseInt(loop), nm, 0)) {
+       return 0;
+    } else if(s_swf3action(name, "play")) {
+       return 0;
+    }
     return 0;
 }
 
 static int c_stop(map_t*args) 
 {
-    char*name = lu(args, "sound");
-    s_playsound(name, 0,0,1);
+    char*name = lu(args, "name");
+
+    if(s_playsound(name, 0,0,1)) {
+       return 0;
+    } else if(s_swf3action(name, "stop")) {
+       return 0;
+    }
+    syntaxerror("I don't know anything about sound/movie \"%s\"", name);
+    return 0;
+}
+
+static int c_nextframe(map_t*args) 
+{
+    char*name = lu(args, "name");
+
+    if(s_swf3action(name, "nextframe")) {
+       return 0;
+    }
+    syntaxerror("I don't know anything about movie \"%s\"", name);
+    return 0;
+}
+
+static int c_previousframe(map_t*args) 
+{
+    char*name = lu(args, "name");
+
+    if(s_swf3action(name, "previousframe")) {
+       return 0;
+    }
+    syntaxerror("I don't know anything about movie \"%s\"", name);
     return 0;
 }
 
@@ -1679,6 +1930,7 @@ static int c_placement(map_t*args, int type)
     char* bstr = lu(args, "blue");
     char* astr = lu(args, "alpha");
     char* pinstr = lu(args, "pin");
+    char* as = map_lookup(args, "as");
     MULADD r,g,b,a;
     float oldwidth;
     float oldheight;
@@ -1686,7 +1938,7 @@ static int c_placement(map_t*args, int type)
     MULADD luminance;
     parameters_t p;
 
-    if(type==5) {
+    if(type==9) { // (?) .rotate  or .arcchange
        pivotstr = lu(args, "pivot");
        anglestr = lu(args, "angle");
     } else {
@@ -1710,6 +1962,10 @@ static int c_placement(map_t*args, int type)
        // put or startclip
        character = lu(args, "character");
        parameters_clear(&p);
+    } else if (type == 5) {
+       character = lu(args, "name");
+       parameters_clear(&p);
+       // button's show
     } else {
        p = s_getParameters(instance);
     }
@@ -1830,14 +2086,21 @@ static int c_placement(map_t*args, int type)
 
     if(type == 0)
        s_put(instance, character, p);
-    if(type == 1)
+    else if(type == 1)
        s_change(instance, p);
-    if(type == 2)
+    else if(type == 2)
        s_qchange(instance, p);
-    if(type == 3)
+    else if(type == 3)
        s_jump(instance, p);
-    if(type == 4)
+    else if(type == 4)
        s_startclip(instance, character, p);
+    else if(type == 5) {
+       if(as && as[0]) {
+           s_buttonput(character, as, p);
+       } else {
+           s_buttonput(character, "shape", p);
+       }
+    }
     return 0;
 }
 static int c_put(map_t*args) 
@@ -1870,6 +2133,11 @@ static int c_startclip(map_t*args)
     c_placement(args, 4);
     return 0;
 }
+static int c_show(map_t*args) 
+{
+    c_placement(args, 5);
+    return 0;
+}
 static int c_del(map_t*args) 
 {
     char*instance = lu(args, "name");
@@ -1891,6 +2159,7 @@ static int c_frame(map_t*args)
 {
     char*framestr = lu(args, "n");
     char*cutstr = lu(args, "cut");
+    char*name = lu(args, "name");
     int frame;
     int cut = 0;
     if(strcmp(cutstr, "no"))
@@ -1907,7 +2176,7 @@ static int c_frame(map_t*args)
                && !(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, cut);
+    s_frame(frame, cut, name);
     return 0;
 }
 static int c_primitive(map_t*args) 
@@ -1957,13 +2226,12 @@ static int c_textshape(map_t*args)
     char*name = lu(args, "name");
     char*text = lu(args, "text");
     char*font = lu(args, "font");
+    float size = parsePercent(lu(args, "size"));
 
-    s_textshape(name, font, text);
+    s_textshape(name, font, size, text);
     return 0;
 }
 
-
-
 static int c_swf(map_t*args) 
 {
     char*name = lu(args, "name");
@@ -2043,29 +2311,171 @@ int fakechar(map_t*args)
 
 static int c_egon(map_t*args) {return fakechar(args);}
 static int c_button(map_t*args) {
+    char*name = lu(args, "name");
+    s_button(name);
+    return 0;
+}
+static int current_button_flags = 0;
+static int c_on_press(map_t*args)
+{
+    char*position = lu(args, "position");
     char*action = "";
+    if(!strcmp(position, "inside")) {
+       current_button_flags |= BC_OVERUP_OVERDOWN;
+    } else if(!strcmp(position, "outside")) {
+       //current_button_flags |= BC_IDLE_OUTDOWN;
+       syntaxerror("IDLE_OVERDOWN not supported by SWF");
+    } else if(!strcmp(position, "anywhere")) {
+       current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
+    }
     readToken();
-    if(type == RAWDATA)
+    if(type == RAWDATA) {
        action = text;
+       s_buttonaction(current_button_flags, action);
+       current_button_flags = 0;
+    }
     else
        pushBack();
+    return 0;
+}
+static int c_on_release(map_t*args)
+{
+    char*position = lu(args, "position");
+    char*action = "";
+    if(!strcmp(position, "inside")) {
+       current_button_flags |= BC_OVERDOWN_OVERUP;
+    } else if(!strcmp(position, "outside")) {
+       current_button_flags |= BC_OUTDOWN_IDLE;
+    } else if(!strcmp(position, "anywhere")) {
+       current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
+    }
+    readToken();
+    if(type == RAWDATA) {
+       action = text;
+       s_buttonaction(current_button_flags, action);
+       current_button_flags = 0;
+    }
+    else
+       pushBack();
+    return 0;
+}
+static int c_on_move_in(map_t*args)
+{
+    char*position = lu(args, "state");
+    char*action = "";
+    if(!strcmp(position, "pressed")) {
+       current_button_flags |= BC_OUTDOWN_OVERDOWN;
+    } else if(!strcmp(position, "not_pressed")) {
+       current_button_flags |= BC_IDLE_OVERUP;
+    } else if(!strcmp(position, "any")) {
+       current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
+    }
+    readToken();
+    if(type == RAWDATA) {
+       action = text;
+       s_buttonaction(current_button_flags, action);
+       current_button_flags = 0;
+    }
+    else
+       pushBack();
+    return 0;
+}
+static int c_on_move_out(map_t*args)
+{
+    char*position = lu(args, "state");
+    char*action = "";
+    if(!strcmp(position, "pressed")) {
+       current_button_flags |= BC_OVERDOWN_OUTDOWN;
+    } else if(!strcmp(position, "not_pressed")) {
+       current_button_flags |= BC_OVERUP_IDLE;
+    } else if(!strcmp(position, "any")) {
+       current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
+    }
+    readToken();
+    if(type == RAWDATA) {
+       action = text;
+       s_buttonaction(current_button_flags, action);
+       current_button_flags = 0;
+    }
+    else
+       pushBack();
+    return 0;
+}
+static int c_on_key(map_t*args)
+{
+    char*key = lu(args, "key");
+    char*action = "";
+    if(strlen(key)==1) {
+       /* ascii */
+       if(key[0]>=32) {
+           current_button_flags |= 0x4000 + (key[0]*0x200);
+       } else {
+           syntaxerror("invalid character: %c"+key[0]);
+           return 1;
+       }
+    } else {
+       /* TODO: 
+          <ctrl-x> = 0x200*(x-'a')
+          esc = = 0x3600
+          space = = 0x4000;
+       */
+       syntaxerror("invalid key: %s",key);
+    }
+    readToken();
+    if(type == RAWDATA) {
+       action = text;
+       s_buttonaction(current_button_flags, action);
+       current_button_flags = 0;
+    }
+    else
+       pushBack();
+    return 0;
+}
 
-    return fakechar(args);
+static int c_edittext(map_t*args) 
+{
+ //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
+    char*name = lu(args, "name");
+    char*font = lu(args, "font");
+    int size = (int)(1024*parsePercent(lu(args, "size")));
+    int width = parseTwip(lu(args, "width"));
+    int height = parseTwip(lu(args, "height"));
+    char*text  = lu(args, "text");
+    RGBA color = parseColor(lu(args, "color"));
+    int maxlength = parseInt(lu(args, "maxlength"));
+    char*variable = lu(args, "variable");
+    char*passwordstr = lu(args, "password");
+    char*wordwrapstr = lu(args, "wordwrap");
+    char*multilinestr = lu(args, "multiline");
+    char*htmlstr = lu(args, "html");
+    char*noselectstr = lu(args, "noselect");
+    char*readonlystr = lu(args, "readonly");
+    char*borderstr = lu(args, "border");
+
+    int flags = 0;
+    if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
+    if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
+    if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
+    if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
+    if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
+    if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
+    if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
+
+    s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
+    return 0;
 }
-static int c_edittext(map_t*args) {return fakechar(args);}
 
 static int c_morphshape(map_t*args) {return fakechar(args);}
 static int c_movie(map_t*args) {return fakechar(args);}
 
-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) 
 {
     readToken();
-    if(type != RAWDATA)
+    if(type != RAWDATA) {
        syntaxerror("colon (:) expected");
+    }
 
     s_action(text);
    
@@ -2078,7 +2488,7 @@ static struct {
     char*arguments;
 } arguments[] =
 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
- {"frame", c_frame, "n=<plus>1 @cut=no"},
+ {"frame", c_frame, "n=<plus>1 name= @cut=no"},
  // "import" type stuff
  {"swf", c_swf, "name filename"},
  {"shape", c_swf, "name filename"},
@@ -2094,7 +2504,7 @@ static struct {
  {"point", c_point, "name x=0 y=0"},
  {"gradient", c_gradient, "name @radial=0"},
  {"outline", c_outline, "name format=simple"},
- {"textshape", c_textshape, "name text font"},
+ {"textshape", c_textshape, "name font size=100% text"},
 
     // character generators
  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
@@ -2102,27 +2512,32 @@ static struct {
  {"filled", c_primitive, "name outline 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"},
+ {"edittext", c_edittext, "name font size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0"},
  {"morphshape", c_morphshape, "name start end"},
+ {"button", c_button, "name"},
+    {"show", c_show,             "name x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below= as="},
+    {"on_press", c_on_press, "position=inside"},
+    {"on_release", c_on_release, "position=anywhere"},
+    {"on_move_in", c_on_move_in, "state=not_pressed"},
+    {"on_move_out", c_on_move_out, "state=not_pressed"},
+    {"on_key", c_on_key, "key=any"},
  
- {"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"},
+ {"play", c_play, "name loop=0 @nomultiple=0"},
+ {"stop", c_stop, "name"},
+ {"nextframe", c_nextframe, "name"},
+ {"previousframe", c_previousframe, "name"},
 
     // 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= shear= 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= shear= rotate= above= below="},
- {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
- {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
- {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
- {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
+ {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
+ {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
+ {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
+ {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
+ {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
+ {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= 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% shear=0 rotate=0 above= below="},
  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
 
     // commands which start a block
@@ -2305,6 +2720,7 @@ static void parseArgumentsForCommand(char*command)
     map_t args;
     int nr = -1;
     msg("<verbose> parse Command: %s (line %d)", command, line);
+
     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
        if(!strcmp(arguments[t].command, command)) {
 
@@ -2323,6 +2739,12 @@ static void parseArgumentsForCommand(char*command)
     }
     if(nr<0)
        syntaxerror("command %s not known", command);
+   
+    // catch missing .flash directives at the beginning of a file
+    if(strcmp(command, "flash") && !stackpos)
+    {
+       syntaxerror("No movie defined- use .flash first");
+    }
 
 #ifdef DEBUG
     printf(".%s\n", command);fflush(stdout);
@@ -2360,13 +2782,13 @@ int main (int argc,char ** argv)
        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()) {