renamed SWAP/REVERSESWAP to {LE,BE}_TO_NATIVE
[swftools.git] / src / swfextract.c
index 965cfd3..a162e45 100644 (file)
@@ -42,14 +42,18 @@ char* extractjpegids = 0;
 char* extractfontids = 0;
 char* extractpngids = 0;
 char* extractsoundids = 0;
+char* extractbinaryids = 0;
+char* extractanyids = 0;
 char extractmp3 = 0;
 
 char* extractname = 0;
 
 char hollow = 0;
 char originalplaceobjects = 0;
+char movetozero = 0;
 
 int numextracts = 0;
+char *outputformat = NULL;
 
 struct options_t options[] =
 {
@@ -59,13 +63,17 @@ struct options_t options[] =
  {"i","id"},
  {"j","jpegs"},
  {"p","pngs"},
+ {"a","any"},
  {"P","placeobject"},
+ {"0","movetozero"},
  {"m","mp3"},
  {"s","sound"},
  {"n","name"},
  {"f","frame"},
  {"F","font"},
  {"V","version"},
+ {"b","binary"},
+ {"O","outputformat"},
  {0,0}
 };
 
@@ -112,6 +120,7 @@ int args_callback_option(char*name,char*val)
            fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n");
            exit(1);
        }
+       /* TODO: count number of IDs in val range */
        numextracts++;
        extractjpegids = val;
        return 1;
@@ -134,6 +143,15 @@ int args_callback_option(char*name,char*val)
        extractsoundids = val;
        return 1;
     } 
