updated documentation
[swftools.git] / src / swfc.c
index b1f42cb..4f42b10 100644 (file)
 #include "../lib/log.h"
 #include "../lib/args.h"
 #include "../lib/q.h"
+#include "../lib/mp3.h"
+#include "../lib/wav.h"
 #include "parser.h"
-#include "wav.h"
+#include "../lib/png.h"
 
 //#define DEBUG
 
@@ -42,10 +44,12 @@ static char * outputname = "output.swf";
 static int verbose = 2;
 static int optimize = 0;
 static int override_outputname = 0;
+static int do_cgi = 0;
 
 static struct options_t options[] = {
 {"h", "help"},
 {"V", "version"},
+{"C", "cgi"},
 {"v", "verbose"},
 {"o", "output"},
 {0,0}
@@ -66,6 +70,10 @@ int args_callback_option(char*name,char*val)
        optimize = 1;
        return 0;
     }
+    else if(!strcmp(name, "C")) {
+       do_cgi = 1;
+       return 0;
+    }
     else if(!strcmp(name, "v")) {
        verbose ++;
        return 0;
@@ -87,6 +95,7 @@ void args_callback_usage(char *name)
     printf("\n");
     printf("-h , --help                    Print short help message and exit\n");
     printf("-V , --version                 Print version info and exit\n");
+    printf("-C , --cgi                     Output to stdout (for use in CGI environments)\n");
     printf("-v , --verbose                 Increase verbosity. \n");
     printf("-o , --output <filename>       Set output file to <filename>.\n");
     printf("\n");
@@ -117,7 +126,7 @@ static void syntaxerror(char*format, ...)
     va_start(arglist, format);
     vsprintf(buf, format, arglist);
     va_end(arglist);
-    printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
+    fprintf(stderr, "\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
     exit(1);
 }
 
@@ -128,7 +137,7 @@ static void warning(char*format, ...)
     va_start(arglist, format);
     vsprintf(buf, format, arglist);
     va_end(arglist);
-    printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
+    fprintf(stderr, "\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
 }
 
 static void readToken()
@@ -671,11 +680,16 @@ static void s_endSWF()
        warning("Empty bounding box for movie");
     }
     
-    fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
+    if(do_cgi)
+       fi = fileno(stdout);
+    else
+       fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
     if(fi<0) {
        syntaxerror("couldn't create output file %s", filename);
     }
