fixed movieclip writing. (sometimes wrote too much)
[swftools.git] / src / swfextract.c
1 /* swfextract.c
2    Allows to extract parts of the swf into a new file.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
7
8    This file is distributed under the GPL, see file COPYING for details */
9
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include "../lib/rfxswf.h"
14 #include "../lib/args.h"
15 #include "reloc.h"
16
17 char * filename = 0;
18 char * destfilename = "output.swf";
19 int verbose = 2;
20 int extractid = -1;
21 char* extractname = 0;
22
23 struct options_t options[] =
24 {
25  {"o","output"},
26  {"v","verbose"},
27  {"i","id"},
28  {"n","name"},
29  {"V","version"},
30  {0,0}
31 };
32
33 int args_callback_option(char*name,char*val)
34 {
35     if(!strcmp(name, "V")) {
36         printf("swfextract - part of %s %s\n", PACKAGE, VERSION);
37         exit(0);
38     } 
39     if(!strcmp(name, "o")) {
40         destfilename = val;
41         return 1;
42     } 
43     if(!strcmp(name, "i")) {
44         extractid = atoi(val);
45         if(extractname) {
46             fprintf(stderr, "You can only supply either name or id\n");
47             exit(1);
48         }
49         return 1;
50     } 
51     if(!strcmp(name, "n")) {
52         extractname = val;
53         if(extractid>=0) {
54             fprintf(stderr, "You can only supply either name or id\n");
55             exit(1);
56         }
57         return 1;
58     } 
59     if(!strcmp(name, "v")) {
60         verbose ++;
61         return 0;
62     } 
63     else {
64         printf("Unknown option: -%s\n", name);
65         return 0;
66     }
67
68     return 0;
69 }
70 int args_callback_longoption(char*name,char*val)
71 {
72     return args_long2shortoption(options, name, val);
73 }
74 void args_callback_usage(char*name)
75 {    
76     printf("Usage: %s [-v] [-i id] file.swf\n", name);
77     printf("\t-v , --verbose\t\t\t Be more verbose\n");
78     printf("\t-i , --id ID\t\t\t ID of the object to extract\n");
79     printf("\t-n , --name name\t\t\t instance name of the object to extract\n");
80     printf("\t-V , --version\t\t\t Print program version and exit\n");
81 }
82 int args_callback_command(char*name,char*val)
83 {
84     if(filename) {
85         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
86                  filename, name);
87     }
88     filename = name;
89     return 0;
90 }
91
92 U8 mainr,maing,mainb;
93 char used[65536];
94 TAG*tags[65536];
95 int changed;
96
97 void idcallback(void*data)
98 {
99     if(!used[*(U16*)data]) {
100         changed = 1;
101         used[*(U16*)data] = 1;
102     }
103 }
104
105 void enumerateIDs(TAG*tag, void(*callback)(void*))
106 {
107     U8*data;
108     int len = tag->len;
109     if(tag->len>=64) {
110         len += 6;
111         data = (U8*)malloc(len);
112         *(U16*)data = (tag->id<<6)+63;
113         *(U32*)&data[2] = tag->len;
114         memcpy(&data[6], tag->data, tag->len);
115     } else {
116         len += 2;
117         data = (U8*)malloc(len);
118         *(U16*)data = (tag->id<<6)+tag->len;
119         memcpy(&data[2], tag->data, tag->len);
120     }
121     map_ids_mem(data, len, callback);
122 }
123
124 void extractTag(SWF*swf, int defineid, char*filename)
125 {
126     SWF newswf;
127     TAG*desttag;
128     TAG*srctag;
129     RGBA rgb;
130     char sprite;
131     int f;
132     int t;
133     int copy = 0;
134     memset(&newswf,0x00,sizeof(SWF));        // set global movie parameters
135     memset(used, 0,65536);
136
137     newswf.fileVersion    = swf->fileVersion;
138     newswf.frameRate      = swf->frameRate;
139     newswf.movieSize      = swf->movieSize;
140                 
141     newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
142     desttag = newswf.firstTag;
143     rgb.r = mainr;
144     rgb.g = maing;
145     rgb.b = mainb;
146     swf_SetRGB(desttag,&rgb);
147
148     used[defineid] = 1;
149     do {
150        changed = 0;
151        for(t=0;t<65536;t++) {
152            if(used[t]==1) {
153              if(tags[t]->id==ST_DEFINESPRITE) {
154                  TAG*tag = tags[t];
155                  while(tag->id != ST_END)
156                  {
157                      enumerateIDs(tag, idcallback);
158                      tag = tag->next;
159                  }
160              }
161              else
162                 enumerateIDs(tags[t], idcallback);
163              used[t] = 2;
164            }
165        }
166     }
167     while(changed);
168
169     srctag = swf->firstTag;
170     while(srctag && (srctag->id || sprite)) {
171         int reset = 0;
172         if(!sprite) {
173             copy = 0;
174         }
175         if(srctag->id == ST_END) {
176             sprite = 0;
177         }
178         if(srctag->id == ST_DEFINESPRITE)
179             sprite = 1;
180         if(swf_isDefiningTag(srctag)) {
181             int id = swf_GetDefineID(srctag);
182             if(used[id]) 
183                 copy = 1;
184         } else 
185         if (((srctag->id == ST_PLACEOBJECT ||
186               srctag->id == ST_PLACEOBJECT2) && swf_GetPlaceID(srctag) == defineid) ||
187               (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)])) 
188         {
189                 if(copy == 0)
190                     reset = 1;
191                 copy = 1;
192         } 
193
194         if(copy) {
195             TAG*ttag = (TAG*)malloc(sizeof(TAG));
196             desttag = swf_InsertTag(desttag, srctag->id);
197             desttag->len = desttag->memsize = srctag->len;
198             desttag->data = malloc(srctag->len);
199             memcpy(desttag->data, srctag->data, srctag->len);
200             if(reset)
201                 copy = 0;
202         }
203         
204         srctag = srctag->next;
205     }
206     desttag = swf_InsertTag(desttag,ST_SHOWFRAME);
207     desttag = swf_InsertTag(desttag,ST_END);
208     
209     f = open(filename, O_TRUNC|O_WRONLY|O_CREAT, 0644);
210     if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n");
211     close(f);
212
213     swf_FreeTags(&newswf);                       // cleanup
214 }
215
216 int main (int argc,char ** argv)
217
218     TAG*tag;
219     SWF swf;
220     int f;
221     processargs(argc, argv);
222
223     if(!filename)
224     {
225         fprintf(stderr, "You must supply a filename.\n");
226         return 1;
227     }
228     initLog(0,-1,0,0,-1, verbose);
229
230     f = open(filename,O_RDONLY);
231
232     if (f<0)
233     { 
234         perror("Couldn't open file: ");
235         exit(1);
236     }
237     if FAILED(swf_ReadSWF(f,&swf))
238     { 
239         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
240         close(f);
241         exit(1);
242     }
243
244     tag = swf.firstTag;
245
246     while(tag) {
247         if(swf_isDefiningTag(tag)) {
248             int id = swf_GetDefineID(tag);
249             tags[id] = tag;
250         }
251         else if (tag->id == ST_SETBACKGROUNDCOLOR) {
252             mainr = tag->data[0];
253             maing = tag->data[1];
254             mainb = tag->data[2];
255         }
256         else if(tag->id == ST_PLACEOBJECT2) {
257             char*name = swf_GetName(tag);
258             if(name && extractname && !strcmp(name, extractname)) {
259                 int id = swf_GetPlaceID(tag); 
260                 if(extractid>=0 && id != extractid) {
261                     fprintf(stderr, "Error: More than one instance with name \"%s\"", name);
262                     exit(1);
263                 }
264                 extractid = swf_GetPlaceID(tag); 
265             }
266         }
267         tag = tag->next;
268     }
269     if(tags[extractid])
270         extractTag(&swf, extractid, destfilename);
271
272     swf_FreeTags(&swf);
273     return 0;
274 }
275