+    else if(!strcmp(name, "b")) {
+       if(extractbinaryids) {
+           fprintf(stderr, "Only one --binary argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
+           exit(1);
+       }
+       numextracts++;
+       extractbinaryids = val;
+       return 1;
+    } 
 #ifdef _ZLIB_INCLUDED_
     else if(!strcmp(name, "p")) {
        if(extractpngids) {
@@ -145,6 +163,11 @@ int args_callback_option(char*name,char*val)
        return 1;
     } 
 #endif
+    else if(!strcmp(name, "a")) {
+       numextracts++;
+       extractanyids = val;
+       return 1;
+    }
     else if(!strcmp(name, "f")) {
        numextracts++;
        extractframes = val;
@@ -154,10 +177,18 @@ int args_callback_option(char*name,char*val)
        originalplaceobjects = 1;
        return 0;
     }
+    else if(!strcmp(name, "0")) {
+       movetozero = 1;
+       return 0;
+    }
     else if(!strcmp(name, "w")) {
        hollow = 1;
        return 0;
     }
+    else if (!strcmp(name, "O")) {
+      outputformat = val;
+       return 1;
+    }
     else {
         printf("Unknown option: -%s\n", name);
        exit(1);
@@ -205,6 +236,23 @@ int args_callback_command(char*name,char*val)
     return 0;
 }
 
+void prepare_name(char *buf, size_t len, const char *prefix, 
+                 const char *suffix, int idx) {
+  if (outputformat!=NULL) {
+    // override default file name formatting
+    // make sure single-file behavior is not used
+    numextracts = -1;
+    // Other parts of codebase use vsnprintf, so I assume snprintf
+    // is available on all platforms that swftools currently works on.
+    // We need to check for buffer overflows now that the user is 
+    // supplying the format string.
+    snprintf(buf,len,outputformat,idx,suffix);
+  } else {
+    // use default file name formatting, unchanged
+    sprintf(buf,"%s%d.%s",prefix,idx,suffix);
+  }
+}
+
 U8 mainr,maing,mainb;
 /* 1 = used, not expanded,
    3 = used, expanded
@@ -254,6 +302,18 @@ void enumerateIDs(TAG*tag, void(*callback)(void*))
        callback(&tag->data[ptr[t]]);
 }
 
+void moveToZero(TAG*tag)
+{
+    if(!swf_isPlaceTag(tag))
+       return;
+    SWFPLACEOBJECT obj;
+    swf_GetPlaceObject(tag, &obj);
+    obj.matrix.tx = 0;
+    obj.matrix.ty = 0;
+    swf_ResetTag(tag, tag->id);
+    swf_SetPlaceObject(tag, &obj);
+}
+
 void extractTag(SWF*swf, char*filename)
 {
     SWF newswf;
@@ -271,6 +331,12 @@ void extractTag(SWF*swf, char*filename)
     newswf.fileVersion    = swf->fileVersion;
     newswf.frameRate      = swf->frameRate;
     newswf.movieSize     = swf->movieSize;
+    if(movetozero && originalplaceobjects) {
+       newswf.movieSize.xmax = swf->movieSize.xmax - swf->movieSize.xmin;
+       newswf.movieSize.ymax = swf->movieSize.ymax - swf->movieSize.ymin;
+       newswf.movieSize.xmin = 0;
+       newswf.movieSize.ymin = 0;
+    }
                
     newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
     desttag = newswf.firstTag;
@@ -316,6 +382,8 @@ void extractTag(SWF*swf, char*filename)
        }
        if(srctag->id == ST_DEFINESPRITE)
            sprite = 1;
+        if(srctag->id == ST_JPEGTABLES)
+            copy = 1;
        if(swf_isDefiningTag(srctag)) {
            int id = swf_GetDefineID(srctag);
            if(used[id])  {
@@ -325,7 +393,7 @@ void extractTag(SWF*swf, char*filename)
                swf_ExpandRect2(&objectbbox, &b);
            }
        } else 
-       if (((((srctag->id == ST_PLACEOBJECT || srctag->id == ST_PLACEOBJECT2) && originalplaceobjects)
+       if ((((swf_isPlaceTag(srctag) && originalplaceobjects)
              || srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) ||
              (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) ||
              (tagused[tagnum])) 
@@ -345,6 +413,9 @@ void extractTag(SWF*swf, char*filename)
            desttag->len = desttag->memsize = srctag->len;
            desttag->data = malloc(srctag->len);
            memcpy(desttag->data, srctag->data, srctag->len);
+           if(movetozero && swf_isPlaceTag(desttag)) {
+               moveToZero(desttag);
+           }
            if(reset)
                copy = 0;
        }
@@ -356,20 +427,25 @@ void extractTag(SWF*swf, char*filename)
        if(!originalplaceobjects && (extractids||extractname_id>=0)) {
             int number = 0;
             int id = 0;
+           int t;
             TAG* objtag = 0;
             SRECT bbox;
             memset(&bbox, 0, sizeof(SRECT));
-           for(t=0;t<65536;t++) {
-               if(is_in_range(t, extractids)) {
-                    id = t;
-                    number++;
-                }
-            }
-            if(number>=2)
+           if(extractids) {
+               for(t=0;t<65536;t++) {
+                   if(is_in_range(t, extractids)) {
+                       id = t;
+                       number++;
+                   }
+               }
+           }
+            if(number>=2) {
                printf("warning! You should use the -P when extracting multiple objects\n");
+           }
+
             if(number == 1) {
                 /* if there is only one object, we will scale it.
-                   So let's figure out it's bounding box */
+                   So let's figure out its bounding box */
                 TAG*tag = swf->firstTag;
                 while(tag) {
                     if(swf_isDefiningTag(tag) && tag->id != ST_DEFINESPRITE) {
@@ -379,11 +455,15 @@ void extractTag(SWF*swf, char*filename)
                     }
                     tag = tag->next;
                 }
-            }
+               newswf.movieSize.xmin = 0;
+               newswf.movieSize.ymin = 0;
+               newswf.movieSize.xmax = 512*20;
+               newswf.movieSize.ymax = 512*20;
+            } else {
+               if((objectbbox.xmin|objectbbox.ymin|objectbbox.xmax|objectbbox.ymax)!=0)
+                   newswf.movieSize = objectbbox;
+           }
 
