rfxswf cleanups: added prefixes and altered structure name conventions
[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* swf_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 = swf_GetU8(tag);
146         if(op<0x80)
147             length = 0;
148         else
149             length = swf_GetU16(tag);
150
151         if(length) {
152             int t;
153             data = malloc(length);
154             for(t=0;t<length;t++)
155                 data[t] = swf_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 swf_SetActions(TAG*tag, ActionTAG*action)
168 {
169     while(action)
170     {
171         swf_SetU8(tag, action->op);
172         if(action->op & 128)
173           swf_SetU16(tag, action->len);
174
175         swf_SetBlock(tag, action->data, action->len);
176
177         action = action->next;
178     }
179 }
180
181 int swf_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 swf_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 += swf_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 swf_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                         Nope: user can force it by reg.ex. if he wants to /r
363                      */
364                     case 'c': {
365                     } break;
366                     case 'o': {
367                     } break;
368                     case 'p': {
369                         U8 type = *data;
370                         char*value = &data[1];
371                         if(type == 0) { //string
372                         } else if (type == 8) { //lookup
373                         }
374                     } break;
375                 }
376                 data += swf_OpAdvance(*cp, data);
377                 cp++;
378
379                 //TODO: apply replacement here.
380             }
381         }
382
383         atag = atag->next;
384     }
385 }
386