fixed clean target (again), added support for $(EXEEXT).
[swftools.git] / src / swfc.c
index c0e8ec1..9e4d69b 100644 (file)
 #include <string.h>
 #include <memory.h>
 #include <errno.h>
-#define logf logarithmf // logf is also used by ../lib/log.h
 #include <math.h>
-#undef logf
 #include "../config.h"
 #include "../lib/rfxswf.h"
 #include "../lib/log.h"
 #include "../lib/args.h"
-#include "q.h"
+#include "../lib/q.h"
 #include "parser.h"
+#include "wav.h"
 
 //#define DEBUG
 
 static char * filename = 0;
 static char * outputname = "output.swf";
 static int verbose = 2;
+static int override_outputname = 0;
 
 static struct options_t options[] =
 {
@@ -45,6 +45,7 @@ int args_callback_option(char*name,char*val)
     }
     else if(!strcmp(name, "o")) {
        outputname = val;
+       override_outputname = 1;
        return 1;
     }
     else if(!strcmp(name, "v")) {
@@ -175,12 +176,14 @@ static SRECT currentrect; //current bounding box in current level
 static U16 currentdepth;
 static dictionary_t instances;
 static dictionary_t fonts;
+static dictionary_t sounds;
 
 typedef struct _parameters {
     int x,y; 
     float scalex, scaley; 
     CXFORM cxform;
     float rotate;
+    float shear;
     SPOINT pivot;
     SPOINT pin;
 } parameters_t;
@@ -262,12 +265,13 @@ static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
     return i;
 }
 
-static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, SPOINT pivot, SPOINT pin, CXFORM cxform)
+static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
 {
     p->x = x; p->y = y; 
     p->scalex = scalex; p->scaley = scaley;
     p->pin    = pin; p->pivot = pivot;
     p->rotate = rotate; p->cxform = cxform;
+    p->shear = shear;
 }
 
 static void parameters_clear(parameters_t*p)
@@ -277,16 +281,29 @@ static void parameters_clear(parameters_t*p)
     p->pin.x = 1; p->pin.y = 0;
     p->pivot.x = 0; p->pivot.y = 0;
     p->rotate = 0; 
+    p->shear = 0; 
     swf_GetCXForm(0, &p->cxform, 1);
 }
 
 static void makeMatrix(MATRIX*m, parameters_t*p)
 {
     SPOINT h;
-    m->sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979)*65535;
-    m->r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)*65535;
-    m->r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979)*65535;
-    m->sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)*65535;
+    float sx,r1,r0,sy;
+
+    /*       /sx r1\ /x\ 
+     *       \r0 sy/ \y/
+     */
+
+    sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979);
+    r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
+    r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979);
+    sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
+
+    m->sx = (int)(sx*65536+0.5);
+    m->r1 = (int)(r1*65536+0.5);
+    m->r0 = (int)(r0*65536+0.5);
+    m->sy = (int)(sy*65536+0.5);
+
     m->tx = m->ty = 0;
 
     h = swf_TurnPoint(p->pin, m);
@@ -331,6 +348,7 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress)
     dictionary_init(&characters);
     dictionary_init(&instances);
     dictionary_init(&fonts);
+    dictionary_init(&sounds);
 
     memset(&stack[stackpos], 0, sizeof(stack[0]));
     stack[stackpos].type = 0;
@@ -415,8 +433,13 @@ static void s_endSWF()
 
     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
        swf->movieSize = currentrect; /* "autocrop" */
+    
+    if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
+       swf->movieSize.xmax += 20; /* 1 by 1 pixels */
+       swf->movieSize.ymax += 20;
+    }
 
-    fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+    fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
     if(fi<0) {
        syntaxerror("couldn't create output file %s", filename);
     }
@@ -430,6 +453,7 @@ static void s_endSWF()
     dictionary_clear(&instances);
     dictionary_clear(&characters);
     dictionary_clear(&fonts);
+    dictionary_clear(&sounds);
 
     swf_FreeTags(swf);
     free(swf);