-           int t;
-           if((objectbbox.xmin|objectbbox.ymin|objectbbox.xmax|objectbbox.ymax)!=0)
-               newswf.movieSize = objectbbox;
            if(extractname_id>=0) {
                desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2);
                swf_ObjectPlace(desttag, extractname_id, extractname_id, 0,0,extractname);
@@ -403,7 +483,7 @@ void extractTag(SWF*swf, char*filename)
                                 m.sx = (512*20*65536)/max;
                                 m.sy = (512*20*65536)/max;
                             }
-                            newswf.movieSize = swf_TurnRect(newswf.movieSize, &m);
+                            //newswf.movieSize = swf_TurnRect(newswf.movieSize, &m);
                         }
                        swf_ObjectPlace(desttag, t, t, &m,0,0);
                    }
@@ -444,9 +524,12 @@ int isOfType(int t, TAG*tag)
     if(t == 4 && (tag->id == ST_DEFINESOUND)) {
        show = 1;
     }
-    if(t == 5 && (tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2)) {
+    if(t == 5 && (tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2 || tag->id == ST_DEFINEFONT3)) {
        show = 1;
     }
+    if (t== 6 && (tag->id == ST_DEFINEBINARY)) {
+        show = 1;
+    }
     return show;
 }
 
@@ -456,8 +539,8 @@ void listObjects(SWF*swf)
     char first;
     int t;
     int frame = 0;
-    char*names[] = {"Shape", "MovieClip", "JPEG", "PNG", "Sound", "Font"};
-    char*options[] = {"-i", "-i", "-j", "-p", "-s", "-F"};
+    char*names[] = {"Shape", "MovieClip", "JPEG", "PNG", "Sound", "Font", "Binary"};
+    char*options[] = {"-i", "-i", "-j", "-p", "-s", "-F","-b"};
     int mp3=0;
     printf("Objects in file %s:\n",filename);
     swf_FoldAll(swf);
@@ -528,7 +611,7 @@ void listObjects(SWF*swf)
        printf(" [-m] 1 MP3 Soundstream\n");
 }
 
-void handlefont(SWF*swf, TAG*tag)
+int handlefont(SWF*swf, TAG*tag)
 {
     SWFFONT* f=0;
     U16 id;
@@ -537,31 +620,34 @@ void handlefont(SWF*swf, TAG*tag)
     int t;
 
     id = swf_GetDefineID(tag);
-    sprintf(name, "font%d.swf", id);
+    prepare_name(name, sizeof(name), "font", "swf", id);
     if(numextracts==1) {
        filename = destfilename;
     }
 
     swf_FontExtract(swf, id, &f);
     if(!f) {
-       printf("Couldn't extract font %d\n", id);
-       return;
+       if (!extractanyids) {
+          printf("Couldn't extract font %d\n", id);
+       }
+       return 0;
     }
-    if(!f->layout)
-       swf_FontCreateLayout(f);
 
     swf_WriteFont(f, filename);
     swf_FontFree(f);
+    return 1;
 }
 
-U8*jpegtables = 0;
-int jpegtablessize;
+static char has_jpegtables=0;
+static U8*jpegtables = 0;
+static int jpegtablessize = 0;
 
 void handlejpegtables(TAG*tag)
 {
     if(tag->id == ST_JPEGTABLES) {
        jpegtables = tag->data;
        jpegtablessize = tag->len;
+       has_jpegtables = 1;
     }
 }
 
