new parameter addspacechars
[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 static struct options_t options[] = {
34 {"h", "help"},
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("\n");
61     printf("Usage: %s [-v] file.swf > file.hexdump\n", name);
62     printf("OR:    %s file.hexdump\n", name);
63     printf("\n");
64     printf("-h , --help                    Print help and exit\n");
65     printf("-v , --verbose                 Be more verbose\n");
66     printf("-V , --version                 Print program version and exit\n");
67     printf("\n");
68 }
69 int args_callback_command(char*name,char*val)
70 {
71     if(filename) {
72         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n", filename, name);
73     }
74     filename = name;
75     return 0;
76 }
77
78 void dumpTag(FILE*fo, char*prefix, TAG*tag)
79 {
80     int t;
81     for(t=0;t<tag->len;t++) {
82         if(!(t&15))
83             fprintf(fo, "%s| ", prefix);
84         fprintf(fo, "%02x ", tag->data[t]);
85         if((t && ((t&15)==15)) || (t==tag->len-1)) {
86             fprintf(fo, "\n");
87         }
88     }
89 }
90
91 void dumpFile(SWF*swf, FILE*fo)
92 {
93     TAG* tag = swf->firstTag;
94     int indent = 0;
95     int t;
96     char whitespace[33];
97     char*prefix = "";
98
99     memset(whitespace, 32, 32); whitespace[32] = 0;
100     
101     fprintf(fo, "Version: %d\n", swf->fileVersion);
102     fprintf(fo, "FrameRate: %f\n",swf->frameRate/256.0);
103     fprintf(fo, "FrameCount: %d\n",swf->frameCount);
104     fprintf(fo, "Width: %.2f\n",(swf->movieSize.xmax-swf->movieSize.xmin)/20.0);
105     fprintf(fo, "X-Offset: %.2f\n", swf->movieSize.xmin/20.0);
106     fprintf(fo, "Height: %.2f\n",(swf->movieSize.ymax-swf->movieSize.ymin)/20.0);
107     fprintf(fo, "Y-Offset: %.2f\n", swf->movieSize.ymin/20.0);
108     fprintf(fo, "\n");
109
110     while(tag)
111     {
112         if(swf_isDefiningTag(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(swf_isPseudoDefiningTag(tag)) {
117             fprintf(fo, "%s%s <%d>\n", prefix, swf_TagGetName(tag), swf_GetDefineID(tag));
118             swf_SetTagPos(tag, 2);
119             dumpTag(fo, prefix, tag);
120         } else if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) { 
121             SWFPLACEOBJECT po;
122             swf_GetPlaceObject(tag, &po);
123             fprintf(fo, "%s%s <%d>\n",  prefix,swf_TagGetName(tag), po.id);
124             /* for now */
125             swf_SetTagPos(tag, 0);
126             dumpTag(fo, prefix, tag);
127             swf_PlaceObjectFree(&po);
128         } else {
129             fprintf(fo, "%s%s\n", prefix, swf_TagGetName(tag));
130             dumpTag(fo, prefix, tag);
131         }
132
133         if(tag->id == ST_DEFINESPRITE) {
134             indent+=4;
135             if(indent>32)
136                 indent = 32;
137             prefix = &whitespace[32-indent];
138         } else if(tag->id == ST_END) {
139             indent-=4;
140             if(indent<0)
141                 indent = 0;
142             prefix = &whitespace[32-indent];
143         }
144
145         tag = tag->next;
146     }
147 }
148
149 static void readline(FILE*fi, char*line, int maxlen) {
150     int pos = 0;
151     while(!feof(fi)) {
152         if(!fread(&line[pos],1,1,fi))
153             break;
154
155         /* cut of preceding whitespace */
156         if(pos == 0 && (line[0] == 32 || line[0] == '\t'))
157             continue;
158
159         if(line[pos] == 13 || line[pos]==10)
160             break;
161         if(pos<maxlen-1)
162             pos++;
163     }
164     line[pos]=0;
165
166     /* cut off comments */
167     char*x=strchr(line,'#');
168     if(x) {
169         *x=0;
170         pos = x-line;
171     }
172
173     /* cut off trailing whitespace */
174     while(pos>=1 && (line[pos-1]==32 || line[pos-1]==9)) {
175         pos--;
176         line[pos]=0;
177     }
178 }
179
180 int getFloat(char*s)
181 {
182     float x;
183     int n;
184     while(*s==32 || *s=='\t') s++;
185     sscanf(s, "%f%n", &x, &n);
186     if(n==0) 
187         fprintf(stderr, "Not a float: %s\n", s);
188     return x;
189 }
190 int getInt(char*s)
191 {
192     int i;
193     int n;
194     while(*s==32 || *s=='\t') s++;
195     sscanf(s, "%d%n", &i, &n);
196     if(n==0) 
197         fprintf(stderr, "Not an integer: %s\n", s);
198     return i;
199 }
200 int getTwip(char*x)
201 {
202     return (int)(getFloat(x)*20);
203 }
204
205
206 static char**lookup;
207 int swf_TagNameToID(char*name)
208 {
209     int t;
210     TAG tag;
211     memset(&tag, 0, sizeof(tag));
212     if(!lookup) {
213         lookup = (char**)malloc(sizeof(char*)*65536);
214         for(t=0;t<65536;t++) {
215             tag.id = t;
216             lookup[t] = swf_TagGetName(&tag);
217         }
218     }
219     for(t=0;t<65536;t++) {
220         if(lookup[t] && !strcasecmp(name, lookup[t]))
221             return t;
222     }
223     fprintf(stderr, "Not a tag name: \"%s\"\n", name);
224     return -1;
225 }
226
227 void parseFile(FILE*fi, SWF*swf)
228 {
229     TAG*tag = 0;
230     char line[1024];
231     memset(swf, 0, sizeof(SWF));
232     while(1) {
233         char*colon = 0;
234         readline(fi, line, 1024);
235         if(!line[0])
236             break;
237         colon = strchr(line, ':');
238         if(colon) {
239             int num = colon - line;
240             int n;
241             if(num == 9 && !strncmp(line, "FrameRate", num)) {
242                 swf->frameRate = getFloat(colon+1)*256;
243             } else if(num == 10 && !strncmp(line, "FrameCount", num)) {
244                 swf->frameCount = getInt(colon+1);
245             } else if(num == 7 && !strncmp(line, "Version", num)) {
246                 swf->fileVersion = getInt(colon+1);
247             } else if(num == 5 && !strncmp(line, "Width", num)) {
248                 int width = getTwip(colon+1);
249                 swf->movieSize.xmax += width;
250             } else if(num == 6 && !strncmp(line, "Height", num)) {
251                 int height = getTwip(colon+1);
252                 swf->movieSize.ymax += height;
253             } else if(num == 8 && !strncmp(line, "X-Offset", num)) {
254                 int xoffset = getTwip(colon+1);
255                 swf->movieSize.xmin += xoffset;
256                 swf->movieSize.xmax += xoffset;
257             } else if(num == 8 && !strncmp(line, "Y-Offset", num)) {
258                 int yoffset = getTwip(colon+1);
259                 swf->movieSize.ymin += yoffset;
260                 swf->movieSize.ymax += yoffset;
261             } else {
262                 fprintf(stderr, "Ignored line \"%s\"\n", line);
263             }
264         }
265     }
266     while(!feof(fi)) {
267         char*s, *tagname;
268         char*br;
269         readline(fi, line, 1024);
270         if(!line[0]) 
271             continue;
272         s = strchr(line, ' ');
273         br = strchr(line, '|');
274         if(!br) {
275             int id = 0;
276             /* DEFINESHAPE <id> ... type line */
277             if(!s) {
278                 tagname = strdup(line);
279             } else {
280                 tagname = strdup(line);
281                 tagname[s-line] = 0;
282             }
283             id = swf_TagNameToID(tagname);
284             free(tagname);
285             if(id<0) {
286                 fprintf(stderr, "Ignored line \"%s\"\n", line);
287                 continue;
288             }
289             tag = swf_InsertTag(tag, id);
290             if(!swf->firstTag)
291                 swf->firstTag = tag;
292         } else {
293             /* | 00 34 fe c7 ... type line */
294             char*p = line;
295             int num = 0;
296             int digits = 0;
297
298             if(!tag) {
299                 fprintf(stderr, "Discarded unassignable data %s\n", line);
300                 continue;
301             }
302
303             while(*p) {
304                 int n = 0;
305                 if((*p>='a' && *p<='f') ||
306                    (*p>='A' && *p<='F')) {
307                     n = 9 + (*p & 15);
308                     num = (num<<4) | n;
309                     digits++;
310                 }
311                 else if(*p>='0' && *p<='9') {
312                     n = *p & 15;
313                     num = (num<<4) | n;
314                     digits++;
315                 } else if(digits) {
316                     swf_SetU8(tag, num);
317                     num = 0;
318                     digits = 0;
319                 }
320                 p++;
321             }
322             if(digits)
323                 swf_SetU8(tag, num);
324         }
325     }
326 }
327     
328 char swf_IsSWF(char*filename)
329 {
330     int fi = open(filename,O_RDONLY|O_BINARY);
331     U8 buf[3] = {0,0,0};
332     if(fi<0)
333         return 0;
334     read(fi, buf, 3);
335     close(fi);
336     if((buf[0] == 'F' || buf[0] == 'C') && buf[1] == 'W' && buf[2] == 'S')
337         return 1;
338     return 0;
339 }
340
341 int main (int argc,char ** argv)
342
343     int fi;
344     TAG*tag = 0;
345     SWF swf;
346
347     processargs(argc, argv);
348     
349     if(!filename) {
350         fprintf(stderr, "You must supply a filename.\n");
351         return -1;
352     }
353     if(swf_IsSWF(filename)) {
354         fi = open(filename,O_RDONLY|O_BINARY);
355         if (fi<0) { 
356             perror("Couldn't open file: ");
357             exit(1);
358         }
359         if FAILED(swf_ReadSWF(fi,&swf)) { 
360             fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
361             close(fi);
362             exit(1);
363         }
364         dumpFile(&swf, stdout);
365         swf_FreeTags(&swf);
366     } else {
367         SWF newswf;
368         FILE*fi = fopen(filename, "rb");
369         parseFile(fi, &newswf);
370         fclose(fi);
371         int f;
372         char*sname = "output.swf";
373         f = open(sname,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
374         if FAILED(swf_WriteSWF(f,&newswf)) { 
375             fprintf(stderr, "Unable to write output file: %s\n", sname);
376         }
377         close(f);
378     }
379
380     return 0;
381 }