@@ -593,7 +617,7 @@ void s_font(char*name, char*filename)
     int f;
     SWF swf;
     SWFFONT* font;
-    f = open(filename,O_RDONLY);
+    f = open(filename,O_RDONLY|O_BINARY);
     if (f<0) { 
        warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
        font = (SWFFONT*)malloc(sizeof(SWFFONT));
@@ -632,6 +656,64 @@ void s_font(char*name, char*filename)
     dictionary_put2(&fonts, name, font);
 }
 
+typedef struct _sound_t
+{
+    U16 id;
+    TAG*tag;
+} sound_t;
+
+void s_sound(char*name, char*filename)
+{
+    struct WAV wav, wav2;
+    sound_t* sound;
+    U16*samples;
+    int numsamples;
+
+    if(!readWAV(filename, &wav)) {
+       warning("Couldn't read wav file \"%s\"", filename);
+       samples = 0;
+       numsamples = 0;
+    } else {
+       convertWAV2mono(&wav, &wav2, 44100);
+       samples = (U16*)wav2.data;
+       numsamples = wav2.size/2;
+       free(wav.data);
+    }
+
+    tag = swf_InsertTag(tag, ST_DEFINESOUND);
+    swf_SetU16(tag, id); //id
+    swf_SetSoundDefine(tag, samples, numsamples);
+   
+    sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
+    sound->tag = tag;
+    sound->id = id;
+
+    if(dictionary_lookup(&sounds, name))
+       syntaxerror("sound %s defined twice", name);
+    dictionary_put2(&sounds, name, sound);
+    
+    incrementid();
+
+    if(samples)
+       free(samples);
+}
+
+void s_playsound(char*name, int loops, int nomultiple, int stop)
+{
+    sound_t* sound = dictionary_lookup(&sounds, name);
+    SOUNDINFO info;
+    if(!sound)
+       syntaxerror("Don't know anything about sound \"%s\"", name);
+
+    tag = swf_InsertTag(tag, ST_STARTSOUND);
+    swf_SetU16(tag, sound->id); //id
+    memset(&info, 0, sizeof(info));
+    info.stop = stop;
+    info.loops = loops;
+    info.nomultiple = nomultiple;
+    swf_SetSoundInfo(tag, &info);
+}
+
 void s_shape(char*name, char*filename)
 {
     int f;
@@ -807,6 +889,7 @@ parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
     p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
     p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
     p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
+    p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
 
     p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
     p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
@@ -838,7 +921,7 @@ void s_change(char*instance, parameters_t p2)
     p1 = i->parameters;
     
     allframes = currentframe - i->lastFrame - 1;
-    if(!allframes) {
+    if(allframes < 0) {
        warning(".change ignored. can only .put/.change an object once per frame.");
        return;
     }
@@ -966,11 +1049,18 @@ int parseInt(char*str)
 }
 int parseTwip(char*str)
 {
-    char*dot = strchr(str, '.');
+    char*dot;
+    int sign=1;
+    if(str[0]=='+' || str[0]=='-') {
+       if(str[0]=='-')
+           sign = -1;
+       str++;
+    }
+    dot = strchr(str, '.');
     if(!dot) {
        int l=strlen(str);
        int t;
-       return parseInt(str)*20;
+       return sign*parseInt(str)*20;
     } else {
        int l=strlen(++dot);
        char*s;
@@ -987,11 +1077,11 @@ int parseTwip(char*str)
            l=2;
        }
        if(l==0)
-           return atoi(str)*20;
+           return sign*atoi(str)*20;
        if(l==1)
-           return atoi(str)*20+atoi(dot)*2;
+           return sign*atoi(str)*20+atoi(dot)*2;
        if(l==2)
-           return atoi(str)*20+atoi(dot)/5;
+           return sign*atoi(str)*20+atoi(dot)/5;
     }
     return 0;
 }
@@ -1024,10 +1114,23 @@ int parseColor2(char*str, RGBA*color)
     int l = strlen(str);
     int r,g,b,a;
     int t;