@@ -579,7 +665,7 @@ int findjpegboundary(U8*data, int len)
 {
     int t;
     int pos=-1;
-    for(t=0;t<len;t++) {
+    for(t=0;t<len-4;t++) {
        if(data[t  ]==0xff &&
           data[t+1]==0xd9 &&
           data[t+2]==0xff &&
@@ -591,13 +677,13 @@ int findjpegboundary(U8*data, int len)
 }
 
 /* extract jpeg data out of a tag */
-void handlejpeg(TAG*tag)
+int handlejpeg(TAG*tag)
 {
     char name[80];
     char*filename = name;
     FILE*fi;
     
-    sprintf(name, "pic%d.jpg", GET16(tag->data));
+    prepare_name(name, sizeof(name), "pic", "jpg", GET16(tag->data));
     if(numextracts==1) {
        filename = destfilename;
        if(!strcmp(filename,"output.swf"))
@@ -606,10 +692,14 @@ void handlejpeg(TAG*tag)
     /* 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 */
-    if(tag->id == ST_DEFINEBITSJPEG && tag->len>2 && jpegtables) {
+    if(tag->id == ST_DEFINEBITSJPEG && tag->len>2 && has_jpegtables) {
        fi = save_fopen(filename, "wb");
-       fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8)
-       fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9)
+       if(jpegtablessize>=2) {
+           fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8)
+           fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9)
+       } else {
+           fwrite(tag->data+2, tag->len-2, 1, fi);
+       }
        fclose(fi);
     }
     else if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) {
@@ -630,19 +720,27 @@ void handlejpeg(TAG*tag)
     else if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) {
        U32 end = GET32(&tag->data[2])+6;
        int pos = findjpegboundary(&tag->data[6], tag->len-6);
-       if(pos<0)
-           return;
-       pos+=6;
-       fi = save_fopen(filename, "wb");
-       fwrite(&tag->data[6], pos-6, 1, fi);
-       fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
-       fclose(fi);
+       if(pos<0) {
+            fi = save_fopen(filename, "wb");
+            fwrite(&tag->data[6], end-6, 1, fi);
+            fclose(fi);
+        } else {
+            pos+=6;
+            fi = save_fopen(filename, "wb");
+            fwrite(&tag->data[6], pos-6, 1, fi);
+            fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
+            fclose(fi);
+        }
     }
     else {
        int id = GET16(tag->data);
-       fprintf(stderr, "Object %d is not a JPEG picture!\n",id);
-       exit(1);
+       if (!extractanyids) {
+         fprintf(stderr, "Object %d is not a JPEG picture!\n", id);
+         exit(1);
+        }
+       return 0;
     }
+    return 1;
 }
 
 #ifdef _ZLIB_INCLUDED_
@@ -673,7 +771,7 @@ static inline void png_write_byte(FILE*fi, U8 byte)
 static void png_start_chunk(FILE*fi, char*type, int len)
 {
     U8 mytype[4]={0,0,0,0};
-    U32 mylen = REVERSESWAP32(len);
+    U32 mylen = BE_32_TO_NATIVE(len);
     memcpy(mytype,type,strlen(type));
     fwrite(&mylen, 4, 1, fi);
     mycrc32=0xffffffff;
@@ -697,7 +795,7 @@ static void png_write_dword(FILE*fi, U32 dword)
 }
 static void png_end_chunk(FILE*fi)
 {
-    U32 tmp = REVERSESWAP32((mycrc32^0xffffffff));
+    U32 tmp = BE_32_TO_NATIVE((mycrc32^0xffffffff));
     fwrite(&tmp,4,1,fi);
 }
 
@@ -706,7 +804,7 @@ static void png_end_chunk(FILE*fi)
    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)
+int handlelossless(TAG*tag)
 {
     char name[80];
     char*filename = name;
@@ -718,11 +816,11 @@ void handlelossless(TAG*tag)
     U8 bpp = 1;
     U8 format;
     U8 tmp;
-    U8* data=0;
+    Bytef* data=0;
     U8* data2=0;
     U8* data3=0;
-    U32 datalen;
-    U32 datalen2;
+    uLongf datalen;
+    uLongf datalen2;
     U32 datalen3;
     U8 head[] = {137,80,78,71,13,10,26,10};
     int cols;
@@ -737,8 +835,11 @@ void handlelossless(TAG*tag)
     if(tag->id != ST_DEFINEBITSLOSSLESS &&
        tag->id != ST_DEFINEBITSLOSSLESS2) {
        int id = GET16(tag->data);
-       fprintf(stderr, "Object %d is not a PNG picture!\n",id);
-       exit(1);
+       if (!extractanyids) {
+         fprintf(stderr, "Object %d is not a PNG picture!\n",id);
+         exit(1);
+       }
+       return 0;
     }
 
     id =swf_GetU16(tag);
@@ -751,7 +852,7 @@ void handlelossless(TAG*tag)
        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;
+       return 0;
     }
     width = swf_GetU16(tag);
     height = swf_GetU16(tag);
