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