#include <fcntl.h>
#include "../lib/rfxswf.h"
#include "../lib/args.h"
-#include "reloc.h"
+#include "../lib/log.h"
+#ifdef HAVE_ZLIB_H
+#ifdef HAVE_LIBZ
+#include "zlib.h"
+#define _ZLIB_INCLUDED_
+#endif
+#endif
char * filename = 0;
char * destfilename = "output.swf";
-int verbose = 2;
+int verbose = 3;
char* extractids = 0;
char* extractframes = 0;
char* extractjpegids = 0;
+char* extractpngids = 0;
+char extractmp3 = 0;
char* extractname = 0;
char hollow = 0;
+int numextracts = 0;
+
struct options_t options[] =
{
{"o","output"},
{"v","verbose"},
{"i","id"},
{"j","jpegs"},
+ {"p","pngs"},
+ {"m","mp3"},
{"n","name"},
{"f","frame"},
{"V","version"},
{0,0}
};
+
int args_callback_option(char*name,char*val)
{
if(!strcmp(name, "V")) {
}
else if(!strcmp(name, "i")) {
extractids = val;
+ numextracts++;
if(extractname) {
fprintf(stderr, "You can only supply either name or id\n");
exit(1);
}
else if(!strcmp(name, "n")) {
extractname = val;
+ numextracts++;
if(extractids) {
fprintf(stderr, "You can only supply either name or id\n");
exit(1);
verbose ++;
return 0;
}
+ else if(!strcmp(name, "m")) {
+ extractmp3 = 1;
+ numextracts++;
+ return 0;
+ }
else if(!strcmp(name, "j")) {
if(extractjpegids) {
fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n");
exit(1);
}
+ numextracts++;
extractjpegids = val;
return 1;
}
+#ifdef _ZLIB_INCLUDED_
+ else if(!strcmp(name, "p")) {
+ if(extractpngids) {
+ fprintf(stderr, "Only one --pngs argument is allowed. (Try to use a range, e.g. -p 1,2,3)\n");
+ exit(1);
+ }
+ numextracts++;
+ extractpngids = val;
+ return 1;
+ }
+#endif
else if(!strcmp(name, "f")) {
+ numextracts++;
extractframes = val;
return 1;
}
printf("\t-o , --output filename\t\t set output filename\n");
printf("\t-n , --name name\t\t instance name of the object to extract\n");
printf("\t-i , --id IDs\t\t\t ID of the object to extract\n");
- printf("\t-j , --jpeg IDs\t\t\t IDs of the jpeg pictures to extract\n");
+ printf("\t-j , --jpeg IDs\t\t\t IDs of the JPEG pictures to extract\n");
+#ifdef _ZLIB_INCLUDED_
+ printf("\t-p , --pngs IDs\t\t\t IDs of the PNG pictures to extract\n");
+#endif
+ printf("\t-m , --mp3\t\t\t Extract main mp3 stream\n");
printf("\t-f , --frame frames\t\t frame numbers to extract\n");
- printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames (use with -f)\n");
+ printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames\n");
+ printf("\t \t\t\t (use with -f)\n");
printf("\t-V , --version\t\t\t Print program version and exit\n");
}
int args_callback_command(char*name,char*val)
void idcallback(void*data)
{
- if(!(used[*(U16*)data]&1)) {
+ if(!(used[GET16(data)]&1)) {
changed = 1;
- used[*(U16*)data] |= 1;
+ used[GET16(data)] |= 1;
}
}
void enumerateIDs(TAG*tag, void(*callback)(void*))
{
- U8*data;
+/* U8*data;
int len = tag->len;
if(tag->len>=64) {
len += 6;
data = (U8*)malloc(len);
- *(U16*)data = (tag->id<<6)+63;
- *(U32*)&data[2] = tag->len;
+ PUT16(data, (tag->id<<6)+63);
+ *(U8*)&data[2] = tag->len;
+ *(U8*)&data[3] = tag->len>>8;
+ *(U8*)&data[4] = tag->len>>16;
+ *(U8*)&data[5] = tag->len>>24;
memcpy(&data[6], tag->data, tag->len);
} else {
len += 2;
data = (U8*)malloc(len);
- *(U16*)data = (tag->id<<6)+tag->len;
+ PUT16(data, (tag->id<<6)+tag->len);
memcpy(&data[2], tag->data, tag->len);
}
map_ids_mem(data, len, callback);
+ */
+ int num = swf_GetNumUsedIDs(tag);
+ int *ptr = malloc(sizeof(int)*num);
+ int t;
+ swf_GetUsedIDs(tag, ptr);
+ for(t=0;t<num;t++)
+ callback(&tag->data[ptr[t]]);
}
void extractTag(SWF*swf, char*filename)
char first;
int t;
int frame = 0;
- char*names[] = {"Shapes","MovieClips","JPEGs","Sounds","Frames"};
+ char*names[] = {"Shapes","MovieClips","JPEGs","PNGs","Sounds","Frames"};
printf("Objects in file %s:\n",filename);
- for(t=0;t<5;t++) {
+ for(t=0;t<6;t++) {
tag = swf->firstTag;
first = 1;
while(tag) {
sprintf(text,"%d", swf_GetDefineID(tag));
}
- if(t == 3 && (tag->id == ST_DEFINESOUND)) {
+ if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS ||
+ tag->id == ST_DEFINEBITSLOSSLESS2)) {
+ show = 1;
+ sprintf(text,"%d", swf_GetDefineID(tag));
+ }
+
+
+ if(t == 4 && (tag->id == ST_DEFINESOUND)) {
show = 1;
sprintf(text,"%d", swf_GetDefineID(tag));
}
- if(t == 4 && (tag->id == ST_SHOWFRAME)) {
+ if(t == 5 && (tag->id == ST_SHOWFRAME)) {
show = 1;
sprintf(text,"%d", frame);
frame ++;
{
char name[80];
FILE*fi;
- sprintf(name, "pic%d.jpeg", *(U16*)tag->data);
+ sprintf(name, "pic%d.jpeg", GET16(tag->data));
/* swf jpeg images have two streams, which both start with ff d8 and
end with ff d9. The following code handles sorting the middle
<ff d9 ff d8> bytes out, so that one stream remains */
fclose(fi);
}
if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) {
- U32 end = *(U32*)&tag->data[2]+6;
+ U32 end = GET32(&tag->data[2])+6;
int pos = findjpegboundary(&tag->data[6], tag->len-6);
if(pos<0)
return;
}
}
+#ifdef _ZLIB_INCLUDED_
+static U32 mycrc32;
+
+static U32*crc32_table = 0;
+static void make_crc32_table(void)
+{
+ int t;
+ if(crc32_table)
+ return;
+ crc32_table = (U32*)malloc(1024);
+
+ for (t = 0; t < 256; t++) {
+ U32 c = t;
+ int s;
+ for (s = 0; s < 8; s++) {
+ c = (0xedb88320L*(c&1)) ^ (c >> 1);
+ }
+ crc32_table[t] = c;
+ }
+}
+static void png_write_byte(FILE*fi, U8 byte)
+{
+ fwrite(&byte,1,1,fi);
+ mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
+}
+static void png_start_chunk(FILE*fi, char*type, int len)
+{
+ U8 mytype[4]={0,0,0,0};
+ U32 mylen = REVERSESWAP32(len);
+ memcpy(mytype,type,strlen(type));
+ fwrite(&mylen, 4, 1, fi);
+ mycrc32=0xffffffff;
+ png_write_byte(fi,mytype[0]);
+ png_write_byte(fi,mytype[1]);
+ png_write_byte(fi,mytype[2]);
+ png_write_byte(fi,mytype[3]);
+}
+static void png_write_bytes(FILE*fi, U8*bytes, int len)
+{
+ int t;
+ for(t=0;t<len;t++)
+ png_write_byte(fi,bytes[t]);
+}
+static void png_write_dword(FILE*fi, U32 dword)
+{
+ png_write_byte(fi,dword>>24);
+ png_write_byte(fi,dword>>16);
+ png_write_byte(fi,dword>>8);
+ png_write_byte(fi,dword);
+}
+static void png_end_chunk(FILE*fi)
+{
+ U32 tmp = REVERSESWAP32((mycrc32^0xffffffff));
+ fwrite(&tmp,4,1,fi);
+}
+
+
+/* extract a lossless image (png) out of a tag
+ This routine was originally meant to be a one-pager. I just
+ didn't know png is _that_ much fun. :) -mk
+ */
+void handlelossless(TAG*tag)
+{
+ char name[80];
+ FILE*fi;
+ int width, height;
+ int crc;
+ int id;
+ int t;
+ U8 bpp = 1;
+ U8 format;
+ U8 tmp;
+ U8* data=0;
+ U8* data2=0;
+ U8* data3=0;
+ U32 datalen;
+ U32 datalen2;
+ U32 datalen3;
+ U8 head[] = {137,80,78,71,13,10,26,10};
+ int cols;
+ char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
+ RGBA* palette;
+ int pos;
+ int error;
+ U32 tmp32;
+
+ make_crc32_table();
+
+ if(tag->id != ST_DEFINEBITSLOSSLESS &&
+ tag->id != ST_DEFINEBITSLOSSLESS2)
+ return;
+
+ id =swf_GetU16(tag);
+ format = swf_GetU8(tag);
+ if(format == 3) bpp = 8;
+ if(format == 4) bpp = 16;
+ if(format == 5) bpp = 32;
+ if(format!=3 && format!=5) {
+ if(format==4)
+ fprintf(stderr, "Can't handle 16-bit palette images yet (image %d)\n",id);
+ else
+ fprintf(stderr, "Unknown image type %d in image %d\n", format, id);
+ return;
+ }
+ width = swf_GetU16(tag);
+ height = swf_GetU16(tag);
+ if(format == 3) cols = swf_GetU8(tag) + 1;
+// this is what format means according to the flash specification. (which is
+// clearly wrong)
+// if(format == 4) cols = swf_GetU16(tag) + 1;
+// if(format == 5) cols = swf_GetU32(tag) + 1;
+ else cols = 0;
+
+ logf("<verbose> Width %d", width);
+ logf("<verbose> Height %d", height);
+ logf("<verbose> Format %d", format);
+ logf("<verbose> Cols %d", cols);
+ logf("<verbose> Bpp %d", bpp);
+
+ datalen = (width*height*bpp/8+cols*8);
+ do {
+ if(data)
+ free(data);
+ datalen+=4096;
+ data = malloc(datalen);
+ error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
+ } while(error == Z_BUF_ERROR);
+ if(error != Z_OK) {
+ fprintf(stderr, "Zlib error %d (image %d)\n", error, id);
+ return;
+ }
+ logf("<verbose> Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols);
+ pos = 0;
+ datalen2 = datalen;
+ data2 = malloc(datalen2);
+ palette = (RGBA*)malloc(cols*sizeof(RGBA));
+
+ for(t=0;t<cols;t++) {
+ palette[t].r = data[pos++];
+ palette[t].g = data[pos++];
+ palette[t].b = data[pos++];
+ if(alpha) {
+ palette[t].a = data[pos++];
+ }
+ }
+
+ sprintf(name, "pic%d.png", id);
+ fi = save_fopen(name, "wb");
+ fwrite(head,sizeof(head),1,fi);
+
+ png_start_chunk(fi, "IHDR", 13);
+ png_write_dword(fi,width);
+ png_write_dword(fi,height);
+ png_write_byte(fi,8);
+ if(format == 3)
+ png_write_byte(fi,3); //indexed
+ else if(format == 5)
+ png_write_byte(fi,2); //rgb
+ else return;
+
+ png_write_byte(fi,0); //compression mode
+ png_write_byte(fi,0); //filter mode
+ png_write_byte(fi,0); //interlace mode
+ png_end_chunk(fi);
+
+ if(format == 3) {
+ png_start_chunk(fi, "PLTE", 768);
+ for(t=0;t<256;t++) {
+ png_write_byte(fi,palette[t].r);
+ png_write_byte(fi,palette[t].g);
+ png_write_byte(fi,palette[t].b);
+ }
+ png_end_chunk(fi);
+ }
+ {
+ int pos2 = 0;
+ int x,y;
+ int srcwidth = width * (bpp/8);
+ datalen3 = width*height*4;
+ data3 = (U8*)malloc(datalen3);
+ for(y=0;y<height;y++)
+ {
+ data3[pos2++]=0; //filter type
+ if(bpp==32) {
+ // 32 bit to 24 bit "conversion"
+ for(x=0;x<width;x++) {
+ data3[pos2++]=data[pos+1];
+ data3[pos2++]=data[pos+2];
+ data3[pos2++]=data[pos+3];
+ pos+=4; //ignore padding byte
+ }
+ }
+ else {
+ for(x=0;x<srcwidth;x++)
+ data3[pos2++]=data[pos++];
+ }
+
+ pos+=((srcwidth+3)&~3)-srcwidth; //align
+ }
+ datalen3=pos2;
+ }
+
+ if(compress (data2, &datalen2, data3, datalen3) != Z_OK) {
+ fprintf(stderr, "zlib error in pic %d\n", id);
+ return;
+ }
+ logf("<verbose> Compressed data is %d bytes", datalen2);
+ png_start_chunk(fi, "IDAT", datalen2);
+ png_write_bytes(fi,data2,datalen2);
+ png_end_chunk(fi);
+ png_start_chunk(fi, "IEND", 0);
+ png_end_chunk(fi);
+
+ free(data);
+ free(data2);
+ free(data3);
+}
+#endif
+
+FILE*mp3file;
+void handlesoundstream(TAG*tag)
+{
+ char*filename = "output.mp3";
+ if(numextracts==1) {
+ filename = destfilename;
+ if(!strcmp(filename,"output.swf"))
+ filename = "output.mp3";
+ }
+ switch(tag->id) {
+ case ST_SOUNDSTREAMHEAD:
+ if((tag->data[1]&0x30) == 0x20) { //mp3 compression
+ mp3file = fopen(filename, "wb");
+ logf("<notice> Writing mp3 data to %s",filename);
+ }
+ else
+ logf("<error> Soundstream is not mp3");
+ break;
+ case ST_SOUNDSTREAMHEAD2:
+ if((tag->data[1]&0x30) == 0x20) {//mp3 compression
+ mp3file = fopen("mainstream.mp3", "wb");
+ logf("<notice> Writing mp3 data to %s",filename);
+ }
+ else
+ logf("<error> Soundstream is not mp3 (2)");
+ break;
+ case ST_SOUNDSTREAMBLOCK:
+ if(mp3file)
+ fwrite(&tag->data[4],tag->len-4,1,mp3file);
+ break;
+ }
+}
+
int main (int argc,char ** argv)
{
TAG*tag;
char listavailable = 0;
processargs(argc, argv);
- if(!extractframes && !extractids && ! extractname && !extractjpegids)
+ if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids
+ && !extractmp3)
listavailable = 1;
if(!filename)
}
}
+ if(tag->id == ST_SOUNDSTREAMHEAD ||
+ tag->id == ST_SOUNDSTREAMHEAD2 ||
+ tag->id == ST_SOUNDSTREAMBLOCK) {
+ handlesoundstream(tag);
+ }
+
if(tag->id == ST_JPEGTABLES)
handlejpegtables(tag);
if(extractjpegids && is_in_range(id, extractjpegids)) {
handlejpeg(tag);
}
+#ifdef _ZLIB_INCLUDED_
+ if(extractpngids && is_in_range(id, extractpngids)) {
+ handlelossless(tag);
+ }
+#endif
}
else if (tag->id == ST_SETBACKGROUNDCOLOR) {
mainr = tag->data[0];
if (found)
extractTag(&swf, destfilename);
+ if(mp3file)
+ fclose(mp3file);
+
swf_FreeTags(&swf);
return 0;
}