@@ -778,11 +879,11 @@ void handlelossless(TAG*tag)
     } while(error == Z_BUF_ERROR);
     if(error != Z_OK) {
        fprintf(stderr, "Zlib error %d (image %d)\n", error, id);
-       return;
+       return 0;
     }
     msg("<verbose> Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols);
     pos = 0;
-    datalen2 = datalen;
+    datalen2 = datalen+16;
     data2 = malloc(datalen2);
     palette = (RGBA*)malloc(cols*sizeof(RGBA));
 
@@ -795,7 +896,7 @@ void handlelossless(TAG*tag)
        }
     }
 
-    sprintf(name, "pic%d.png", id);
+    prepare_name(name, sizeof(name), "pic", "png", id);
     if(numextracts==1) {
        filename = destfilename;
        if(!strcmp(filename,"output.swf"))
@@ -814,7 +915,7 @@ void handlelossless(TAG*tag)
      png_write_byte(fi,2); //rgb
      else if(format == 5 && alpha==1)
      png_write_byte(fi,6); //rgba
-     else return;
+     else return 0;
 
      png_write_byte(fi,0); //compression mode
      png_write_byte(fi,0); //filter mode
@@ -830,12 +931,21 @@ void handlelossless(TAG*tag)
             png_write_byte(fi,palette[t].b);
         }
        png_end_chunk(fi);
+
+       if(alpha) {
+           /* write alpha palette */
+           png_start_chunk(fi, "tRNS", 256);
+           for(t=0;t<256;t++) {
+               png_write_byte(fi,palette[t].a);
+           }
+           png_end_chunk(fi);
+       }
     }
     {
        int pos2 = 0;
        int x,y;
        int srcwidth = width * (bpp/8);
-       datalen3 = width*height*4;
+       datalen3 = (width*4+5)*height;
        data3 = (U8*)malloc(datalen3);
        for(y=0;y<height;y++)
        {
@@ -871,7 +981,7 @@ void handlelossless(TAG*tag)
 
     if(compress (data2, &datalen2, data3, datalen3) != Z_OK) {
        fprintf(stderr, "zlib error in pic %d\n", id);
-       return;
+       return 0;
     }
     msg("<verbose> Compressed data is %d bytes", datalen2);
     png_start_chunk(fi, "IDAT", datalen2);
@@ -883,10 +993,11 @@ void handlelossless(TAG*tag)
     free(data);
     free(data2);
     free(data3);
+    return 1;
 }
 #endif
 
-FILE*mp3file;
+static FILE*mp3file=0;
 void handlesoundstream(TAG*tag)
 {
     char*filename = "output.mp3";
@@ -919,7 +1030,7 @@ void handlesoundstream(TAG*tag)
     }
 }
 
-void handledefinesound(TAG*tag)
+int handledefinesound(TAG*tag)
 {
     U8 flags;
     U32 samples;
@@ -953,8 +1064,10 @@ void handledefinesound(TAG*tag)
     } else if(format == 1) { // adpcm
        printf("Sound is ADPCM, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono");
        extension = "adpcm";
+    } else {
+        return 0;
     }
