70ddf3eabf288b51937c47945f4d06447622b153
[swftools.git] / lib / modules / swfaction.c
1 /* swfaction.c
2
3    Actionscript generation and parsing routines
4    
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 #include "../rfxswf.h"
25
26 #define MAX_LOOKUP 1024   // make cross references in dumps
27
28 struct Action
29 {
30     int version;
31     char*name;
32     U8 op;
33     char*flags;
34 } static actions[] =
35 {
36 /*
37 f: frame (word)
38 u: url (string)
39 t: target (string)
40 l: label (string)
41 C: constant pool header (word)
42 c: constant pool entry (string)
43 s: skip (byte) (number of actions)
44 m: method (byte) swf_GetUrl2:(0=none, 1=get, 2=post)/GotoFrame2:(1=play)
45 b: branch (word) (number of bytes)
46 p (push): type(byte), type=0:string, type=1:double
47 {: define function (name (string), num (word), params (num strings), codesize (word)
48 o: codesize (word) object (string)
49 r: register (byte)
50  */
51 {3,"End", 0x00, ""},
52 {3,"GotoFrame", 0x81, "f"},
53 {4,"GotoFrame2", 0x9f, "m"}, // -1 (/Movieclip:3)
54 {3,"GetUrl", 0x83, "ul"},
55 {4,"GetUrl2", 0x9a, "m"}, //-2
56 {3,"NextFrame", 0x04, ""},
57 {3,"PreviousFrame", 0x05, ""},
58 {3,"Play", 0x06, ""},
59 {3,"Stop", 0x07, ""},
60 {3,"ToggleQuality", 0x08, ""},
61 {3,"StopSounds", 0x09, ""},
62 {3,"WaitForFrame", 0x8a, "fs"},
63 {4,"WaitForFrame2", 0x8d, "s"}, // -1
64 {3,"SetTarget", 0x8b, "t"},
65 {4,"SetTarget2", 0x20, ""}, //-1
66 {3,"GotoLabel", 0x8c, "l"},
67 {4,"Add", 0x0a, ""}, //  -2, +1
68 {4,"Multiply", 0x0c, ""}, //  -2, +1
69 {4,"Divide", 0x0d, ""}, //  -2, +1
70 {4,"Subtract", 0x0b, ""}, //  -2, +1
71 {4,"Less", 0x0f, ""}, //  -2, +1
72 {4,"Equals", 0x0e, ""}, //  -2, +1
73 {4,"And", 0x10, ""}, //  -2, +1
74 {4,"Or", 0x11, ""}, //  -2, +1
75 {4,"Not", 0x12, ""}, //  -1, +1
76 {4,"StringAdd", 0x21, ""}, // -2,+1
77 {4,"StringLength", 0x14, ""}, // -1, +1
78 {4,"MBStringLength", 0x31, ""}, // -1, +1
79 {4,"StringEquals", 0x13, ""}, // -2, +1
80 {4,"StringLess", 0x29, ""}, //-2, +1
81 {4,"StringExtract", 0x15, ""}, // -3, +1
82 {4,"MBStringExtract", 0x35, ""}, //-3 +1
83 {4,"Push", 0x96, "p"}, //  +1
84 {4,"Pop", 0x17, ""}, //  -1
85 {4,"ToInteger", 0x18, ""}, // -1, +1
86 {4,"CharToAscii", 0x32, ""}, // -1, +1
87 {4,"AsciiToChar", 0x33, ""}, // -1, +1
88 {4,"MBCharToAscii", 0x36, ""}, // -1, +1
89 {4,"MBAsciiToChar", 0x37, ""}, // -1, +1
90 {4,"Jump", 0x99, "b"},
91 {4,"If", 0x9d, "b"}, // -1
92 {4,"Call", 0x9e, ""}, //-1 (frame label/number) (high bit is wrong.)
93 {4,"GetVariable", 0x1c,""}, // -1, +1
94 {4,"SetVariable", 0x1d,""}, // -2
95 {4,"GetProperty", 0x22,""}, //-2, +1
96 {4,"SetProperty", 0x23, ""}, // -3
97 {4,"RemoveSprite", 0x25, ""}, //-1
98 {4,"StartDrag", 0x27, ""}, // -2, -1, (-4)
99 {4,"EndDrag", 0x28, ""}, 
100 {4,"CloneSprite", 0x24, ""}, // -3
101 {4,"Trace", 0x26, ""}, //-1
102 {4,"GetTime", 0x34, ""}, //+1
103 {4,"RandomNumber", 0x30, ""}, //-1,+1
104 {5,"Modulo", 0x3f,""},
105 {5,"BitAnd", 0x60,""},
106 {5,"BitLShift", 0x63,""},
107 {5,"BitOr", 0x61,""},
108 {5,"BitRShift", 0x64,""},
109 {5,"BitURShift", 0x65,""},
110 {5,"BitXor", 0x62,""},//66?
111 {5,"Decrement", 0x51,""},
112 {5,"Increment", 0x50,""},
113 {5,"PushDuplicate", 0x4c,""},
114 {5,"StackSwap", 0x4d,""}, //?
115 {5,"StoreRegister", 0x87,"r"},
116 {5,"CallFunction", 0x3d,""},
117 {5,"DefineFunction", 0x9b, "{"},
118 {5,"Return", 0x3e,""},
119 {5,"GetMember", 0x4e,""},
120 {5,"SetMember", 0x4f,""},
121 {5,"CallMethod", 0x52,""},
122 {5,"Constantpool", 0x88, "Cc"},
123 {5,"DefineLocal", 0x3c,""},
124 {5,"DefineLocal2", 0x41,""},
125 {5,"Makehash", 0x43, ""}, //??
126 {5,"Delete", 0x3a,""}, //?
127 {5,"Delete2", 0x3b,""},
128 {5,"Enumerate", 0x46,""},
129 {5,"Equals2", 0x49,""},
130 {5,"InitArray", 0x42,""}, // InitObject?
131 {5,"NewMethod", 0x53,""}, //?
132 {5,"NewObject", 0x40,""},
133 {5,"TargetPath", 0x45,""}, //?
134 {5,"With", 0x94, "o"},
135 {5,"ToNumber", 0x4a,""}, //?
136 {5,"ToString", 0x4b,""}, //?
137 {5,"TypeOf", 0x44,""},
138 {5,"Add2", 0x47,""},
139 {5,"Less2", 0x48,""},
140 {6,"Greater", 0x67,""},
141 {6,"StringGreater", 0x68,""},
142 {6,"Enumerate2", 0x55,""},
143 {6,"InstanceOf", 0x54,""},
144 {6,"StrictEquals", 0x66,""}
145 };
146 static int definedactions = sizeof(actions)/sizeof(struct Action);
147
148 ActionTAG* swf_ActionGet(TAG*tag) 
149 {
150     U8 op = 1;
151     int length;
152     ActionTAG tmp;
153     ActionTAG*action = &tmp;
154     U8*data;
155     while(op)
156     {
157         action->next = (ActionTAG*)rfx_calloc(sizeof(ActionTAG));
158         action->next->prev = action;
159         action->next->next = 0;
160         action->next->parent = tmp.next;
161         action = action->next;
162
163         op = swf_GetU8(tag);
164         if(op<0x80)
165             length = 0;
166         else
167             length = swf_GetU16(tag);
168
169         if(length) {
170             data = rfx_alloc(length);
171             swf_GetBlock(tag, data, length);
172         } else {
173           data = 0;
174         }
175         action->op = op;
176         action->len = length;
177         action->data = data;
178     }
179     return tmp.next;
180 }
181
182 void swf_ActionFree(ActionTAG*action)
183 {
184     if(!action) {
185         fprintf(stderr, "Warning: freeing zero action");
186         return;
187     }
188     action = action->parent;
189     if(!action) {
190         fprintf(stderr, "Warning: freeing zero action (no parent)");
191         return;
192     }
193
194     while(action)
195     {
196         ActionTAG*tmp;
197         if(action->data && action->data != action->tmp) {
198             rfx_free(action->data);
199             action->data = 0;
200         }
201         action->len = 0;
202         
203         tmp = action;
204         action=action->next;
205         rfx_free(tmp);
206     }
207 }
208
209 void swf_ActionSet(TAG*tag, ActionTAG*action)
210 {
211     action=action->parent;
212     while(action)
213     {
214         swf_SetU8(tag, action->op);
215         if(action->op & 128)
216           swf_SetU16(tag, action->len);
217
218         swf_SetBlock(tag, action->data, action->len);
219
220         action = action->next;
221     }
222 }
223
224 int OpAdvance(char c, U8*data)
225 {
226     switch (c)
227     {
228         case 'f':
229             return 2;
230         case 'u':
231             return strlen(data)+1;
232         case 't':
233             return strlen(data)+1;
234         case 'l': 
235             return strlen(data)+1;
236         case 'c': 
237             return strlen(data)+1;
238         case 'C': 
239             return 2;
240         case 's':
241             return 1;
242         case 'm':
243             return 1;
244         case 'b':
245             return 2;
246         case 'r':
247             return 1;
248         case 'p': {
249             U8 type = *data++;
250             if(type == 0) {
251                 return 1+strlen(data)+1; //string
252             } else if (type == 1) {
253                 return 1+4; //float
254             } else if (type == 2) {
255                 return 1+0; //NULL
256             } else if (type == 3) {
257                 return 1+0; //Undefined
258             } else if (type == 4) {
259                 return 1+1; //register
260             } else if (type == 5) {
261                 return 1+1; //bool
262             } else if (type == 6) {
263                 return 1+8; //double
264             } else if (type == 7) {
265                 return 1+4; //int
266             } else if (type == 8) {
267                 return 1+1; //lookup
268             } else if (type == 9) {
269                 return 1+2; //lookup 16
270             } else return 1;
271             break;
272         }
273         case 'o': {
274             return 2;
275         }
276         case '{': {
277             U16 num;
278             U16 codesize;
279             U8* odata = data;
280             int t;
281             while(*data++); //name
282             num = (*data++)*256; //num
283             num += (*data++);
284             for(t=0;t<num;t++)
285                 while(*data++); //param
286             codesize = (*data++)*256; //num
287             codesize += (*data++);
288             return data-odata;
289         }
290     }
291     return 0;
292 }
293 #define ATAG_FULLLENGTH(atag) ((atag)->len + 1 + ((atag)->op&0x80?2:0))
294 #define MAX_LEVELS 16
295 /* TODO: * this should be in swfdump.c */
296 void swf_DumpActions(ActionTAG*atag, char*prefix) 
297 {
298     int t;
299     U8*data;
300     char* cp;
301     int entry = 0;
302     char spaces[MAX_LEVELS*4+1];
303     struct {
304         char*text;
305         int count;
306     } counter[MAX_LEVELS];
307     int countpos = 0;
308 #ifdef MAX_LOOKUP
309     char * lookup[MAX_LOOKUP];
310     memset(lookup,0x00,sizeof(lookup));
311 #endif
312     memset(spaces, 32, sizeof(spaces));
313     spaces[sizeof(spaces)-1] = 0;
314
315    if (!prefix)
316         prefix="";
317
318     while(atag)
319     {
320         char*indent = &spaces[sizeof(spaces)-1-countpos*4];
321         U16 poollen = 0;
322         for(t=0;t<definedactions;t++)
323             if(actions[t].op == atag->op)
324                 break;
325
326         if(t==definedactions) {
327             printf("%s (%5d bytes) action:%s unknown[%02x]", prefix, atag->len, indent, atag->op);
328         } else {
329             printf("%s (%5d bytes) action:%s %s", prefix, atag->len, indent, actions[t].name);
330         }
331         data = atag->data;
332         if(atag->len && t!=definedactions) //TODO: check for consistency: should we have a length?
333         {
334           cp = actions[t].flags;
335           while(*cp)
336           {
337               switch(*cp)
338               {
339                   case 'f': { //frame
340                       printf(" %d", data[0]+256*data[1]);
341                   } break;
342                   case 'u': {
343                       printf(" URL:\"%s\"", data);
344                   } break;
345                   case 't': {
346                       printf(" Target:\"%s\"", data);
347                   } break;
348                   case 'l': {
349                       printf(" Label:\"%s\"", data);
350                   } break;
351                   case 'c': {
352                       printf(" String:\"%s\"", data);
353 #ifdef MAX_LOOKUP
354                       if (entry<MAX_LOOKUP)
355                         lookup[entry++] = strdup(data);
356 #endif
357                   } break;
358                   case 'C': {
359                       poollen = data[0]+256*data[1];
360                       entry = 0;
361                       printf("(%d entries)", poollen);
362                   } break;
363                   case 's': {
364                       printf(" +%d", *data);
365                   } break;
366                   case 'm': {
367                       //m: method (byte) url:(0=none, 1=get, 2=datat)/gf2:(1=play)
368                       printf(" %d", *data);
369                   } break;
370                   case '{': {
371                       U16 num;
372                       U16 codesize;
373                       int s = 0;
374                       int t;
375                       printf(" %s(", data);
376                       while(data[s++]); //name
377                       num = (data[s++]); //num
378                       num += (data[s++])*256;
379                       for(t=0;t<num;t++) {
380                           printf("%s",data+s);  // 10/22/04 MD: added +s to
381                           if(t<num-1)
382                               printf(", ");
383                           while(data[s++]); //param
384                       }
385                       printf(")");
386                       codesize = (data[s++]); //num
387                       codesize += (data[s++])*256;
388                       printf(" codesize:%d ",codesize);
389                       printf("\n%s                       %s{", prefix, indent);
390                       if(countpos>=15) {
391                           printf("Error: nested too deep\n");
392                           continue;
393                       }
394                       counter[countpos].text = "}";
395                       counter[countpos].count = codesize + ATAG_FULLLENGTH(atag);
396                       countpos++;
397                   } break;
398                   case 'o': {
399                       int t;
400                       U16 codesize = data[0]+256*data[1];
401                       printf(" codesize:%d ", codesize);
402
403                       /* the following tries to find the "string"
404                          the flash documentation speaks of- I've
405                          never actually seen one yet. -mk */
406                       for(t=2;t<atag->len;t++)
407                           printf("[%02x]", atag->data[t]);
408
409                       printf("\n%s                       %s{", prefix, indent);
410                       if(countpos>=15) {
411                           printf("Error: nested too deep\n");
412                           continue;
413                       }
414                       counter[countpos].text = "}";
415                       counter[countpos].count = codesize + ATAG_FULLLENGTH(atag);
416                       countpos++;
417                   } break;
418                   case 'b': {
419                       printf(" %d", data[0]+256*(signed char)data[1]);
420                   } break;
421                   case 'r': {
422                       printf(" %d", data[0]);
423                   } break;
424                   case 'p': {
425                       U8 type = *data;
426                       unsigned char*value = data+1;
427                       if(type == 0) {
428                           printf(" String:\"%s\"", value);
429                       } else if (type == 1) {
430                           U32 f = value[0]+(value[1]<<8)+
431                                   (value[2]<<16)+(value[3]<<24);
432                           printf(" Float:%f", *(float*)&f);
433                       } else if (type == 2) {
434                           printf(" NULL");
435                       } else if (type == 3) {
436                           printf(" Undefined");
437                       } else if (type == 4) {
438                           printf(" register:%d", *value);
439                       } else if (type == 5) {
440                           printf(" bool:%s", *value?"true":"false");
441                       } else if (type == 6) {
442                           U8 a[8];
443                           int t;
444                           memcpy(&a[4],value,4);
445                           memcpy(a,&value[4],4);
446 #ifdef WORDS_BIGENDIAN
447                           for(t=0;t<4;t++) {
448                               U8 tmp = a[t];
449                               a[t]=a[7-t];
450                               a[7-t] = tmp;
451                           }
452 #endif
453                           printf(" double:%f", *(double*)a);
454                       } else if (type == 7) {
455                           printf(" int:%d", value[0]+(value[1]<<8)+
456                                             (value[2]<<16)+(value[3]<<24));
457                       } else if (type == 8) {
458                           printf(" Lookup:%d", *value);
459 #ifdef MAX_LOOKUP
460                           if (lookup[*value])
461                             printf(" (\"%s\")",lookup[*value]);
462 #endif
463                       } else if (type == 9) {
464                           U32 offset = value[0]+(value[1]<<8);
465                           printf(" Lookup16:%d", offset);
466 #ifdef MAX_LOOKUP
467                           if (lookup[offset])
468                             printf(" (\"%s\")",lookup[offset]);
469 #endif
470                       } else {
471                           printf(" UNKNOWN[%02x]",type);
472                       }
473                   } break;
474               }
475               data += OpAdvance(*cp, data);
476               if((*cp!='c' || !poollen) &&
477                  (*cp!='p' || !(data<&atag->data[atag->len])))
478                   cp++;
479               if(poollen)
480                   poollen--;
481           }
482         }
483
484         if(data < atag->data + atag->len)
485         {
486             int nl = ((atag->data+atag->len)-data);
487             int t;
488             printf(" (remainder of %d bytes:\"", nl);
489             for(t=0;t<nl;t++) {
490                 if(data[t]<32)
491                     printf("\\%d",data[t]);
492                 else
493                     printf("%c", data[t]);
494             }
495             printf("\")");
496         }
497         printf("\n");
498
499         for(t=0;t<countpos;t++) {
500             counter[t].count -= ATAG_FULLLENGTH(atag);
501             if(counter[t].count < 0) {
502                 printf("===== Error: Oplength errors =====\n");
503                 countpos = 0;
504                 break;
505             }
506         }
507
508         while(countpos && !counter[countpos-1].count)
509         {
510             printf("%s                   %s%s\n", 
511                 prefix, indent, counter[countpos-1].text);
512             indent += 4;
513             countpos--;
514         }
515
516         atag = atag->next;
517     }
518
519 #ifdef MAX_LOOKUP
520   for (t=0;t<MAX_LOOKUP;t++) if (lookup[t]) rfx_free(lookup[t]);
521 #endif
522 }
523
524 static const char TYPE_URL = 1;
525 static const char TYPE_TARGET = 2;
526 static const char TYPE_STRING = 4;
527
528 int swf_ActionEnumerate(ActionTAG*atag, char*(*callback)(char*), int type)
529 {
530     int t;
531     U8*data;
532     char* cp;
533     int count = 0;
534     while(atag)
535     {
536         U16 poollen = 0;
537         for(t=0;t<definedactions;t++)
538             if(actions[t].op == atag->op)
539                 break;
540
541         if(t==definedactions) {
542             // unknown actiontag
543             atag = atag->next;
544             count++;
545             continue;
546         }
547         cp = actions[t].flags;
548         data = atag->data;
549         if(atag->len) {
550             while(*cp) {
551                 U8 * replacepos = 0;
552                 int replacelen = 0;
553                 U8 * replacement = 0;
554                 switch(*cp)
555                 {
556                     case 'u': {
557                         if(type&TYPE_URL)
558                         {
559                             replacelen = strlen(data);
560                             replacepos = data;
561                             replacement = callback(data); // may be null
562                         }
563                     } break;
564                     case 't': {
565                         if(type&TYPE_TARGET)
566                         {
567                             replacelen = strlen(data);
568                             replacepos = data;
569                             replacement = callback(data); // may be null
570                         }
571                     } break;
572                     case 'c': {
573                         if(type&TYPE_STRING)
574                         {
575                             replacelen = strlen(data);
576                             replacepos = data;
577                             replacement = callback(data); // may be null
578                         }
579                     } break;
580                     case 'C': {
581                         poollen = (data[0]+256*data[1]);
582                     } break;
583                     case 'o': {
584                     } break;
585                     case 'p': {
586                         U8 datatype = *data;
587                         char*value = &data[1];
588                         if(datatype == 0) { //string
589                             if(type&TYPE_STRING)
590                             {
591                                 replacelen = strlen(value);
592                                 replacepos = value;
593                                 replacement = callback(value); // may be null
594                             }
595                         } else if (datatype == 8) { //lookup
596                         }
597                     } break;
598                 }
599                 data += OpAdvance(*cp, data);
600                 if(*cp!='c' || !poollen)
601                     cp++;
602                 if(poollen)
603                     poollen--;
604
605                 if(replacement)
606                 {
607                     int newlen = strlen(replacement);
608                     char * newdata = rfx_alloc(atag->len - replacelen + newlen);
609                     int rpos = replacepos - atag->data;
610                     memcpy(newdata, atag->data, rpos);
611                     memcpy(&newdata[rpos], replacement, newlen);
612                     memcpy(&newdata[rpos+newlen], &replacepos[replacelen],
613                             &data[atag->len] - &replacepos[replacelen]);
614                     rfx_free(atag->data);
615                     atag->data = newdata;
616                     data = &atag->data[rpos+newlen+1];
617                 }
618             }
619         }
620         atag = atag->next;
621         count ++;
622     }
623     return count;
624 }
625
626 void swf_ActionEnumerateTargets(ActionTAG*atag, char*(*callback)(char*))
627 {
628     swf_ActionEnumerate(atag, callback, TYPE_TARGET);
629 }
630 void swf_ActionEnumerateStrings(ActionTAG*atag, char*(*callback)(char*))
631 {
632     swf_ActionEnumerate(atag, callback, TYPE_STRING);
633 }
634 void swf_ActionEnumerateURLs(ActionTAG*atag, char*(*callback)(char*))
635 {
636     swf_ActionEnumerate(atag, callback, TYPE_URL);
637 }
638
639 /*static ActionTAG* swf_ActionStart()
640 {
641     ActionTAG*atag;
642     atag = (ActionTAG*)rfx_alloc(sizeof(ActionTAG));
643     atag->prev = 0;
644     atag->next = 0;
645     atag->parent = 0;
646     atag->data = 0;
647     atag->len = 0;
648     return atag;
649 }
650
651 void swf_ActionEnd(ActionTAG* atag)
652 {
653     ActionTAG*last;
654     while(atag) {
655         last = atag;
656         atag=atag->next;
657     } 
658
659     last->prev->next = 0;
660     rfx_free(last);
661 }*/
662
663 static ActionTAG*lastATAG(ActionTAG*atag)
664 {
665     ActionTAG*last = 0;
666     while(atag) {
667         last = atag;
668         atag=atag->next;
669     } 
670     return last;
671 }
672
673 ActionTAG* swf_AddActionTAG(ActionTAG*atag, U8 op, U8*data, U16 len)
674 {
675     ActionTAG*tmp;
676     tmp = (ActionTAG*)rfx_alloc(sizeof(ActionTAG));
677     tmp->next = 0;
678     if(atag) {
679         tmp->prev = atag;
680         atag->next = tmp;
681         tmp->parent = atag->parent;
682     } else {
683         tmp->prev = 0;
684         tmp->parent = tmp;
685     }
686     if(data || !len) {
687         tmp->data = data;
688     } else {
689         tmp->data = tmp->tmp;
690     }
691
692     tmp->len = len;
693     tmp->op = op;
694     return tmp;
695 }
696
697 ActionMarker action_setMarker(ActionTAG*atag)
698 {
699     ActionMarker m;
700     m.atag = atag;
701     return m;
702 }
703
704 int inline ActionTagSize(ActionTAG*atag)
705 {
706     return (atag->op&0x80)?3+(atag->len):1+0;
707 }
708
709
710 #define ACTION_END            0x00
711 #define ACTION_NEXTFRAME      0x04
712 #define ACTION_PREVIOUSFRAME  0x05
713 #define ACTION_PLAY           0x06
714 #define ACTION_STOP           0x07
715 #define ACTION_TOGGLEQUALITY  0x08
716 #define ACTION_STOPSOUNDS     0x09
717 #define ACTION_ADD            0x0a
718 #define ACTION_SUBTRACT       0x0b
719 #define ACTION_MULTIPLY       0x0c
720 #define ACTION_DIVIDE         0x0d
721 #define ACTION_EQUALS         0x0e
722 #define ACTION_LESS           0x0f
723 #define ACTION_AND            0x10
724 #define ACTION_OR             0x11
725 #define ACTION_NOT            0x12
726 #define ACTION_STRINGEQUALS   0x13
727 #define ACTION_STRINGLENGTH   0x14
728 #define ACTION_STRINGEXTRACT  0x15
729 #define ACTION_POP            0x17
730 #define ACTION_TOINTEGER      0x18
731 #define ACTION_GETVARIABLE    0x1c
732 #define ACTION_SETVARIABLE    0x1d
733 #define ACTION_SETTARGET2     0x20
734 #define ACTION_STRINGADD      0x21
735 #define ACTION_GETPROPERTY    0x22
736 #define ACTION_SETPROPERTY    0x23
737 #define ACTION_CLONESPRITE    0x24
738 #define ACTION_REMOVESPRITE   0x25
739 #define ACTION_TRACE          0x26
740 #define ACTION_STARTDRAG      0x27
741 #define ACTION_ENDDRAG        0x28
742 #define ACTION_STRINGLESS     0x29
743 #define ACTION_RANDOMNUMBER   0x30
744 #define ACTION_MBSTRINGLENGTH 0x31
745 #define ACTION_CHARTOASCII    0x32
746 #define ACTION_ASCIITOCHAR    0x33
747 #define ACTION_GETTIME        0x34
748 #define ACTION_MBSTRINGEXTRACT 0x35
749 #define ACTION_MBCHARTOASCII  0x36
750 #define ACTION_MBASCIITOCHAR  0x37
751 #define ACTION_DELETE         0x3a
752 #define ACTION_DELETE2        0x3b
753 #define ACTION_DEFINELOCAL    0x3c
754 #define ACTION_CALLFUNCTION   0x3d
755 #define ACTION_RETURN         0x3e
756 #define ACTION_MODULO         0x3f
757 #define ACTION_NEWOBJECT      0x40
758 #define ACTION_DEFINELOCAL2   0x41
759 #define ACTION_INITARRAY      0x42
760 #define ACTION_MAKEHASH       0x43
761 #define ACTION_TYPEOF         0x44
762 #define ACTION_TARGETPATH     0x45
763 #define ACTION_ENUMERATE      0x46
764 #define ACTION_ADD2           0x47
765 #define ACTION_LESS2          0x48
766 #define ACTION_EQUALS2        0x49
767 #define ACTION_TONUMBER       0x4a
768 #define ACTION_TOSTRING       0x4b
769 #define ACTION_PUSHDUPLICATE  0x4c
770 #define ACTION_STACKSWAP      0x4d
771 #define ACTION_GETMEMBER      0x4e
772 #define ACTION_SETMEMBER      0x4f
773 #define ACTION_INCREMENT      0x50
774 #define ACTION_DECREMENT      0x51
775 #define ACTION_CALLMETHOD     0x52
776 #define ACTION_NEWMETHOD      0x53
777 #define ACTION_BITAND         0x60
778 #define ACTION_BITOR          0x61
779 #define ACTION_BITXOR         0x62
780 #define ACTION_BITLSHIFT      0x63
781 #define ACTION_BITRSHIFT      0x64
782 #define ACTION_BITURSHIFT     0x65
783 #define ACTION_GOTOFRAME      0x81
784 #define ACTION_GETURL         0x83
785 #define ACTION_STOREREGISTER  0x87
786 #define ACTION_CONSTANTPOOL   0x88
787 #define ACTION_WAITFORFRAME   0x8a
788 #define ACTION_SETTARGET      0x8b
789 #define ACTION_GOTOLABEL      0x8c
790 #define ACTION_WAITFORFRAME2  0x8d
791 #define ACTION_WITH           0x94
792 #define ACTION_PUSH           0x96
793 #define ACTION_JUMP           0x99
794 #define ACTION_GETURL2        0x9a
795 #define ACTION_DEFINEFUNCTION 0x9b
796 #define ACTION_IF             0x9d
797 #define ACTION_CALL           0x9e
798 #define ACTION_GOTOFRAME2     0x9f
799
800 void action_fixjump(ActionMarker m1, ActionMarker m2)
801 {
802     ActionTAG* a1 = m1.atag;
803     ActionTAG* a2 = m2.atag;
804     ActionTAG* a;
805     int len = 0;
806     int oplen = 0;
807     a = a1;
808     
809     a = a->next; //first one is free
810     while(a && a!=a2)
811     {
812         len += ActionTagSize(a);
813         oplen ++;
814         a = a->next;
815     }
816     if(!a)
817     { len = 0;
818       oplen = 0;
819       a = a2;
820       while(a && a!=a1) {
821           len -= ActionTagSize(a);
822           oplen --;
823           a = a->next;
824       }
825       if(!a) {
826           fprintf(stderr, "action_fixjump: couldn't find second tag\n");
827           return;
828       }
829       len -= ActionTagSize(a);
830       oplen --;
831     }
832
833     if (a1->op == ACTION_IF || a1->op == ACTION_JUMP) 
834     {
835         *(U16*)(a1->data) = SWAP16(len);
836     }
837     else if(a1->op == ACTION_WAITFORFRAME)
838     {
839         ((U8*)(a1->data))[2] = oplen;
840     }
841     else if(a1->op == ACTION_WAITFORFRAME2)
842     {
843         ((U8*)(a1->data))[0] = oplen;
844     }
845     
846 }
847
848 ActionTAG* action_NextFrame(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_NEXTFRAME, 0, 0);}
849 ActionTAG* action_PreviousFrame(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_PREVIOUSFRAME, 0, 0);}
850 ActionTAG* action_Play(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_PLAY, 0, 0);}
851 ActionTAG* action_Stop(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STOP, 0, 0);}
852 ActionTAG* action_ToggleQuality(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_TOGGLEQUALITY, 0, 0);}
853 ActionTAG* action_StopSounds(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STOPSOUNDS, 0, 0);}
854 ActionTAG* action_Add(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_ADD, 0, 0);}
855 ActionTAG* action_Subtract(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_SUBTRACT, 0, 0);}
856 ActionTAG* action_Multiply(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_MULTIPLY, 0, 0);}
857 ActionTAG* action_Divide(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_DIVIDE, 0, 0);}
858 ActionTAG* action_Equals(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_EQUALS, 0, 0);}
859 ActionTAG* action_Less(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_LESS, 0, 0);}
860 ActionTAG* action_And(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_AND, 0, 0);}
861 ActionTAG* action_Or(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_OR, 0, 0);}
862 ActionTAG* action_Not(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_NOT, 0, 0);}
863 ActionTAG* action_StringEquals(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STRINGEQUALS, 0, 0);}
864 ActionTAG* action_StringLength(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STRINGLENGTH, 0, 0);}
865 ActionTAG* action_StringExtract(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STRINGEXTRACT, 0, 0);}
866 ActionTAG* action_Pop(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_POP, 0, 0);}
867 ActionTAG* action_ToInteger(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_TOINTEGER, 0, 0);}
868 ActionTAG* action_GetVariable(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_GETVARIABLE, 0, 0);}
869 ActionTAG* action_SetVariable(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_SETVARIABLE, 0, 0);}
870 ActionTAG* action_SetTarget2(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_SETTARGET2, 0, 0);}
871 ActionTAG* action_StringAdd(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STRINGADD, 0, 0);}
872 ActionTAG* action_GetProperty(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_GETPROPERTY, 0, 0);}
873 ActionTAG* action_SetProperty(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_SETPROPERTY, 0, 0);}
874 ActionTAG* action_CloneSprite(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_CLONESPRITE, 0, 0);}
875 ActionTAG* action_RemoveSprite(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_REMOVESPRITE, 0, 0);}
876 ActionTAG* action_Trace(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_TRACE, 0, 0);}
877 ActionTAG* action_StartDrag(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STARTDRAG, 0, 0);}
878 ActionTAG* action_EndDrag(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_ENDDRAG, 0, 0);}
879 ActionTAG* action_StringLess(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STRINGLESS, 0, 0);}
880 ActionTAG* action_RandomNumber(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_RANDOMNUMBER, 0, 0);}
881 ActionTAG* action_MBStringLength(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_MBSTRINGLENGTH, 0, 0);}
882 ActionTAG* action_CharToAscii(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_CHARTOASCII, 0, 0);}
883 ActionTAG* action_AsciiToChar(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_ASCIITOCHAR, 0, 0);}
884 ActionTAG* action_GetTime(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_GETTIME, 0, 0);}
885 ActionTAG* action_MBStringExtract(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_MBSTRINGEXTRACT, 0, 0);}
886 ActionTAG* action_MBCharToAscii(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_MBCHARTOASCII, 0, 0);}
887 ActionTAG* action_MBAsciiToChar(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_MBASCIITOCHAR, 0, 0);}
888 ActionTAG* action_Delete(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_DELETE, 0, 0);}
889 ActionTAG* action_Delete2(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_DELETE2, 0, 0);}
890 ActionTAG* action_DefineLocal(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_DEFINELOCAL, 0, 0);}
891 ActionTAG* action_CallFunction(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_CALLFUNCTION, 0, 0);}
892 ActionTAG* action_Return(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_RETURN, 0, 0);}
893 ActionTAG* action_Modulo(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_MODULO, 0, 0);}
894 ActionTAG* action_NewObject(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_NEWOBJECT, 0, 0);}
895 ActionTAG* action_DefineLocal2(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_DEFINELOCAL2, 0, 0);}
896 ActionTAG* action_InitArray(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_INITARRAY, 0, 0);}
897 ActionTAG* action_Makehash(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_MAKEHASH, 0, 0);}
898 ActionTAG* action_TypeOf(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_TYPEOF, 0, 0);}
899 ActionTAG* action_TargetPath(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_TARGETPATH, 0, 0);}
900 ActionTAG* action_Enumerate(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_ENUMERATE, 0, 0);}
901 ActionTAG* action_Add2(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_ADD2, 0, 0);}
902 ActionTAG* action_Less2(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_LESS2, 0, 0);}
903 ActionTAG* action_Equals2(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_EQUALS2, 0, 0);}
904 ActionTAG* action_ToNumber(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_TONUMBER, 0, 0);}
905 ActionTAG* action_ToString(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_TOSTRING, 0, 0);}
906 ActionTAG* action_PushDuplicate(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_PUSHDUPLICATE, 0, 0);}
907 ActionTAG* action_StackSwap(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_STACKSWAP, 0, 0);}
908 ActionTAG* action_GetMember(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_GETMEMBER, 0, 0);}
909 ActionTAG* action_SetMember(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_SETMEMBER, 0, 0);}
910 ActionTAG* action_Increment(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_INCREMENT, 0, 0);}
911 ActionTAG* action_Decrement(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_DECREMENT, 0, 0);}
912 ActionTAG* action_CallMethod(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_CALLMETHOD, 0, 0);}
913 ActionTAG* action_NewMethod(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_NEWMETHOD, 0, 0);}
914 ActionTAG* action_BitAnd(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_BITAND, 0, 0);}
915 ActionTAG* action_BitOr(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_BITOR, 0, 0);}
916 ActionTAG* action_BitXor(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_BITXOR, 0, 0);}
917 ActionTAG* action_BitLShift(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_BITLSHIFT, 0, 0);}
918 ActionTAG* action_BitRShift(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_BITRSHIFT, 0, 0);}
919 ActionTAG* action_BitURShift(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_BITURSHIFT, 0, 0);}
920 ActionTAG* action_Call(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_CALL, 0, 0);}
921 ActionTAG* action_End(ActionTAG*atag) {return swf_AddActionTAG(atag, ACTION_END, 0, 0);}
922 ActionTAG* action_GotoFrame(ActionTAG*atag, U16 frame) 
923 {
924     atag = swf_AddActionTAG(atag, ACTION_GOTOFRAME, 0, 2);
925     *(U16*)atag->tmp = SWAP16(frame);
926     return atag;
927 }
928
929 ActionTAG* action_Jump(ActionTAG*atag, U16 branch) 
930 {
931     atag = swf_AddActionTAG(atag, ACTION_JUMP, 0, 2);
932     *(U16*)atag->tmp = SWAP16(branch);
933     return atag;
934 }
935 ActionTAG* action_If(ActionTAG*atag, U16 branch) 
936 {
937     atag = swf_AddActionTAG(atag, ACTION_IF, 0, 2);
938     *(U16*)atag->tmp = SWAP16(branch);
939     return atag;
940 }
941 ActionTAG* action_StoreRegister(ActionTAG*atag, U8 reg) 
942 {
943     atag = swf_AddActionTAG(atag, ACTION_STOREREGISTER, 0, 1);
944     *(U8*)atag->tmp = reg;
945     return atag;
946 }
947 ActionTAG* action_GotoFrame2(ActionTAG*atag, U8 method) 
948 {
949     atag = swf_AddActionTAG(atag, ACTION_GOTOFRAME2, 0, 1);
950     *(U8*)atag->tmp = method;
951     return atag;
952 }
953 ActionTAG* action_GetUrl2(ActionTAG*atag, U8 method) 
954 {
955     atag = swf_AddActionTAG(atag, ACTION_GETURL2, 0, 1);
956     *(U8*)atag->tmp = method;
957     return atag;
958 }
959 ActionTAG* action_WaitForFrame2(ActionTAG*atag, U8 skip) 
960 {
961     atag = swf_AddActionTAG(atag, ACTION_WAITFORFRAME2, 0, 1);
962     *(U8*)atag->tmp = skip;
963     return atag;
964 }
965 ActionTAG* action_WaitForFrame(ActionTAG*atag, U16 frame, U8 skip) 
966 {
967     atag = swf_AddActionTAG(atag, ACTION_WAITFORFRAME, 0, 3);
968     *(U16*)atag->tmp = SWAP16(frame);
969     *(U8*)&atag->tmp[2] = skip;
970     return atag;
971 }
972 ActionTAG* action_SetTarget(ActionTAG*atag, char* target)
973 {
974     char*ptr = strdup(target);
975     return swf_AddActionTAG(atag, ACTION_SETTARGET, (U8*)ptr, strlen(ptr)+1);
976 }
977 ActionTAG* action_PushNULL(ActionTAG*atag) 
978 {
979     atag = swf_AddActionTAG(atag, ACTION_PUSH, 0, 1);
980     *(U8*)atag->tmp = 2; //NULL
981     return atag;
982 }
983 ActionTAG* action_PushUndefined(ActionTAG*atag) 
984 {
985     atag = swf_AddActionTAG(atag, ACTION_PUSH, 0, 1);
986     *(U8*)atag->tmp = 3; //Undefined
987     return atag;
988 }
989 ActionTAG* action_PushBoolean(ActionTAG*atag, char c) 
990 {
991     atag = swf_AddActionTAG(atag, ACTION_PUSH, 0, 2);
992     *(U8*)atag->tmp = 5; //bool
993     *(U8*)&atag->tmp[1] = c;
994     return atag;
995 }
996 ActionTAG* action_PushRegister(ActionTAG*atag, U8 reg) 
997 {
998     atag = swf_AddActionTAG(atag, ACTION_PUSH, 0, 2);
999     *(U8*)atag->tmp = 4; //register
1000     *(U8*)&atag->tmp[1] = reg;
1001     return atag;
1002 }
1003 ActionTAG* action_PushLookup(ActionTAG*atag, U8 index) 
1004 {
1005     atag = swf_AddActionTAG(atag, ACTION_PUSH, 0, 2);
1006     *(U8*)atag->tmp = 8; //lookup
1007     *(U8*)&atag->tmp[1] = index;
1008     return atag;
1009 }
1010 ActionTAG* action_PushLookup16(ActionTAG*atag, U16 index) 
1011 {
1012     atag = swf_AddActionTAG(atag, ACTION_PUSH, 0, 3);
1013     *(U8*)atag->tmp = 9; //lookup
1014     *(U8*)&atag->tmp[1] = index;
1015     *(U8*)&atag->tmp[2] = index>>8;
1016     return atag;
1017 }
1018 ActionTAG* action_PushString(ActionTAG*atag, const char*str) 
1019 {
1020     int l = strlen(str);
1021     char*ptr = (char*)rfx_alloc(l+2);
1022     ptr[0] = 0; // string
1023     strcpy(&ptr[1], str);
1024     return swf_AddActionTAG(atag, ACTION_PUSH, (U8*)ptr, l+2);
1025 }
1026 ActionTAG* action_PushFloat(ActionTAG*atag, float f)
1027 {
1028     char*ptr = (char*)rfx_alloc(5);
1029     U32 fd = *(U32*)&f;
1030     ptr[0] = 1; //float
1031     ptr[1]  = fd;
1032     ptr[2]  = fd>>8;
1033     ptr[3]  = fd>>16;
1034     ptr[4]  = fd>>24;
1035     return swf_AddActionTAG(atag, ACTION_PUSH, (U8*)ptr, 5);
1036 }
1037 ActionTAG* action_PushDouble(ActionTAG*atag, double d) 
1038 {
1039     char*ptr = (char*)rfx_alloc(9);
1040     U8*dd = (U8*)&d;
1041     ptr[0] = 6; //double
1042 #ifdef WORDS_BIGENDIAN
1043     ptr[1] = dd[7];ptr[2] = dd[6];
1044     ptr[3] = dd[5];ptr[4] = dd[4];
1045     ptr[5] = dd[3];ptr[6] = dd[2];
1046     ptr[7] = dd[1];ptr[8] = dd[0];
1047 #else
1048     ptr[1] = dd[0];ptr[2] = dd[1];
1049     ptr[3] = dd[2];ptr[4] = dd[3];
1050     ptr[5] = dd[4];ptr[6] = dd[5];
1051     ptr[7] = dd[6];ptr[8] = dd[7];
1052 #endif
1053     return swf_AddActionTAG(atag, ACTION_PUSH, (U8*)ptr, 9);
1054 }
1055 ActionTAG* action_PushInt(ActionTAG*atag, int i)
1056 {
1057     atag = swf_AddActionTAG(atag, ACTION_PUSH, 0, 5);
1058     atag->tmp[0] = 7; //int
1059     atag->tmp[1] = i;
1060     atag->tmp[2] = i>>8;
1061     atag->tmp[3] = i>>16;
1062     atag->tmp[4] = i>>24;
1063     return atag;
1064 }
1065 ActionTAG* action_GotoLabel(ActionTAG*atag, char* label)
1066 {
1067     char*ptr = strdup(label);
1068     return swf_AddActionTAG(atag, ACTION_GOTOLABEL, (U8*)ptr, strlen(ptr));
1069 }
1070 ActionTAG* action_GetUrl(ActionTAG*atag, const char* url, char* label) 
1071 {
1072     int l1= strlen(url);
1073     int l2= strlen(label);
1074     char*ptr = rfx_alloc(l1+l2+2);
1075     strcpy(ptr, url);
1076     strcpy(&ptr[l1+1], label);
1077     return swf_AddActionTAG(atag, ACTION_GETURL, ptr, l1+l2+2);
1078 }
1079 //TODO:
1080 ActionTAG* action_DefineFunction(ActionTAG*atag, U8*data, int len) {return atag;}
1081 ActionTAG* action_Constantpool(ActionTAG*atag, char* constantpool) {return atag;}
1082 ActionTAG*  action_With(ActionTAG*atag, char*object) {return atag;}
1083
1084 #include "../action/actioncompiler.h"
1085
1086 ActionTAG* swf_ActionCompile(const char* source, int version)
1087 {
1088     TAG* tag;
1089     ActionTAG* a = 0;
1090     void*buffer = 0;
1091     int len = 0;
1092     int ret;
1093     
1094     tag = swf_InsertTag(NULL, ST_DOACTION);
1095     ret = compileSWFActionCode(source, version, &buffer, &len);
1096     if(!ret || buffer==0 || len == 0)
1097         return 0;
1098
1099     swf_SetBlock(tag, buffer, len);
1100     swf_SetU8(tag, 0);
1101
1102     rfx_free(buffer);
1103
1104     a = swf_ActionGet(tag);
1105     swf_DeleteTag(tag);
1106     return a;
1107 }
1108
1109
1110 /*
1111   Properties:
1112
1113   _X 0
1114   _Y 1
1115   _xscale 2
1116   _yscale 3
1117   _currentframe 4
1118   _totalframes 5
1119   _alpha 6
1120   _visible 7
1121   _width 8
1122   _height 9
1123   _rotation 10
1124   _target 11
1125   _framesloaded 12
1126   _name 13
1127   _droptarget 14
1128   _url 15
1129   _highquality 16
1130   _focusrect 17
1131   _soundbuftime 18
1132   _quality* 19
1133   _xmouse* 20
1134   _ymouse* 21
1135 */