+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(¤trect, 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;
+ while(from!=to) {
+ TAG*next = from->next;
+ if(swf_isAllowedSpriteTag(from))
+ swf_DeleteTag(from);
+ from = next;
+ }
+ save->next = 0;
+ return save;
+}
+
+static int parametersChange(history_t* history, int frame)
+{
+ int willChange = 0;
+
+ willChange = willChange || history_change(history, frame, "x");
+ willChange = willChange || history_change(history, frame, "y");
+ willChange = willChange || history_change(history, frame, "scalex");
+ willChange = willChange || history_change(history, frame, "scaley");
+ willChange = willChange || history_change(history, frame, "cxform.r0");
+ willChange = willChange || history_change(history, frame, "cxform.g0");
+ willChange = willChange || history_change(history, frame, "cxform.b0");
+ willChange = willChange || history_change(history, frame, "cxform.a0");
+ willChange = willChange || history_change(history, frame, "cxform.r1");
+ willChange = willChange || history_change(history, frame, "cxform.g1");
+ willChange = willChange || history_change(history, frame, "cxform.b1");
+ willChange = willChange || history_change(history, frame, "cxform.a1");
+ willChange = willChange || history_change(history, frame, "rotate");
+ willChange = willChange || history_change(history, frame, "shear");
+ willChange = willChange || history_change(history, frame, "pivot.x");
+ willChange = willChange || history_change(history, frame, "pivot.y");
+ willChange = willChange || history_change(history, frame, "pin.x");
+ willChange = willChange || history_change(history, frame, "pin.y");
+ willChange = willChange || history_change(history, frame, "blendmode");
+ willChange = willChange || history_changeFilter(history, frame);
+
+ return willChange;
+}
+
+static void free_filterlist(FILTERLIST* f_list)
+{
+ int i;
+ for (i = 0; i < f_list->num; i++)
+ {
+ if (f_list->filter[i]->type == FILTERTYPE_GRADIENTGLOW)
+ gradient_free(((FILTER_GRADIENTGLOW*)f_list->filter[i])->gradient);
+ free(f_list->filter[i]);
+ }
+ free(f_list);
+}
+
+static void readParameters(history_t* history, parameters_t* p, int frame)
+{
+ p->x = history_value(history, frame, "x");
+ p->y = history_value(history, frame, "y");
+ p->scalex = history_value(history, frame, "scalex");
+ p->scaley = history_value(history, frame, "scaley");
+ p->cxform.r0 = history_value(history, frame, "cxform.r0");
+ p->cxform.g0 = history_value(history, frame, "cxform.g0");
+ p->cxform.b0 = history_value(history, frame, "cxform.b0");
+ p->cxform.a0 = history_value(history, frame, "cxform.a0");
+ p->cxform.r1 = history_value(history, frame, "cxform.r1");
+ p->cxform.g1 = history_value(history, frame, "cxform.g1");
+ p->cxform.b1 = history_value(history, frame, "cxform.b1");
+ p->cxform.a1 = history_value(history, frame, "cxform.a1");
+ p->rotate = history_rotateValue(history, frame);
+ p->shear = history_value(history, frame, "shear");
+ p->pivot.x = history_value(history, frame, "pivot.x");
+ p->pivot.y = history_value(history, frame, "pivot.y");
+ p->pin.x = history_value(history, frame, "pin.x");
+ p->pin.y = history_value(history, frame, "pin.y");
+ p->blendmode = history_value(history, frame, "blendmode");
+ p->filters = history_filterValue(history, frame);
+}
+
+void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
+{
+ SWFPLACEOBJECT po;
+ FILTERLIST flist;
+ swf_GetPlaceObject(NULL, &po);
+ po.id = id;
+ po.depth = depth;
+ po.matrix = m;
+ po.cxform = p->cxform;
+ po.name = name;
+ po.move = move;
+ if(move)
+ po.id = 0;
+ if(p->blendmode) {
+ po.blendmode = p->blendmode;
+ }
+ if (p->filters)
+ po.filters = p->filters;
+ swf_SetPlaceObject(tag, &po);
+}
+
+static void writeInstance(instance_t* i)
+{
+ parameters_t p;
+ MATRIX m;
+ int frame = i->history->firstFrame;
+ TAG* tag = i->history->firstTag;
+ history_processFlags(i->history);
+ while (frame < currentframe)
+ {
+ frame++;
+ while (tag->id != ST_SHOWFRAME)
+ tag = tag->next;
+ if (parametersChange(i->history, frame))
+ {
+ readParameters(i->history, &p, frame);
+ m = s_instancepos(i->character->size, &p);
+
+ if(p.blendmode || p.filters)
+ tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
+ else
+ tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+ setPlacement(tag, 0, i->depth, m, 0, &p, 1);
+ if (p.filters)
+ free_filterlist(p.filters);
+ }
+ else
+ tag = tag->next;
+ }
+}
+
+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");
+}
+