-    sprintf(buf, "sound%d.%s", id, extension);
+    prepare_name(buf, sizeof(buf), "sound", extension, id);
     if(numextracts==1) {
        filename = destfilename;
        if(!strcmp(filename,"output.swf")) {
@@ -965,6 +1078,34 @@ void handledefinesound(TAG*tag)
     fi = save_fopen(filename, "wb");
     fwrite(&tag->data[tag->pos], tag->len - tag->pos, 1, fi);
     fclose(fi);
+    return 1;
+}
+
+int handlebinary(TAG*tag) {
+    FILE *fout = NULL;
+    char buf[100];
+    char *filename = buf;
+    int len = tag->memsize;
+    int dx = 6; // offset to binary data
+    if (tag->id!=ST_DEFINEBINARY) {
+        if (!extractanyids) {
+          fprintf(stderr, "Object %d is not a binary entity!\n",
+                          GET16(tag->data));
+        }
+        return 0;
+    }
+    prepare_name(buf, sizeof(buf), "binary", "bin", GET16(tag->data));
+    if(numextracts==1) {
+       filename = destfilename;
+       if(!strcmp(filename,"output.swf")) {
+           sprintf(buf, "output.bin");
+           filename = buf;
+       }
+    }
+    fout = fopen(filename, "wb");
+    fwrite(tag->data+dx,len-dx,1,fout);
+    fclose(fout);
+    return 1;
 }
 
 int main (int argc,char ** argv)
@@ -980,9 +1121,14 @@ int main (int argc,char ** argv)
     processargs(argc, argv);
 
     if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids
-       && !extractmp3 && !extractsoundids && !extractfontids)
+       && !extractmp3 && !extractsoundids && !extractfontids && !extractbinaryids && !extractanyids)
        listavailable = 1;
 
+    if(!originalplaceobjects && movetozero) {
+       fprintf(stderr, "Error: -0 (--movetozero) can only be used in conjunction with -P (--placeobject)\n");
+       return 0;
+    }
+
     if(!filename)
     {
         fprintf(stderr, "You must supply a filename.\n");
@@ -1060,8 +1206,9 @@ int main (int argc,char ** argv)
                handlesoundstream(tag);
        }
 
-       if(tag->id == ST_JPEGTABLES)
+       if(tag->id == ST_JPEGTABLES) {
            handlejpegtables(tag);
+       }
 
        if(swf_isDefiningTag(tag)) {
            int id = swf_GetDefineID(tag);
@@ -1079,24 +1226,47 @@ int main (int argc,char ** argv)
            if(extractsoundids && is_in_range(id, extractsoundids)) {
                handledefinesound(tag);
            }
+           if(extractbinaryids && is_in_range(id, extractbinaryids)) {
+               handlebinary(tag);
+           }
 #ifdef _ZLIB_INCLUDED_
            if(extractpngids && is_in_range(id, extractpngids)) {
                handlelossless(tag);
            }
 #endif
+           if(extractanyids && is_in_range(id, extractanyids)) {
+               if (handlefont(&swf,tag)) {
+                   // pass
+               } else if (handlejpeg(tag)) {
+                   // pass
+               } else if (handlebinary(tag)) {
+                   // pass
+#ifdef _ZLIB_INCLUDED_
+               } else if (handlelossless(tag)) {
+                   // pass
+#endif
+               } else if (handledefinesound(tag)) {
+                   // Not sure if sound code checks carefully for type.
+                   // pass
+               } else {
+                   printf("#%d not processed\n", id);
+               }
+           }
        }
        else if (tag->id == ST_SETBACKGROUNDCOLOR) {
            mainr = tag->data[0];
            maing = tag->data[1];
            mainb = tag->data[2];
        }
-       else if(tag->id == ST_PLACEOBJECT2) {
+       else if(swf_isPlaceTag(tag) && tag->id != ST_PLACEOBJECT ) {
            char*name = swf_GetName(tag);
            if(name && extractname && !strcmp(name, extractname)) {
                int id = swf_GetPlaceID(tag); 
                used[id] = 5;
                found = 1;
-               tagused[tagnum] = 1;
+               if(originalplaceobjects) {
+                   tagused[tagnum] = 1;
+               }
                depths[swf_GetDepth(tag)] = 1;
                extractname_id = id;
            }
@@ -1121,8 +1291,13 @@ int main (int argc,char ** argv)
     if (found)
        extractTag(&swf, destfilename);
 
-    if(mp3file)
+    if(mp3file) {
        fclose(mp3file);
+    } else {
+        if(extractmp3) {
+            msg("<error> Didn't find a soundstream in file");
+        }
+    }
 
     swf_FreeTags(&swf);
     return 0;