added -L,-N options
[swftools.git] / src / swfbytes.c
1 /* swfbytes.c
2    A tool for modifying swfs on the tag level
3
4    Part of the swftools package.
5    
6    Copyright (c) 2008/2009 Matthias Kramm <kramm@quiss.org>
7  
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include "../config.h"
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <stdarg.h>
27 #include "../lib/rfxswf.h"
28 #include "../lib/args.h"
29
30 static char * filename = 0;
31 static int verbose;
32
33 struct options_t options[] =
34 {
35  {"v","verbose"},
36  {"V","version"},
37  {0,0}
38 };
39
40 int args_callback_option(char*name,char*val)
41 {
42     if(!strcmp(name, "V")) {
43         printf("swfedit - part of %s %s\n", PACKAGE, VERSION);
44         return 0;
45     } else if(!strcmp(name, "v")) {
46         verbose++;
47         return 0;
48     } else {
49         printf("Unknown option: -%s\n", name);
50         return 0;
51     }
52     return 0;
53 }
54 int args_callback_longoption(char*name,char*val)
55 {
56     return args_long2shortoption(options, name, val);
57 }
58 void args_callback_usage(char*name)
59 {    
60     printf("Usage: %s [-at] file.swf\n", name);
61     printf("\t-h , --help\t\t Print help and exit\n");
62     printf("\t-v , --verbose\t\t Be more verbose\n");
63     printf("\t-V , --version\t\t Print program version and exit\n");
64 }
65 int args_callback_command(char*name,char*val)
66 {
67     if(filename) {
68         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n", filename, name);
69     }
70     filename = name;
71     return 0;
72 }
73
74 void dumpTag(FILE*fo, char*prefix, TAG*tag)
75 {
76     int t;
77     for(t=0;t<tag->len;t++) {
78         if(!(t&15))
79             fprintf(fo, "%s| ", prefix);
80         fprintf(fo, "%02x ", tag->data[t]);
81         if((t && ((t&15)==15)) || (t==tag->len-1)) {
82             fprintf(fo, "\n");
83         }
84     }
85 }
86
87 void dumpFile(SWF*swf, FILE*fo)
88 {
89     TAG* tag = swf->firstTag;
90     int indent = 0;
91     int t;
92     char whitespace[33];
93     char*prefix = "";
94
95     memset(whitespace, 32, 32); whitespace[32] = 0;
96     
97     fprintf(fo, "Version: %d\n", swf->fileVersion);
98     fprintf(fo, "FrameRate: %f\n",swf->frameRate/256.0);
99     fprintf(fo, "FrameCount: %d\n",swf->frameCount);
100     fprintf(fo, "Width: %.2f\n",(swf->movieSize.xmax-swf->movieSize.xmin)/20.0);
101     fprintf(fo, "X-Offset: %.2f\n", swf->movieSize.xmin/20.0);
102     fprintf(fo, "Height: %.2f\n",(swf->movieSize.ymax-swf->movieSize.ymin)/20.0);
103     fprintf(fo, "Y-Offset: %.2f\n", swf->movieSize.ymin/20.0);
104     fprintf(fo, "\n");
105
106     while(tag)
107     {
108         if(swf_isDefiningTag(tag)) {
109             fprintf(fo, "%s%s <%d>\n", prefix, swf_TagGetName(tag), swf_GetDefineID(tag));
110             swf_SetTagPos(tag, 2);
111             dumpTag(fo, prefix, tag);
112         } else if(swf_isPseudoDefiningTag(tag)) {
113             fprintf(fo, "%s%s <%d>\n", prefix, swf_TagGetName(tag), swf_GetDefineID(tag));
114             swf_SetTagPos(tag, 2);
115             dumpTag(fo, prefix, tag);
116         } else if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) { 
117             SWFPLACEOBJECT po;
118             swf_GetPlaceObject(tag, &po);
119             fprintf(fo, "%s%s <%d>\n",  prefix,swf_TagGetName(tag), po.id);
120             /* for now */
121             swf_SetTagPos(tag, 0);
122             dumpTag(fo, prefix, tag);
123             swf_PlaceObjectFree(&po);
124         } else {
125             fprintf(fo, "%s%s\n", prefix, swf_TagGetName(tag));
126             dumpTag(fo, prefix, tag);
127         }
128
129         if(tag->id == ST_DEFINESPRITE) {
130             indent+=4;
131             if(indent>32)
132                 indent = 32;
133             prefix = &whitespace[32-indent];
134         } else if(tag->id == ST_END) {
135             indent-=4;
136             if(indent<0)
137                 indent = 0;
138             prefix = &whitespace[32-indent];
139         }
140
141         tag = tag->next;
142     }
143 }
144
145 static void readline(FILE*fi, char*line, int maxlen) {
146     int pos = 0;
147     while(!feof(fi)) {
148         if(!fread(&line[pos],1,1,fi))
149             break;
150
151         /* cut of preceding whitespace */
152         if(pos == 0 && (line[0] == 32 || line[0] == '\t'))
153             continue;
154
155         if(line[pos] == 13 || line[pos]==10)
156             break;
157         if(pos<maxlen-1)
158             pos++;
159     }
160     line[pos]=0;
161
162     /* cut off comments */
163     char*x=strchr(line,'#');
164     if(x) {
165         *x=0;
166         pos = x-line;
167     }
168
169     /* cut off trailing whitespace */
170     while(pos>=1 && (line[pos-1]==32 || line[pos-1]==9)) {
171         pos--;
172         line[pos]=0;
173     }
174 }
175
176 int getFloat(char*s)
177 {
178     float x;
179     int n;
180     while(*s==32 || *s=='\t') s++;
181     sscanf(s, "%f%n", &x, &n);
182     if(n==0) 
183         fprintf(stderr, "Not a float: %s\n", s);
184     return x;
185 }
186 int getInt(char*s)
187 {
188     int i;
189     int n;
190     while(*s==32 || *s=='\t') s++;
191     sscanf(s, "%d%n", &i, &n);
192     if(n==0) 
193         fprintf(stderr, "Not an integer: %s\n", s);
194     return i;
195 }
196 int getTwip(char*x)
197 {
198     return (int)(getFloat(x)*20);
199 }
200
201
202 static char**lookup;
203 int swf_TagNameToID(char*name)
204 {
205     int t;
206     TAG tag;
207     memset(&tag, 0, sizeof(tag));
208     if(!lookup) {
209         lookup = (char**)malloc(sizeof(char*)*65536);
210         for(t=0;t<65536;t++) {
211             tag.id = t;
212             lookup[t] = swf_TagGetName(&tag);
213         }
214     }
215     for(t=0;t<65536;t++) {
216         if(lookup[t] && !strcasecmp(name, lookup[t]))
217             return t;
218     }
219     fprintf(stderr, "Not a tag name: \"%s\"\n", name);
220     return -1;
221 }
222
223 void parseFile(FILE*fi, SWF*swf)
224 {
225     TAG*tag = 0;
226     char line[1024];
227     memset(swf, 0, sizeof(SWF));
228     while(1) {
229         char*colon = 0;
230         readline(fi, line, 1024);
231         if(!line[0])
232             break;
233         colon = strchr(line, ':');
234         if(colon) {
235             int num = colon - line;
236             int n;
237             if(num == 9 && !strncmp(line, "FrameRate", num)) {
238                 swf->frameRate = getFloat(colon+1)*256;
239             } else if(num == 10 && !strncmp(line, "FrameCount", num)) {
240                 swf->frameCount = getInt(colon+1);
241             } else if(num == 7 && !strncmp(line, "Version", num)) {
242                 swf->fileVersion = getInt(colon+1);
243             } else if(num == 5 && !strncmp(line, "Width", num)) {
244                 int width = getTwip(colon+1);
245                 swf->movieSize.xmax += width;
246             } else if(num == 6 && !strncmp(line, "Height", num)) {
247                 int height = getTwip(colon+1);
248                 swf->movieSize.ymax += height;
249             } else if(num == 8 && !strncmp(line, "X-Offset", num)) {
250                 int xoffset = getTwip(colon+1);
251                 swf->movieSize.xmin += xoffset;
252                 swf->movieSize.xmax += xoffset;
253             } else if(num == 8 && !strncmp(line, "Y-Offset", num)) {
254                 int yoffset = getTwip(colon+1);
255                 swf->movieSize.ymin += yoffset;
256                 swf->movieSize.ymax += yoffset;
257             } else {
258                 fprintf(stderr, "Ignored line \"%s\"\n", line);
259             }
260         }
261     }
262     while(!feof(fi)) {
263         char*s, *tagname;
264         char*br;
265         readline(fi, line, 1024);
266         if(!line[0]) 
267             continue;
268         s = strchr(line, ' ');
269         br = strchr(line, '|');
270         if(!br) {
271             int id = 0;
272             /* DEFINESHAPE <id> ... type line */
273             if(!s) {
274                 tagname = strdup(line);
275             } else {
276                 tagname = strdup(line);
277                 tagname[s-line] = 0;
278             }
279             id = swf_TagNameToID(tagname);
280             free(tagname);
281             if(id<0) {
282                 fprintf(stderr, "Ignored line \"%s\"\n", line);
283                 continue;
284             }
285             tag = swf_InsertTag(tag, id);
286             if(!swf->firstTag)
287                 swf->firstTag = tag;
288         } else {
289             /* | 00 34 fe c7 ... type line */
290             char*p = line;
291             int num = 0;
292             int digits = 0;
293
294             if(!tag) {
295                 fprintf(stderr, "Discarded unassignable data %s\n", line);
296                 continue;
297             }
298
299             while(*p) {
300                 int n = 0;
301                 if((*p>='a' && *p<='f') ||
302                    (*p>='A' && *p<='F')) {
303                     n = 9 + (*p & 15);
304                     num = (num<<4) | n;
305                     digits++;
306                 }
307                 else if(*p>='0' && *p<='9') {
308                     n = *p & 15;
309                     num = (num<<4) | n;
310                     digits++;
311                 } else if(digits) {
312                     swf_SetU8(tag, num);
313                     num = 0;
314                     digits = 0;
315                 }
316                 p++;
317             }
318             if(digits)
319                 swf_SetU8(tag, num);
320         }
321     }
322 }
323     
324 char swf_IsSWF(char*filename)
325 {
326     int fi = open(filename,O_RDONLY|O_BINARY);
327     U8 buf[3] = {0,0,0};
328     if(fi<0)
329         return 0;
330     read(fi, buf, 3);
331     close(fi);
332     if((buf[0] == 'F' || buf[0] == 'C') && buf[1] == 'W' && buf[2] == 'S')
333         return 1;
334     return 0;
335 }
336
337 int main (int argc,char ** argv)
338
339     int fi;
340     TAG*tag = 0;
341     SWF swf;
342
343     processargs(argc, argv);
344     
345     if(!filename) {
346         fprintf(stderr, "You must supply a filename.\n");
347         return -1;
348     }
349     if(swf_IsSWF(filename)) {
350         fi = open(filename,O_RDONLY|O_BINARY);
351         if (fi<0) { 
352             perror("Couldn't open file: ");
353             exit(1);
354         }
355         if FAILED(swf_ReadSWF(fi,&swf)) { 
356             fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
357             close(fi);
358             exit(1);
359         }
360         dumpFile(&swf, stdout);
361         swf_FreeTags(&swf);
362     } else {
363         SWF newswf;
364         FILE*fi = fopen(filename, "rb");
365         parseFile(fi, &newswf);
366         fclose(fi);
367         int f;
368         char*sname = "output.swf";
369         f = open(sname,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
370         if FAILED(swf_WriteSWF(f,&newswf)) { 
371             fprintf(stderr, "Unable to write output file: %s\n", sname);
372         }
373         close(f);
374     }
375
376     return 0;
377 }