added action enumeration routines
[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 file is distributed under the GPL, see file COPYING for details 
11
12 */
13
14 #include "../rfxswf.h"
15
16 struct Action
17 {
18     int version;
19     char*name;
20     U8 op;
21     char*flags;
22 } static actions[] =
23 {
24 /*
25 f: frame (word)
26 u: url (string)
27 t: target (string)
28 l: label (string)
29 C: constant pool header (byte)
30 c: constant pool entry (string)
31 s: skip (byte) (number of actions)
32 m: method (byte) GetUrl2:(0=none, 1=get, 2=post)/GotoFrame2:(1=play)
33 b: branch (word) (number of bytes)
34 p (push): type(byte), type=0:string, type=1:double
35 {: define function (name (string), num (word), params (num strings), codesize (word)
36 o: object (string)
37 r: register (byte)
38  */
39 {3,"End", 0x00, ""},
40 {3,"GotoFrame", 0x81, "f"},
41 {4,"GotoFrame2", 0x9f, "m"}, // -1 (/Movieclip:3)
42 {3,"GetUrl", 0x83, "ul"},
43 {4,"GetUrl2", 0x9a, "m"}, //-2
44 {3,"NextFrame", 0x04, ""},
45 {3,"PreviousFrame", 0x05, ""},
46 {3,"Play", 0x06, ""},
47 {3,"Stop", 0x07, ""},
48 {3,"ToggleQuality", 0x08, ""},
49 {3,"StopSounds", 0x09, ""},
50 {3,"WaitForFrame", 0x8a, "fs"},
51 {4,"WaitForFrame2", 0x8d, "s"}, // -1
52 {3,"SetTarget", 0x8b, "t"},
53 {4,"SetTarget2", 0x20, ""}, //-1
54 {3,"GotoLabel", 0x8c, "l"},
55 {4,"Add", 0x0a, ""}, //  -2, +1
56 {4,"Multiply", 0x0c, ""}, //  -2, +1
57 {4,"Divide", 0x0d, ""}, //  -2, +1
58 {4,"Subtract", 0x0b, ""}, //  -2, +1
59 {4,"Less", 0x0f, ""}, //  -2, +1
60 {4,"Equals", 0x0e, ""}, //  -2, +1
61 {4,"And", 0x10, ""}, //  -2, +1
62 {4,"Or", 0x11, ""}, //  -2, +1
63 {4,"Not", 0x12, ""}, //  -1, +1
64 {4,"StringAdd", 0x21, ""}, // -2,+1
65 {4,"StringLength", 0x14, ""}, // -1, +1
66 {4,"MBStringLength", 0x31}, // -1, +1
67 {4,"StringEquals", 0x13, ""}, // -2, +1
68 {4,"StringLess", 0x29, ""}, //-2, +1
69 {4,"StringExtract", 0x15, ""}, // -3, +1
70 {4,"MBStringExtract", 0x35, ""}, //-3 +1
71 {4,"Push", 0x96, "p"}, //  +1
72 {4,"Pop", 0x17, ""}, //  -1
73 {4,"ToInteger", 0x18, ""}, // -1, +1
74 {4,"CharToAscii", 0x32, ""}, // -1, +1
75 {4,"AsciiToChar", 0x33, ""}, // -1, +1
76 {4,"MBCharToAscii", 0x36, ""}, // -1, +1
77 {4,"MBAsciiToChar", 0x37, ""}, // -1, +1
78 {4,"Jump", 0x99, "b"},
79 {4,"If", 0x9d, "b"}, // -1
80 {4,"Call", 0x9e, ""}, //-1 (frame label/number)
81 {4,"GetVariable", 0x1c,""}, // -1, +1
82 {4,"SetVariable", 0x1d,""}, // -2
83 {4,"GetProperty", 0x22,""}, //-2, +1
84 {4,"SetProperty", 0x23, ""}, // -3
85 {4,"RemoveSprite", 0x25, ""}, //-1
86 {4,"StartDrag", 0x27, ""}, // -2, -1, (-4)
87 {4,"EndDrag", 0x28, ""}, 
88 {4,"CloneSprite", 0x24, ""}, // -3
89 {4,"Trace", 0x26, ""}, //-1
90 {4,"GetTime", 0x34, ""}, //+1
91 {4,"RandomNumber", 0x30, ""}, //-1,+1
92 {5,"Modulo", 0x3f,""},
93 {5,"BitAnd", 0x60,""},
94 {5,"BitLShift", 0x63,""},
95 {5,"BitOr", 0x61,""},
96 {5,"BitRShift", 0x64,""},
97 {5,"BitURShift", 0x65,""},
98 {5,"BitXor", 0x62,""},//66?
99 {5,"Decrement", 0x51,""},
100 {5,"Increment", 0x50,""},
101 {5,"PushDuplicate", 0x4c,""},
102 {5,"StackSwap", 0x4d,""}, //?
103 {5,"StoreRegister", 0x87,"r"},
104 {5,"CallFunction", 0x3d,""},
105 {5,"DefineFunction", 0x9b, "{"},
106 {5,"Return", 0x3e,""},
107 {5,"GetMember", 0x4e,""},
108 {5,"SetMember", 0x4f,""},
109 {5,"CallMethod", 0x52,""},
110 {5,"Constantpool", 0x88, "Cc"},
111 {5,"DefineLocal", 0x3c,""},
112 {5,"DefineLocal2", 0x41,""},
113 {5,"Makehash", 0x43, ""}, //??
114 {5,"Delete", 0x3a,""}, //?
115 {5,"Delete2", 0x3b,""},
116 {5,"Enumerate", 0x46,""},
117 {5,"Equals2", 0x49,""},
118 {5,"InitArray", 0x42,""}, // InitObject?
119 {5,"NewMethod", 0x53,""}, //?
120 {5,"NewObject", 0x40,""},
121 {5,"TargetPath", 0x45,""}, //?
122 {5,"With", 0x94, "o"},
123 {5,"ToNumber", 0x4a,""}, //?
124 {5,"ToString", 0x4b,""}, //?
125 {5,"TypeOf", 0x44,""},
126 {5,"Add2", 0x47,""},
127 {5,"Less2", 0x48,""}
128 };
129 static int definedactions = sizeof(actions)/sizeof(struct Action);
130
131 ActionTAG* GetActions(TAG*tag) 
132 {
133     U8 op = 1;
134     int length;
135     ActionTAG tmp;
136     ActionTAG*action = &tmp;
137     U8*data;
138     while(op)
139     {
140         int pos;
141         action->next = (ActionTAG*)malloc(sizeof(ActionTAG));
142         action->next->prev = action;
143         action->next->next = 0;
144         action = action->next;
145
146         op = GetU8(tag);
147         if(op<0x80)
148             length = 0;
149         else
150             length = GetU16(tag);
151
152         if(length) {
153             int t;
154             data = malloc(length);
155             for(t=0;t<length;t++)
156                 data[t] = GetU8(tag);
157         } else {
158           data = 0;
159         }
160         action->op = op;
161         action->len = length;
162         action->data = data;
163         action->parent = tag;
164     }
165     return tmp.next;
166 }
167
168 void SetActions(TAG*tag, ActionTAG*action)
169 {
170     while(action)
171     {
172         SetU8(tag, action->op);
173         if(action->op & 128)
174           SetU16(tag, action->len);
175
176         SetBlock(tag, action->data, action->len);
177
178         action = action->next;
179     }
180 }
181
182 int OpAdvance(char c, char*data)
183 {
184     switch (c)
185     {
186         case 'f':
187             return 2;
188         case 'u':
189             return strlen(data)+1;
190         case 't':
191             return strlen(data)+1;
192         case 'l': 
193             return strlen(data)+1;
194         case 'c': 
195             return strlen(data)+1;
196         case 'C': 
197             return 2;
198         case 's':
199             return 1;
200         case 'm':
201             return 1;
202         case 'b':
203             return 2;
204         case 'p': {
205             U8 type = *data++;
206             if(type == 0) {
207                 return 1+strlen(data)+1; //string
208             } else if (type == 1) {
209                 return 1+4; //float
210             } else if (type == 2) {
211                 return 1+0; //NULL
212             } else if (type == 4) {
213                 return 1+1; //register
214             } else if (type == 5) {
215                 return 1+1; //bool
216             } else if (type == 6) {
217                 return 1+8; //double
218             } else if (type == 7) {
219                 return 1+4; //int
220             } else if (type == 8) {
221                 return 1+1; //lookup
222             }
223             break;
224         }
225     }
226     return 0;
227 }
228
229 /* TODO: this should be in swfdump.c */
230 void DumpActions(ActionTAG*atag, char*prefix) 
231 {
232     U8 op;
233     int t;
234     U8*data;
235     char* cp;
236     if(!prefix) 
237         prefix="";
238     while(atag)
239     {
240         U8 poollen = 0;
241         for(t=0;t<definedactions;t++)
242             if(actions[t].op == atag->op)
243                 break;
244
245         if(t==definedactions) {
246             printf("%s (%5d bytes) action: %02x\n", prefix, atag->len, op);
247             atag = atag->next;
248             continue;
249         }
250         printf("%s (%5d bytes) action: %s", prefix, atag->len, actions[t].name);
251         cp = actions[t].flags;
252         data = atag->data;
253         if(atag->len) //TODO: check for consistency: should we have a length?
254         while(*cp)
255         {
256             switch(*cp)
257             {
258                 case 'f': {
259                     printf(" %d", *(U16*)data); //FIXME: le/be
260                 } break;
261                 case 'u': {
262                     printf(" URL:\"%s\"", data);
263                 } break;
264                 case 't': {
265                     printf(" Target:\"%s\"", data);
266                 } break;
267                 case 'l': {
268                     printf(" Label:\"%s\"", data);
269                 } break;
270                 case 'c': {
271                     printf(" String:\"%s\"", data);
272                 } break;
273                 case 'C': {
274                     poollen = *data;
275                     printf("(%d entries)", poollen);
276                 } break;
277                 case 's': {
278                     printf(" +%d", data);
279                 } break;
280                 case 'm': {
281                     //m: method (byte) url:(0=none, 1=get, 2=datat)/gf2:(1=play)
282                     printf(" %d", data);
283                 } break;
284                 case 'b': {
285                     printf(" %d", *(U16*)data);
286                 } break;
287                 case 'p': {
288                     U8 type = *data;
289                     char*value = data+1;
290                     if(type == 0) {
291                         printf(" String:\"%s\"", value);
292                     } else if (type == 1) {
293                         printf(" Float:\"%f\"", *(float*)value);
294                     } else if (type == 2) {
295                         printf(" NULL");
296                     } else if (type == 4) {
297                         printf(" register:%d", value);
298                     } else if (type == 5) {
299                         printf(" %s", *value?"true":"false");
300                     } else if (type == 6) {
301                         printf(" %f", *(double*)value);
302                     } else if (type == 7) {
303                         printf(" %d", *(int*)value);
304                     } else if (type == 8) {
305                         printf(" Lookup:%d", *value);
306                     }
307                 } break;
308             }
309             data += OpAdvance(*cp, data);
310             if(*cp!='c' || !poollen)
311                 cp++;
312             if(poollen)
313                 poollen--;
314         }
315
316         if(data < atag->data + atag->len)
317         {
318             int nl = ((atag->data+atag->len)-data);
319             int t;
320             printf(" remainder of %d bytes:\"", nl);
321             for(t=0;t<nl;t++) {
322                 if(data[t]<32)
323                     printf("\\%d",data[t]);
324                 else
325                     printf("%c", data[t]);
326             }
327             printf("\"");
328         }
329         printf("\n");
330         atag = atag->next;
331     }
332 }
333
334 static const char TYPE_URL = 1;
335 static const char TYPE_TARGET = 2;
336 static const char TYPE_STRING = 4;
337
338 int ActionEnumerate(ActionTAG*atag, char*(*callback)(char*), int type)
339 {
340     U8 op;
341     int t;
342     U8*data;
343     char* cp;
344     
345     while(atag)
346     {
347         U8 poollen = 0;
348         for(t=0;t<definedactions;t++)
349             if(actions[t].op == atag->op)
350                 break;
351
352         if(t==definedactions) {
353             // unknown actiontag
354             atag = atag->next;
355             continue;
356         }
357         cp = actions[t].flags;
358         data = atag->data;
359         if(atag->len) {
360             while(*cp) {
361                 U8 * replacepos = 0;
362                 int replacelen = 0;
363                 U8 * replacement = 0;
364                 switch(*cp)
365                 {
366                     case 'u': {
367                         if(type&TYPE_URL)
368                         {
369                             replacelen = strlen(data);
370                             replacepos = data;
371                             replacement = callback(data); // may be null
372                         }
373                     } break;
374                     case 't': {
375                         if(type&TYPE_TARGET)
376                         {
377                             replacelen = strlen(data);
378                             replacepos = data;
379                             replacement = callback(data); // may be null
380                         }
381                     } break;
382                     case 'c': {
383                         if(type&TYPE_STRING)
384                         {
385                             replacelen = strlen(data);
386                             replacepos = data;
387                             replacement = callback(data); // may be null
388                         }
389                     } break;
390                     case 'C': {
391                         poollen = (*data);
392                     } break;
393                     case 'o': {
394                     } break;
395                     case 'p': {
396                         U8 datatype = *data;
397                         char*value = &data[1];
398                         if(datatype == 0) { //string
399                             if(type&TYPE_STRING)
400                             {
401                                 replacelen = strlen(value);
402                                 replacepos = value;
403                                 replacement = callback(value); // may be null
404                             }
405                         } else if (datatype == 8) { //lookup
406                         }
407                     } break;
408                 }
409                 data += OpAdvance(*cp, data);
410                 if(*cp!='c' || !poollen)
411                     cp++;
412                 if(poollen)
413                     poollen--;
414
415                 if(replacement)
416                 {
417                     int newlen = strlen(replacement);
418                     char * newdata = malloc(atag->len - replacelen + newlen);
419                     int rpos = replacepos - atag->data;
420                     memcpy(newdata, atag->data, rpos);
421                     memcpy(&newdata[rpos], replacement, newlen);
422                     memcpy(&newdata[rpos+newlen], &replacepos[replacelen],
423                             &data[atag->len] - &replacepos[replacelen]);
424                     free(atag->data);
425                     atag->data = newdata;
426                     data = &atag->data[rpos+newlen+1];
427                 }
428             }
429         }
430         atag = atag->next;
431     }
432 }
433
434 void ActionEnumerateTargets(ActionTAG*atag, char*(*callback)(char*))
435 {
436     ActionEnumerate(atag, callback, TYPE_TARGET);
437 }
438 void ActionEnumerateStrings(ActionTAG*atag, char*(*callback)(char*))
439 {
440     ActionEnumerate(atag, callback, TYPE_STRING);
441 }
442 void ActionEnumerateURLs(ActionTAG*atag, char*(*callback)(char*))
443 {
444     ActionEnumerate(atag, callback, TYPE_URL);
445 }
446