-    char*names[8] = {"black", "blue", "green", "cyan",
-                    "red", "violet", "yellow", "white"};
-    a=255;
-    r=g=b=0;
+
+    struct {unsigned char r,g,b;char*name;} colors[] =
+    {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
+    {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
+    {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
+    {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
+    {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
+    {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
+    {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
+    {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
+    {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
+    {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
+    {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
+    {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
+
+    a=255;r=g=b=0;
+
     if(str[0]=='#' && (l==7 || l==9)) {
        if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
            return 0;
@@ -1036,14 +1139,12 @@ int parseColor2(char*str, RGBA*color)
        color->r = r; color->g = g; color->b = b; color->a = a;
        return 1;
     }
-    for(t=0;t<8;t++)
-       if(!strcmp(str, names[t])) {
-           if(t&1)
-               b = 255;
-           if(t&2)
-               g = 255;
-           if(t&4)
-               r = 255;
+    for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
+       if(!strcmp(str, colors[t].name)) {
+           r = colors[t].r;
+           g = colors[t].g;
+           b = colors[t].b;
+           a = 255;
            color->r = r; color->g = g; color->b = b; color->a = a;
            return 1;
        }
@@ -1139,6 +1240,7 @@ static char* lu(map_t* args, char*name)
 {
     char* value = map_lookup(args, name);
     if(!value) {
+       map_dump(args, stdout, "");
        syntaxerror("internal error 2: value %s should be set", name);
     }
     return value;
@@ -1152,7 +1254,7 @@ static int c_swf(map_t*args)
     int version = parseInt(lu(args, "version"));
     int fps = (int)(parseFloat(lu(args, "fps"))*256);
     int compress = 0;
-    if(!strcmp(name, "!default!"))
+    if(!strcmp(name, "!default!") || override_outputname)
        name = outputname;
     
     if(!strcmp(compressstr, "default"))
@@ -1195,7 +1297,7 @@ int points_initialized = 0;
 
 SPOINT getPoint(SRECT r, char*name)
 {
-    int l;
+    int l=0;
     if(!strcmp(name, "center")) {
        SPOINT p;
        p.x = (r.xmin + r.xmax)/2;
@@ -1203,13 +1305,18 @@ SPOINT getPoint(SRECT r, char*name)
        return p;
     }
 
-    l = (int)dictionary_lookup(&points, name);
+    if(points_initialized)
+       l = (int)dictionary_lookup(&points, name);
     if(l==0) {
-       syntaxerror("Couldn't find point \"%s\".", name);
+       syntaxerror("Invalid point: \"%s\".", name);
     }
     l--;
     return *(SPOINT*)&mpoints.buffer[l];
 }
+static int c_gradient(map_t*args) 
+{
+    return 0;
+}
 static int c_point(map_t*args) 
 {
     char*name = lu(args, "name");
@@ -1229,6 +1336,28 @@ static int c_point(map_t*args)
     dictionary_put(&points, s1, (void*)pos);
     return 0;
 }
+static int c_play(map_t*args) 
+{
+    char*name = lu(args, "sound");
+    char*loop = lu(args, "loop");
+    char*nomultiple = lu(args, "nomultiple");
+    int nm = 0;
+    if(!strcmp(nomultiple, "nomultiple"))
+       nm = 1;
+    else
+       nm = parseInt(nomultiple);
+
+    s_playsound(name, parseInt(loop), nm, 0);
+    return 0;
+}
+
+static int c_stop(map_t*args) 
+{
+    char*name = lu(args, "sound");
+    s_playsound(name, 0,0,1);
+    return 0;
+}
+
 static int c_placement(map_t*args, int type)
 {
     char*instance = lu(args, (type==0||type==4)?"instance":"name");
@@ -1239,6 +1368,7 @@ static int c_placement(map_t*args, int type)
     char* scalexstr = lu(args, "scalex");
     char* scaleystr = lu(args, "scaley");
     char* rotatestr = lu(args, "rotate");
+    char* shearstr = lu(args, "shear");
     char* xstr="", *pivotstr="";
     char* ystr="", *anglestr="";
     char*above = lu(args, "above"); /*FIXME*/
@@ -1335,6 +1465,15 @@ static int c_placement(map_t*args, int type)
        }
     }
 
+    /* shearing */
+    if(shearstr[0]) {
+       if(isRelative(shearstr)) {
+           p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
+       } else {
+           p.shear = parseFloat(shearstr);
+       }
+    }
+
     if(pivotstr[0]) {
        if(isPoint(pivotstr)) 
            p.pivot = parsePoint(pivotstr);
@@ -1451,9 +1590,11 @@ static int c_frame(map_t*args)
 {
     char*framestr = lu(args, "n");
     int frame;
-    if(framestr[0]=='+') {
+    if(isRelative(framestr)) {
        frame = s_getframe();
-       frame += parseInt(framestr+1);
+       if(getSign(framestr)<0)
+           syntaxerror("relative frame expressions must be positive");
+       frame += parseInt(getOffset(framestr));
     }
     else {
        frame = parseInt(framestr);
@@ -1530,6 +1671,14 @@ static int c_font(map_t*args)
     return 0;
 }
 
+static int c_sound(map_t*args) 
+{
+    char*name = lu(args, "name");
+    char*filename = lu(args, "filename");
+    s_sound(name, filename);
+    return 0;
+}
+
 static int c_text(map_t*args) 
 {
     char*name = lu(args, "name");
@@ -1541,13 +1690,17 @@ static int c_text(map_t*args)
     return 0;
 }
 
+static int c_soundtrack(map_t*args) 
+{
+    return 0;
+}
+
 int fakechar(map_t*args)
 {
     char*name = lu(args, "name");
     s_box(name, 0, 0, black, 20, black, 0);
     return 0;
 }
-static int c_circle(map_t*args) {return fakechar(args);}
 
 static int c_egon(map_t*args) {return fakechar(args);}
 static int c_button(map_t*args) {return fakechar(args);}
@@ -1556,12 +1709,7 @@ static int c_edittext(map_t*args) {return fakechar(args);}
 static int c_morphshape(map_t*args) {return fakechar(args);}
 static int c_image(map_t*args) {return fakechar(args);}
 static int c_movie(map_t*args) {return fakechar(args);}
-static int c_sound(map_t*args) {return fakechar(args);}
 
-static int c_play(map_t*args) {return 0;}
-static int c_stop(map_t*args) {return 0;}
-
-static int c_soundtrack(map_t*args) {return 0;}
 static int c_buttonsounds(map_t*args) {return 0;}
 static int c_buttonput(map_t*args) {return 0;}
 static int c_texture(map_t*args) {return 0;}
@@ -1573,7 +1721,7 @@ static struct {
     char*arguments;
 } arguments[] =
 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
- {"frame", c_frame, "n=+1"},
+ {"frame", c_frame, "n=<plus>1"},
 
     // "import" type stuff
  {"shape", c_shape, "name filename"},
@@ -1585,6 +1733,11 @@ static struct {
  {"font", c_font, "name filename"},
  {"soundtrack", c_soundtrack, "filename"},
 
+    // generators of primitives
+
+ {"point", c_point, "name x=0 y=0"},
+ {"gradient", c_gradient, "name"},
+
     // character generators
  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
@@ -1601,17 +1754,16 @@ static struct {
  {"stop", c_stop, "sound"},
 
     // object placement tags
- {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
- {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
- {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
- {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
- {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
- {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
+ {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
+ {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
+ {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
+ {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
+ {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
+ {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
  {"del", c_del, "name"},
     // virtual object placement
- {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% rotate=0 above= below="},
- {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% rotate=0"},
- {"point", c_point, "name x=0 y=0"},
+ {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% shear=0 rotate=0 above= below="},
+ {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
 
     // commands which start a block
 //startclip (see above)