-    if(swf->compressed) 
+    if(do_cgi)
+       {if(swf_WriteCGI(swf)<0) syntaxerror("WriteCGI() failed.\n");}
+    else if(swf->compressed) 
        {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
     else
        {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
@@ -713,7 +727,7 @@ int s_getframe()
     return currentframe+1;
 }
 
-void s_frame(int nr, int cut, char*name)
+void s_frame(int nr, int cut, char*name, char anchor)
 {
     int t;
     TAG*now = tag;
@@ -727,11 +741,15 @@ void s_frame(int nr, int cut, char*name)
        if(t==nr-1 && name && *name) {
            tag = swf_InsertTag(tag, ST_FRAMELABEL);
            swf_SetString(tag, name);
+           if(anchor)
+               swf_SetU8(tag, 1); //make this an anchor
        }
     }
-    if(nr == 0 && currentframe == 0 && name) {
+    if(nr == 0 && currentframe == 0 && name && *name) {
         tag = swf_InsertTag(tag, ST_FRAMELABEL);
         swf_SetString(tag, name);
+       if(anchor)
+           swf_SetU8(tag, 1); //make this an anchor
     }
 
     if(cut) {
@@ -805,16 +823,18 @@ void s_box(char*name, int width, int height, RGBA color, int linewidth, char*tex
     r2.ymax = height;
     tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
     swf_ShapeNew(&s);
-    if(linewidth)
-        ls1 = swf_ShapeAddLineStyle(s,linewidth>=20?linewidth-20:0,&color);
+    if(linewidth) {
+       linewidth = linewidth>=20?linewidth-20:0;
+        ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    }
     if(texture)
        fs1 = addFillStyle(s, &r2, texture);
 
     swf_SetU16(tag,id);
-    r.xmin = r2.xmin-linewidth-linewidth/2;
-    r.ymin = r2.ymin-linewidth-linewidth/2;
-    r.xmax = r2.xmax+linewidth+linewidth/2;
-    r.ymax = r2.ymax+linewidth+linewidth/2;
+    r.xmin = r2.xmin-linewidth/2;
+    r.ymin = r2.ymin-linewidth/2;
+    r.xmax = r2.xmax+linewidth/2;
+    r.ymax = r2.ymax+linewidth/2;
     swf_SetRect(tag,&r);
     swf_SetShapeHeader(tag,s);
     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
@@ -843,22 +863,24 @@ void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*textu
 
     tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
     swf_ShapeNew(&s);
-    if(linewidth)
-        ls1 = swf_ShapeAddLineStyle(s,linewidth>=20?linewidth-20:0,&color);
+    if(linewidth) {
+       linewidth = linewidth>=20?linewidth-20:0;
+        ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    }
     if(texture)
        fs1 = addFillStyle(s, &r2, texture);
     
     swf_SetU16(tag,id);
-    rect.xmin = r2.xmin-linewidth-linewidth/2;
-    rect.ymin = r2.ymin-linewidth-linewidth/2;
-    rect.xmax = r2.xmax+linewidth+linewidth/2;
-    rect.ymax = r2.ymax+linewidth+linewidth/2;
+    rect.xmin = r2.xmin-linewidth/2;
+    rect.ymin = r2.ymin-linewidth/2;
+    rect.xmax = r2.xmax+linewidth/2;
+    rect.ymax = r2.ymax+linewidth/2;
 
     swf_SetRect(tag,&rect);
     swf_SetShapeStyles(tag, s);
     swf_ShapeCountBits(s,0,0);
-    swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, 1,            1, 
-                        &s->data,             &s->bitlen,             s->bits.fill, s->bits.line);
+    swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line, 
+                        &s->data,             &s->bitlen,             s->bits.fill,              s->bits.line);
     swf_SetShapeBits(tag, s);
     swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
     swf_ShapeFree(s);
@@ -878,15 +900,17 @@ void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
 
     tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
     swf_ShapeNew(&s);
-    if(linewidth)
-        ls1 = swf_ShapeAddLineStyle(s,linewidth>=20?linewidth-20:0,&color);
+    if(linewidth) {
+       linewidth = linewidth>=20?linewidth-20:0;
+        ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
+    }
     if(texture)
        fs1 = addFillStyle(s, &r2, texture);
     swf_SetU16(tag,id);
-    rect.xmin = r2.xmin-linewidth-linewidth/2;
-    rect.ymin = r2.ymin-linewidth-linewidth/2;
-    rect.xmax = r2.xmax+linewidth+linewidth/2;
-    rect.ymax = r2.ymax+linewidth+linewidth/2;
+    rect.xmin = r2.xmin-linewidth/2;
+    rect.ymin = r2.ymin-linewidth/2;
+    rect.xmax = r2.xmax+linewidth/2;
+    rect.ymax = r2.ymax+linewidth/2;
 
     swf_SetRect(tag,&rect);
     swf_SetShapeHeader(tag,s);
@@ -973,7 +997,7 @@ void s_quicktime(char*name, char*url)
     incrementid();
 }
 
-void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
+void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags, int align)
 {
     SWFFONT*font = 0;
     EditTextLayout layout;
@@ -987,7 +1011,7 @@ void s_edittext(char*name, char*fontname, int size, int width, int height, char*
     }
     tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
     swf_SetU16(tag, id);
-    layout.align = 0;
+    layout.align = align;
     layout.leftmargin = 0;
     layout.rightmargin = 0;
     layout.indent = 0;
@@ -1014,13 +1038,8 @@ void s_image(char*name, char*type, char*filename, int quality)
     SRECT r;
     int imageID = id;
     int width, height;
-    if(type=="png") {
-       warning("image type \"png\" not supported yet!");
-       s_box(name, 0, 0, black, 20, 0);
-       return;
-    }
-    if(type=="jpeg") {
-#ifndef HAVE_LIBJPEG
+    if(!strcmp(type,"jpeg")) {
+#ifndef HAVE_JPEGLIB
        warning("no jpeg support compiled in");
        s_box(name, 0, 0, black, 20, 0);
        return;
@@ -1042,6 +1061,31 @@ void s_image(char*name, char*type, char*filename, int quality)
        s_addimage(name, id, tag, r);
        incrementid();
 #endif
+    } else if(!strcmp(type,"png")) {
+       RGBA*data = 0;
+       swf_SetU16(tag, imageID);
+
+       getPNG(filename, &width, &height, (unsigned char**)&data);
+
+       if(!data) {
+           syntaxerror("Image \"%s\" not found, or contains errors", filename);
+       }
+
+       /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
+       tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
+       swf_SetU16(tag, imageID);
+       swf_SetLosslessImage(tag, data, width, height);
+
+       r.xmin = 0;
+       r.ymin = 0;
+       r.xmax = width*20;
+       r.ymax = height*20;
+       s_addimage(name, id, tag, r);
+       incrementid();
+    } else {
+       warning("image type \"%s\" not supported yet!", type);
+       s_box(name, 0, 0, black, 20, 0);
+       return;
     }
 
     /* step 2: the character */
@@ -1084,6 +1128,8 @@ void s_texture(char*name, char*object, int x, int y, float scalex, float scaley,
     parameters_t p;
     FILLSTYLE*fs = &texture->fs;
 
+    memset(&p, 0, sizeof(parameters_t));
+
     if(bitmap) {
        fs->type = FILL_TILED;
        fs->id_bitmap = bitmap->id;
@@ -1150,6 +1196,10 @@ void s_font(char*name, char*filename)
     font->id = id;
     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
     swf_FontSetDefine2(tag, font);
+    tag = swf_InsertTag(tag, ST_EXPORTASSETS);
+    swf_SetU16(tag, 1);
+    swf_SetU16(tag, id);
+    swf_SetString(tag, name);
     incrementid();
 
     if(dictionary_lookup(&fonts, name))
@@ -1168,17 +1218,16 @@ typedef struct _sound_t
 void s_sound(char*name, char*filename)
 {
     struct WAV wav, wav2;
+    struct MP3 mp3;
     sound_t* sound;
-    U16*samples;
-    int numsamples;
-    int t;
-
-    if(!readWAV(filename, &wav)) {
-       warning("Couldn't read wav file \"%s\"", filename);
-       samples = 0;
-       numsamples = 0;
-    } else {
-       convertWAV2mono(&wav, &wav2, 44100);
+    U16*samples = NULL;
+    unsigned numsamples;
+    unsigned blocksize = 1152;
+    int is_mp3 = 0;
+
+    if(wav_read(&wav, filename)) {
+        int t;
+       wav_convert2mono(&wav, &wav2, 44100);
        samples = (U16*)wav2.data;
        numsamples = wav2.size/2;
        free(wav.data);
@@ -1188,12 +1237,56 @@ void s_sound(char*name, char*filename)
            samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
        }
 #endif
+    } else if(mp3_read(&mp3, filename)) {
+        fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
+        blocksize = 1;
+        is_mp3 = 1;
+    }
+    else
+    {
+       warning("Couldn't read WAV/MP3 file \"%s\"", filename);
+       samples = 0;
+       numsamples = 0;
+    }
+    
+    if(numsamples%blocksize != 0)
+    {
+       // apply padding, so that block is a multiple of blocksize
+       int numblocks = (numsamples+blocksize-1)/blocksize;
+       int numsamples2;
+       U16* samples2;
+       numsamples2 = numblocks * blocksize;
+       samples2 = malloc(sizeof(U16)*numsamples2);
+       memcpy(samples2, samples, numsamples*sizeof(U16));
+       memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
+       numsamples = numsamples2;
+       samples = samples2;
     }
 
     tag = swf_InsertTag(tag, ST_DEFINESOUND);
     swf_SetU16(tag, id); //id
-    swf_SetSoundDefine(tag, samples, numsamples);
-   
+    if(is_mp3)
+    {
+        swf_SetSoundDefineMP3(
+                tag, mp3.data, mp3.size,
+                mp3.SampRate,
+                mp3.Channels,
+                mp3.NumFrames);
+       mp3_clear(&mp3);
+    }
+    else
+    {
+        swf_SetSoundDefine(tag, samples, numsamples);
+    }
+    
+    tag = swf_InsertTag(tag, ST_NAMECHARACTER);
+    swf_SetU16(tag, id);
+    swf_SetString(tag, name);
+    tag = swf_InsertTag(tag, ST_EXPORTASSETS);
+    swf_SetU16(tag, 1);
+    swf_SetU16(tag, id);
+    swf_SetString(tag, name);
+
     sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
     sound->tag = tag;
     sound->id = id;
@@ -1313,7 +1406,7 @@ int s_swf3action(char*name, char*action)
     ActionTAG* a = 0;
     instance_t* object = 0;
     if(name) 
-       dictionary_lookup(&instances, name);
+       object = (instance_t*)dictionary_lookup(&instances, name);
     if(!object && name && *name) {
        /* we have a name, but couldn't find it. Abort. */
        return 0;
@@ -1341,7 +1434,9 @@ void s_outline(char*name, char*format, char*source)
     SHAPE2* shape2;
     SRECT bounds;
     
+    //swf_Shape10DrawerInit(&draw, 0);
     swf_Shape11DrawerInit(&draw, 0);
+
     draw_string(&draw, source);
     draw.finish(&draw);
     shape = swf_ShapeDrawerToShape(&draw);
@@ -1427,13 +1522,17 @@ void s_includeswf(char*name, char*filename)
            level--;
        if(!level)
            break;
-       /* We simply dump all tags right after the sprite
-          header, relying on the fact that swf_OptimizeTagOrder() will
-          sort things out for us later. 
-          We also rely on the fact that the imported SWF is well-formed.
-        */
-       tag = swf_InsertTag(tag, ftag->id);
-       swf_SetBlock(tag, ftag->data, ftag->len);
+
+       if(ftag->id != ST_SETBACKGROUNDCOLOR) {
+           /* We simply dump all tags right after the sprite
+              header, relying on the fact that swf_OptimizeTagOrder() will
+              sort things out for us later. 
+              We also rely on the fact that the imported SWF is well-formed.
+            */
+           tag = swf_InsertTag(tag, ftag->id);
+           swf_SetBlock(tag, ftag->data, ftag->len);
+       }
+
        ftag = ftag->next;
     }
     if(!ftag)
@@ -1658,7 +1757,7 @@ typedef int command_func_t(map_t*args);
 
 SRECT parseBox(char*str)
 {
-    SRECT r;
+    SRECT r = {0,0,0,0};
     float xmin, xmax, ymin, ymax;
     char*x = strchr(str, 'x');
     char*d1=0,*d2=0;
@@ -1728,8 +1827,10 @@ int parseTwip(char*str)
        int t;
        return sign*parseInt(str)*20;
     } else {
-       int l=strlen(++dot);
+       char* old = strdup(str);
+       int l=strlen(dot+1);
        char*s;
+       *dot++ = 0;
        for(s=str;s<dot-1;s++)
            if(*s<'0' || *s>'9')
                syntaxerror("Not a coordinate: \"%s\"", str);
@@ -1738,10 +1839,12 @@ int parseTwip(char*str)
                syntaxerror("Not a coordinate: \"%s\"", str);
        }
        if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
-           warning("precision loss: %s converted to twip: %s", str, dot);
+           dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
            dot[2] = 0;
            l=2;
+           warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
        }
+       free(old);
        if(l==0)
            return sign*atoi(str)*20;
        if(l==1)
@@ -2417,7 +2520,14 @@ static int c_frame(map_t*args)
 {
     char*framestr = lu(args, "n");
     char*cutstr = lu(args, "cut");
+    
     char*name = lu(args, "name");
+    char*anchor = lu(args, "anchor");
+    char buf[40];
+
+    if(!strcmp(anchor, "anchor") && !*name)
+       name = framestr;
+
     int frame;
     int cut = 0;
     if(strcmp(cutstr, "no"))
@@ -2434,7 +2544,7 @@ static int c_frame(map_t*args)
                && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
            syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
     }
-    s_frame(frame, cut, name);
+    s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
     return 0;
 }
 static int c_primitive(map_t*args) 
@@ -2700,7 +2810,7 @@ static int c_on_key(map_t*args)
 
 static int c_edittext(map_t*args) 
 {
- //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
+ //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @autosize=0"},
     char*name = lu(args, "name");
     char*font = lu(args, "font");
     int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
@@ -2717,6 +2827,9 @@ static int c_edittext(map_t*args)
     char*noselectstr = lu(args, "noselect");
     char*readonlystr = lu(args, "readonly");
     char*borderstr = lu(args, "border");
+    char*autosizestr = lu(args, "autosize");
+    char*alignstr = lu(args, "align");
+    int align = -1;
 
     int flags = 0;
     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
@@ -2726,8 +2839,14 @@ static int c_edittext(map_t*args)
     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
-
-    s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
+    if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
+    if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
+    else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
+    else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
+    else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
+    else syntaxerror("Unknown alignment: %s", alignstr);
+
+    s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
     return 0;
 }
 
@@ -2790,7 +2909,7 @@ static struct {
     char*arguments;
 } arguments[] =
 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
- {"frame", c_frame, "n=<plus>1 name= @cut=no"},
+ {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
  // "import" type stuff
  {"swf", c_swf, "name filename"},
  {"shape", c_swf, "name filename"},
@@ -2816,7 +2935,7 @@ static struct {
 
  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
  {"text", c_text, "name text font size=100% color=white"},
- {"edittext", c_edittext, "name font= size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0"},
+ {"edittext", c_edittext, "name font= size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0 @autosize=0 align="},
  {"morphshape", c_morphshape, "name start end"},
  {"button", c_button, "name"},
     {"show", c_show,             "name x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below= as="},
@@ -3089,7 +3208,7 @@ int main (int argc,char ** argv)
     
     file = generateTokens(filename);
     if(!file) {
-       printf("parser returned error.\n");
+       fprintf(stderr, "parser returned error.\n");
        return 1;
     }
     pos=0;