X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fswfc.c;h=131c794d6eccab29070eb4f46ed40400b18d5aee;hb=2c9f24cd50973dd2b69395fb284ca2e8981f93c2;hp=064261e3e3a6e8564295f54d3a16fc1c0ad39479;hpb=b3635a1f4b060ad0ad167301d90596a29821b0fd;p=swftools.git diff --git a/src/swfc.c b/src/swfc.c index 064261e..131c794 100644 --- a/src/swfc.c +++ b/src/swfc.c @@ -4,8 +4,20 @@ Part of the swftools package. Copyright (c) 2001 Matthias Kramm + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - This file is distributed under the GPL, see file COPYING for details */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include @@ -13,29 +25,32 @@ #include #include #include -#define logf logarithmf // logf is also used by ../lib/log.h #include -#undef logf #include "../config.h" #include "../lib/rfxswf.h" +#include "../lib/drawer.h" #include "../lib/log.h" #include "../lib/args.h" -#include "q.h" +#include "../lib/q.h" +#include "../lib/mp3.h" +#include "../lib/wav.h" #include "parser.h" +#include "../lib/png.h" //#define DEBUG static char * filename = 0; static char * outputname = "output.swf"; static int verbose = 2; +static int optimize = 0; static int override_outputname = 0; -static struct options_t options[] = -{ - {"o","output"}, - {"v","verbose"}, - {"V","version"}, - {0,0} +static struct options_t options[] = { +{"h", "help"}, +{"V", "version"}, +{"v", "verbose"}, +{"o", "output"}, +{0,0} }; int args_callback_option(char*name,char*val) @@ -49,6 +64,10 @@ int args_callback_option(char*name,char*val) override_outputname = 1; return 1; } + else if(!strcmp(name, "O")) { + optimize = 1; + return 0; + } else if(!strcmp(name, "v")) { verbose ++; return 0; @@ -63,12 +82,16 @@ int args_callback_longoption(char*name,char*val) { return args_long2shortoption(options, name, val); } -void args_callback_usage(char*name) +void args_callback_usage(char *name) { - printf("Usage: %s [-o filename] file.wav\n", name); - printf("\t-v , --verbose\t\t\t Be more verbose\n"); - printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n"); - printf("\t-V , --version\t\t\t Print program version and exit\n"); + printf("\n"); + printf("Usage: %s [-o file.swf] file.sc\n", name); + printf("\n"); + printf("-h , --help Print short help message and exit\n"); + printf("-V , --version Print version info and exit\n"); + printf("-v , --verbose Increase verbosity. \n"); + printf("-o , --output Set output file to .\n"); + printf("\n"); } int args_callback_command(char*name,char*val) { @@ -109,7 +132,7 @@ static void warning(char*format, ...) va_end(arglist); printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf); } - + static void readToken() { type = file[pos].type; @@ -149,7 +172,7 @@ static int noMoreTokens() struct _character; static struct level { - int type; //0=swf, 1=sprite, 2=clip + int type; //0=swf, 1=sprite, 2=clip, 3=button /* for swf (0): */ SWF*swf; @@ -163,11 +186,16 @@ static struct level int oldframe; dictionary_t oldinstances; SRECT oldrect; + TAG* cut; } stack[256]; static int stackpos = 0; static dictionary_t characters; +static dictionary_t images; +static dictionary_t textures; +static dictionary_t outlines; +static dictionary_t gradients; static char idmap[65536]; static TAG*tag = 0; //current tag @@ -177,6 +205,7 @@ 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; @@ -202,6 +231,21 @@ typedef struct _instance { U16 lastFrame; //frame lastTag is in } instance_t; +typedef struct _outline { + SHAPE* shape; + SRECT bbox; +} outline_t; + +typedef struct _gradient { + GRADIENT gradient; + char radial; + int rotate; +} gradient_t; + +typedef struct _texture { + FILLSTYLE fs; +} texture_t; + static void character_init(character_t*c) { memset(c, 0, sizeof(character_t)); @@ -253,6 +297,17 @@ static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r) swf_SetU16(tag, id); swf_SetString(tag, name); } +static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r) +{ + character_t* c = character_new(); + c->definingTag = ctag; + c->id = id; + c->size = r; + + if(dictionary_lookup(&images, name)) + syntaxerror("image %s defined twice", name); + dictionary_put2(&images, name, c); +} static instance_t* s_addinstance(char*name, character_t*c, U16 depth) { instance_t* i = instance_new(); @@ -278,7 +333,8 @@ static void parameters_clear(parameters_t*p) { p->x = 0; p->y = 0; p->scalex = 1.0; p->scaley = 1.0; - p->pin.x = 1; p->pin.y = 0; + p->pin.x = 0; //1?? + p->pin.y = 0; p->pivot.x = 0; p->pivot.y = 0; p->rotate = 0; p->shear = 0; @@ -305,18 +361,18 @@ static void makeMatrix(MATRIX*m, parameters_t*p) m->sy = (int)(sy*65536+0.5); m->tx = m->ty = 0; - + h = swf_TurnPoint(p->pin, m); m->tx = p->x - h.x; m->ty = p->y - h.y; } -static MATRIX s_instancepos(instance_t*i, parameters_t*p) +static MATRIX s_instancepos(SRECT rect, parameters_t*p) { MATRIX m; SRECT r; makeMatrix(&m, p); - r = swf_TurnRect(i->character->size, &m); + r = swf_TurnRect(rect, &m); if(currentrect.xmin == 0 && currentrect.ymin == 0 && currentrect.xmax == 0 && currentrect.ymax == 0) currentrect = r; @@ -325,10 +381,9 @@ static MATRIX s_instancepos(instance_t*i, parameters_t*p) return m; } -void s_swf(char*name, SRECT r, int version, int fps, int compress) +void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background) { SWF*swf = (SWF*)malloc(sizeof(SWF)); - RGBA rgb; if(stackpos) syntaxerror(".swf blocks can't be nested"); @@ -339,15 +394,19 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress) swf->frameRate = fps; swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR); swf->compressed = compress; - rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00; - swf_SetRGB(tag,&rgb); - + swf_SetRGB(tag,&background); + if(stackpos==sizeof(stack)/sizeof(stack[0])) syntaxerror("too many levels of recursion"); dictionary_init(&characters); + dictionary_init(&images); + dictionary_init(&textures); + dictionary_init(&outlines); + dictionary_init(&gradients); dictionary_init(&instances); dictionary_init(&fonts); + dictionary_init(&sounds); memset(&stack[stackpos], 0, sizeof(stack[0])); stack[stackpos].type = 0; @@ -391,9 +450,170 @@ void s_sprite(char*name) incrementid(); } +typedef struct _buttonrecord +{ + U16 id; + MATRIX matrix; + CXFORM cxform; + char set; +} buttonrecord_t; + +typedef struct _button +{ + int endofshapes; + int nr_actions; + buttonrecord_t records[4]; +} button_t; + +static button_t mybutton; + +void s_button(char*name) +{ + tag = swf_InsertTag(tag, ST_DEFINEBUTTON2); + swf_SetU16(tag, id); //id + swf_ButtonSetFlags(tag, 0); //menu=no + + memset(&mybutton, 0, sizeof(mybutton)); + + memset(&stack[stackpos], 0, sizeof(stack[0])); + stack[stackpos].type = 3; + stack[stackpos].tag = tag; + stack[stackpos].id = id; + stack[stackpos].name = strdup(name); + stack[stackpos].oldrect = currentrect; + memset(¤trect, 0, sizeof(currentrect)); + + stackpos++; + incrementid(); +} +void s_buttonput(char*character, char*as, parameters_t p) +{ + character_t* c = dictionary_lookup(&characters, character); + MATRIX m; + int flags = 0; + char*o = as,*s = as; + buttonrecord_t r; + if(!stackpos || (stack[stackpos-1].type != 3)) { + syntaxerror(".show may only appear in .button"); + } + if(!c) { + syntaxerror("character %s not known (in .shape %s)", character, character); + } + if(mybutton.endofshapes) { + syntaxerror("a .do may not precede a .show", character, character); + } + + m = s_instancepos(c->size, &p); + + r.id = c->id; + r.matrix = m; + r.cxform = p.cxform; + r.set = 1; + + while(1) { + if(*s==',' || *s==0) { + if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;} + else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;} + else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;} + else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;} + else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;} + else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o)); + } + if(!*s) + break; + s++; + } +} +static void setbuttonrecords(TAG*tag) +{ + int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT}; + if(!mybutton.endofshapes) { + int t; + + if(!mybutton.records[3].set) { + memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t)); + } + + for(t=0;t<4;t++) { + if(mybutton.records[t].set) { + swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform); + } + } + swf_SetU8(tag,0); // end of button records + mybutton.endofshapes = 1; + } +} + +void s_buttonaction(int flags, char*action) +{ + ActionTAG* a = 0; + if(flags==0) { + return; + } + setbuttonrecords(stack[stackpos-1].tag); + + a = swf_ActionCompile(text, stack[0].swf->fileVersion); + if(!a) { + syntaxerror("Couldn't compile ActionScript"); + } + + swf_ButtonSetCondition(stack[stackpos-1].tag, flags); + swf_ActionSet(stack[stackpos-1].tag, a); + mybutton.nr_actions++; + + swf_ActionFree(a); +} + +static void setactionend(TAG*tag) +{ + if(!mybutton.nr_actions) { + /* no actions means we didn't have an actionoffset, + which means we can't signal the end of the + buttonaction records, so, *sigh*, we have + to insert a dummy record */ + swf_SetU16(tag, 0); //offset + swf_SetU16(tag, 0); //condition + swf_SetU8(tag, 0); //action + } +} + +static void s_endButton() +{ + SRECT r; + setbuttonrecords(stack[stackpos-1].tag); + setactionend(stack[stackpos-1].tag); + stackpos--; + + swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions); + + r = currentrect; + + tag = stack[stackpos].tag; + currentrect = stack[stackpos].oldrect; + + s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r); + free(stack[stackpos].name); +} + +TAG* removeFromTo(TAG*from, TAG*to) +{ + TAG*save = from->prev; + while(from!=to) { + TAG*next = from->next; + swf_DeleteTag(from); + from = next; + } + save->next = 0; + return save; +} + static void s_endSprite() { SRECT r = currentrect; + + if(stack[stackpos].cut) + tag = removeFromTo(stack[stackpos].cut, tag); + stackpos--; /* TODO: before clearing, prepend "." to names and @@ -404,6 +624,8 @@ static void s_endSprite() currentrect = stack[stackpos].oldrect; currentdepth = stack[stackpos].olddepth; instances = stack[stackpos].oldinstances; + + tag = swf_InsertTag(tag, ST_SHOWFRAME); tag = swf_InsertTag(tag, ST_END); tag = stack[stackpos].tag; @@ -420,20 +642,38 @@ static void s_endSWF() int fi; SWF* swf; char*filename; - stackpos--; + + if(stack[stackpos].cut) + tag = removeFromTo(stack[stackpos].cut, tag); + stackpos--; + swf = stack[stackpos].swf; filename = stack[stackpos].filename; - + + //if(tag->prev && tag->prev->id != ST_SHOWFRAME) + // tag = swf_InsertTag(tag, ST_SHOWFRAME); tag = swf_InsertTag(tag, ST_SHOWFRAME); + tag = swf_InsertTag(tag, ST_END); swf_OptimizeTagOrder(swf); - - if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) + + if(optimize) { + swf_Optimize(swf); + } + + if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) { swf->movieSize = currentrect; /* "autocrop" */ - - fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); + } + + 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; + warning("Empty bounding box for movie"); + } + + fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644); if(fi<0) { syntaxerror("couldn't create output file %s", filename); } @@ -446,17 +686,23 @@ static void s_endSWF() dictionary_clear(&instances); dictionary_clear(&characters); + dictionary_clear(&images); + dictionary_clear(&textures); + dictionary_clear(&outlines); + dictionary_clear(&gradients); dictionary_clear(&fonts); + dictionary_clear(&sounds); swf_FreeTags(swf); free(swf); free(filename); } + void s_close() { if(stackpos) { if(stack[stackpos-1].type == 0) - syntaxerror("End of file encountered in .swf block"); + syntaxerror("End of file encountered in .flash block"); if(stack[stackpos-1].type == 1) syntaxerror("End of file encountered in .sprite block"); if(stack[stackpos-1].type == 2) @@ -466,34 +712,115 @@ void s_close() int s_getframe() { - return currentframe; + return currentframe+1; } -void s_frame(int nr) +void s_frame(int nr, int cut, char*name) { int t; + TAG*now = tag; + + if(nr<1) + syntaxerror("Illegal frame number"); + nr--; // internally, frame 1 is frame 0 + for(t=currentframe;tfs); + } else if((image = dictionary_lookup(&images, name))) { + MATRIX m; + swf_GetMatrix(0, &m); + m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax; + m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax; + m.tx = r->xmin; + m.ty = r->ymin; + return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0); + } else if ((gradient = dictionary_lookup(&gradients, name))) { + SRECT r2; + MATRIX rot,m; + double ccos,csin; + swf_GetMatrix(0, &rot); + ccos = cos(-gradient->rotate*2*3.14159265358979/360); + csin = sin(-gradient->rotate*2*3.14159265358979/360); + rot.sx = ccos*65536; + rot.r1 = -csin*65536; + rot.r0 = csin*65536; + rot.sy = ccos*65536; + r2 = swf_TurnRect(*r, &rot); + swf_GetMatrix(0, &m); + m.sx = (r2.xmax - r2.xmin)*2*ccos; + m.r1 = -(r2.xmax - r2.xmin)*2*csin; + m.r0 = (r2.ymax - r2.ymin)*2*csin; + m.sy = (r2.ymax - r2.ymin)*2*ccos; + m.tx = r->xmin + (r->xmax - r->xmin)/2; + m.ty = r->ymin + (r->ymax - r->ymin)/2; + return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial); + } else if (parseColor2(name, &color)) { + return swf_ShapeAddSolidFillStyle(s, &color); + } else { + syntaxerror("not a color/fillstyle: %s", name); + return 0; + } +} RGBA black={r:0,g:0,b:0,a:0}; -void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill) +void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture) { - SRECT r; + SRECT r,r2; SHAPE* s; - int ls1,fs1=0; - tag = swf_InsertTag(tag, ST_DEFINESHAPE); + int ls1=0,fs1=0; + r2.xmin = 0; + r2.ymin = 0; + r2.xmax = width; + r2.ymax = height; + tag = swf_InsertTag(tag, ST_DEFINESHAPE3); swf_ShapeNew(&s); - ls1 = swf_ShapeAddLineStyle(s,linewidth,&color); - if(dofill) - fs1 = swf_ShapeAddSolidFillStyle(s,&fill); + 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 = -linewidth-linewidth/2; - r.ymin = -linewidth-linewidth/2; - r.xmax = width+linewidth+linewidth/2; - r.ymax = height+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); @@ -508,21 +835,68 @@ void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fil incrementid(); } -void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill) +void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture) { - SRECT rect; + SRECT rect,r2; SHAPE* s; int ls1,fs1=0; - tag = swf_InsertTag(tag, ST_DEFINESHAPE); + outline_t* outline; + outline = dictionary_lookup(&outlines, outlinename); + if(!outline) { + syntaxerror("outline %s not defined", outlinename); + } + r2 = outline->bbox; + + tag = swf_InsertTag(tag, ST_DEFINESHAPE3); + swf_ShapeNew(&s); + 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/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, 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); + + s_addcharacter(name, id, tag, rect); + incrementid(); +} + +void s_circle(char*name, int r, RGBA color, int linewidth, char*texture) +{ + SRECT rect,r2; + SHAPE* s; + int ls1=0,fs1=0; + r2.xmin = r2.ymin = 0; + r2.xmax = 2*r; + r2.ymax = 2*r; + + tag = swf_InsertTag(tag, ST_DEFINESHAPE3); swf_ShapeNew(&s); - ls1 = swf_ShapeAddLineStyle(s,linewidth,&color); - if(dofill) - fs1 = swf_ShapeAddSolidFillStyle(s,&fill); + 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 = -linewidth-linewidth/2; - rect.ymin = -linewidth-linewidth/2; - rect.xmax = 2*r+linewidth+linewidth/2; - rect.ymax = 2*r+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); @@ -535,48 +909,48 @@ void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill incrementid(); } -void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill) +void s_textshape(char*name, char*fontname, float size, char*_text) { - SRECT rect; - SHAPE* s; - int ls1,fs1=0; int g; U8*text = (U8*)_text; + outline_t* outline; SWFFONT*font; font = dictionary_lookup(&fonts, fontname); if(!font) syntaxerror("font \"%s\" not known!", fontname); - if(!dofill) - syntaxerror("textshapes must be filled", fontname); - + if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) { warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname); - s_box(name, 0, 0, black, 20, black, 0); + s_box(name, 0, 0, black, 20, 0); return; } g = font->ascii2glyph[text[0]]; - rect = font->layout->bounds[g]; - swf_ShapeNew(&s); - ls1 = swf_ShapeAddLineStyle(s,linewidth,&color); - if(dofill) - fs1 = swf_ShapeAddSolidFillStyle(s,&fill); - - tag = swf_InsertTag(tag, ST_DEFINESHAPE); - swf_SetU16(tag,id); - swf_SetRect(tag, &rect); - swf_SetShapeStyles(tag, s); - swf_SetSimpleShape(tag, font->glyph[g].shape); - swf_ShapeFree(s); + outline = malloc(sizeof(outline_t)); + memset(outline, 0, sizeof(outline_t)); + outline->shape = font->glyph[g].shape; + outline->bbox = font->layout->bounds[g]; - s_addcharacter(name, id, tag, rect); - incrementid(); + { + drawer_t draw; + swf_Shape11DrawerInit(&draw, 0); + swf_DrawText(&draw, font, (int)(size*100), _text); + draw.finish(&draw); + outline->shape = swf_ShapeDrawerToShape(&draw); + outline->bbox = swf_ShapeDrawerGetBBox(&draw); + draw.dealloc(&draw); + } + + if(dictionary_lookup(&outlines, name)) + syntaxerror("outline %s defined twice", name); + dictionary_put2(&outlines, name, outline); } + void s_text(char*name, char*fontname, char*text, int size, RGBA color) { SRECT r; - + MATRIX _m,*m=0; SWFFONT*font; font = dictionary_lookup(&fonts, fontname); if(!font) @@ -585,7 +959,7 @@ void s_text(char*name, char*fontname, char*text, int size, RGBA color) tag = swf_InsertTag(tag, ST_DEFINETEXT2); swf_SetU16(tag, id); if(!font->numchars) { - s_box(name, 0, 0, black, 20, black, 0); + s_box(name, 0, 0, black, 20, 0); return; } r = swf_SetDefineText(tag, font, &color, text, size); @@ -594,62 +968,487 @@ void s_text(char*name, char*fontname, char*text, int size, RGBA color) incrementid(); } -void dumpSWF(SWF*swf) +void s_quicktime(char*name, char*url) +{ + SRECT r; + MATRIX _m,*m=0; + + memset(&r, 0, sizeof(r)); + + tag = swf_InsertTag(tag, ST_DEFINEMOVIE); + swf_SetU16(tag, id); + swf_SetString(tag, url); + + s_addcharacter(name, id, tag, r); + incrementid(); +} + +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; + SRECT r; + + if(fontname && *fontname) { + flags |= ET_USEOUTLINES; + font = dictionary_lookup(&fonts, fontname); + if(!font) + syntaxerror("font \"%s\" not known!", fontname); + } + tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT); + swf_SetU16(tag, id); + layout.align = align; + layout.leftmargin = 0; + layout.rightmargin = 0; + layout.indent = 0; + layout.leading = 0; + r.xmin = 0; + r.ymin = 0; + r.xmax = width; + r.ymax = height; + + swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable); + + s_addcharacter(name, id, tag, r); + incrementid(); +} + +/* type: either "jpeg" or "png" + */ +void s_image(char*name, char*type, char*filename, int quality) +{ + /* an image is actually two folded: 1st bitmap, 2nd character. + Both of them can be used separately */ + + /* step 1: the bitmap */ + SRECT r; + int imageID = id; + int width, height; + if(!strcmp(type,"jpeg")) { +#ifndef HAVE_LIBJPEG + warning("no jpeg support compiled in"); + s_box(name, 0, 0, black, 20, 0); + return; +#else + tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2); + swf_SetU16(tag, imageID); + + if(swf_SetJPEGBits(tag, filename, quality) < 0) { + syntaxerror("Image \"%s\" not found, or contains errors", filename); + } + + swf_GetJPEGSize(filename, &width, &height); + + r.xmin = 0; + r.ymin = 0; + r.xmax = width*20; + r.ymax = height*20; + + 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 */ + tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?) + swf_SetU16(tag, id); + swf_ShapeSetBitmapRect(tag, imageID, width, height); + + s_addcharacter(name, id, tag, r); + incrementid(); +} + +void s_getBitmapSize(char*name, int*width, int*height) +{ + character_t* image = dictionary_lookup(&images, name); + gradient_t* gradient = dictionary_lookup(&gradients,name); + if(image) { + *width = image->size.xmax; + *height = image->size.ymax; + return; + } + if(gradient) { + /* internal SWF gradient size */ + if(gradient->radial) { + *width = 16384; + *height = 16384; + } else { + *width = 32768; + *height = 32768; + } + return; + } + syntaxerror("No such bitmap/gradient: %s", name); +} + +void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear) +{ + gradient_t* gradient = dictionary_lookup(&gradients, object); + character_t* bitmap = dictionary_lookup(&images, object); + texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t)); + parameters_t p; + FILLSTYLE*fs = &texture->fs; + + if(bitmap) { + fs->type = FILL_TILED; + fs->id_bitmap = bitmap->id; + } else if(gradient) { + fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR; + fs->gradient = gradient->gradient; + } + p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear; + makeMatrix(&fs->m, &p); + if(gradient && !gradient->radial) { + MATRIX m = fs->m; + SPOINT p1,p2; + m.tx = 0; + m.ty = 0; + p1.x = 16384; + p1.y = 16384; + p2 = swf_TurnPoint(p1, &m); + fs->m.tx += p2.x; + fs->m.ty += p2.y; + } + + if(dictionary_lookup(&textures, name)) + syntaxerror("texture %s defined twice", name); + dictionary_put2(&textures, name, texture); +} + +void dumpSWF(SWF*swf) +{ + TAG* tag = swf->firstTag; + printf("vvvvvvvvvvvvvvvvvvvvv\n"); + while(tag) { + printf("%8d %s\n", tag->len, swf_TagGetName(tag)); + tag = tag->next; + } + printf("^^^^^^^^^^^^^^^^^^^^^\n"); +} + +void s_font(char*name, char*filename) +{ + SWFFONT* font; + font = swf_LoadFont(filename); + + if(font == 0) { + warning("Couldn't open font file \"%s\"", filename); + font = (SWFFONT*)malloc(sizeof(SWFFONT)); + memset(font, 0, sizeof(SWFFONT)); + dictionary_put2(&fonts, name, font); + return; + } + + if(0) + { + /* fix the layout. Only needed for old fonts */ + int t; + for(t=0;tnumchars;t++) { + font->glyph[t].advance = 0; + } + font->layout = 0; + swf_FontCreateLayout(font); + } + /* just in case this thing is used in .edittext later on */ + swf_FontPrepareForEditText(font); + + 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)) + syntaxerror("font %s defined twice", name); + 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; + struct MP3 mp3; + sound_t* sound; + U16*samples = NULL; + unsigned numsamples; + unsigned blocksize = 1152; + int is_mp3 = 0; + + if(wav_read(filename, &wav)) { + int t; + wav_convert2mono(&wav, &wav2, 44100); + samples = (U16*)wav2.data; + numsamples = wav2.size/2; + free(wav.data); +#ifdef WORDS_BIGENDIAN + /* swap bytes */ + for(t=0;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 + 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); + } + + 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); +} + +static char* gradient_getToken(const char**p) +{ + const char*start; + char*result; + while(**p && strchr(" \t\n\r", **p)) { + (*p)++; + } + start = *p; + while(**p && !strchr(" \t\n\r", **p)) { + (*p)++; + } + result = malloc((*p)-start+1); + memcpy(result,start,(*p)-start+1); + result[(*p)-start] = 0; + return result; +} + +float parsePercent(char*str); +RGBA parseColor(char*str); + +GRADIENT parseGradient(const char*str) +{ + GRADIENT gradient; + int lastpos = -1; + const char* p = str; + memset(&gradient, 0, sizeof(GRADIENT)); + while(*p) { + char*posstr,*colorstr; + int pos; + RGBA color; + posstr = gradient_getToken(&p); + if(!*posstr) + break; + pos = (int)(parsePercent(posstr)*255.0); + if(pos == lastpos) + pos++; + if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr); + colorstr = gradient_getToken(&p); + color = parseColor(colorstr); + if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) { + warning("gradient record too big- max size is 8, rest ignored"); + break; + } + gradient.ratios[gradient.num] = pos; + gradient.rgba[gradient.num] = color; + gradient.num++; + free(posstr); + free(colorstr); + lastpos = pos; + } + return gradient; +} + +void s_gradient(char*name, const char*text, int radial, int rotate) +{ + gradient_t* gradient; + gradient = malloc(sizeof(gradient_t)); + memset(gradient, 0, sizeof(gradient_t)); + gradient->gradient = parseGradient(text); + gradient->radial = radial; + gradient->rotate = rotate; + + if(dictionary_lookup(&gradients, name)) + syntaxerror("gradient %s defined twice", name); + dictionary_put2(&gradients, name, gradient); +} + +void s_action(const char*text) +{ + ActionTAG* a = 0; + a = swf_ActionCompile(text, stack[0].swf->fileVersion); + if(!a) { + syntaxerror("Couldn't compile ActionScript"); + } + + tag = swf_InsertTag(tag, ST_DOACTION); + + swf_ActionSet(tag, a); + + swf_ActionFree(a); +} + +void s_initaction(const char*character, const char*text) { - TAG* tag = swf->firstTag; - printf("vvvvvvvvvvvvvvvvvvvvv\n"); - while(tag) { - printf("%8d %s\n", tag->len, swf_TagGetName(tag)); - tag = tag->next; + ActionTAG* a = 0; + character_t*c = 0; + a = swf_ActionCompile(text, stack[0].swf->fileVersion); + if(!a) { + syntaxerror("Couldn't compile ActionScript"); } - printf("^^^^^^^^^^^^^^^^^^^^^\n"); + + c = (character_t*)dictionary_lookup(&characters, character); + + tag = swf_InsertTag(tag, ST_DOINITACTION); + swf_SetU16(tag, c->id); + swf_ActionSet(tag, a); + + swf_ActionFree(a); } - -void s_font(char*name, char*filename) + +int s_swf3action(char*name, char*action) { - int f; - SWF swf; - SWFFONT* font; - f = open(filename,O_RDONLY); - if (f<0) { - warning("Couldn't open file \"%s\": %s", filename, strerror(errno)); - font = (SWFFONT*)malloc(sizeof(SWFFONT)); - memset(font, 0, sizeof(SWFFONT)); - dictionary_put2(&fonts, name, font); - return; - } - font = 0; - if (swf_ReadSWF(f,&swf)>=0) { - swf_FontExtract(&swf, 0x4e46, &font); - swf_FreeTags(&swf); - } - close(f); - if (font==0) { - syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename); + ActionTAG* a = 0; + instance_t* object = 0; + if(name) + dictionary_lookup(&instances, name); + if(!object && name && *name) { + /* we have a name, but couldn't find it. Abort. */ + return 0; } + a = action_SetTarget(0, name); + if(!strcmp(action, "nextframe")) a = action_NextFrame(a); + else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a); + else if(!strcmp(action, "stop")) a = action_Stop(a); + else if(!strcmp(action, "play")) a = action_Play(a); + a = action_SetTarget(a, ""); + a = action_End(a); - if(0) - { - /* fix the layout. Only needed for old fonts */ - int t; - for(t=0;tnumchars;t++) { - font->glyph[t].advance = 0; - } - font->layout = 0; - swf_FontCreateLayout(font); - } + tag = swf_InsertTag(tag, ST_DOACTION); + swf_ActionSet(tag, a); + swf_ActionFree(a); + return 1; +} - font->id = id; - tag = swf_InsertTag(tag, ST_DEFINEFONT2); - swf_FontSetDefine2(tag, font); - incrementid(); +void s_outline(char*name, char*format, char*source) +{ + outline_t* outline; - if(dictionary_lookup(&fonts, name)) - syntaxerror("font %s defined twice", name); - dictionary_put2(&fonts, name, font); + drawer_t draw; + SHAPE* shape; + SHAPE2* shape2; + SRECT bounds; + + //swf_Shape10DrawerInit(&draw, 0); + swf_Shape11DrawerInit(&draw, 0); + + draw_string(&draw, source); + draw.finish(&draw); + shape = swf_ShapeDrawerToShape(&draw); + bounds = swf_ShapeDrawerGetBBox(&draw); + draw.dealloc(&draw); + + outline = (outline_t*)rfx_calloc(sizeof(outline_t)); + outline->shape = shape; + outline->bbox = bounds; + + if(dictionary_lookup(&outlines, name)) + syntaxerror("outline %s defined twice", name); + dictionary_put2(&outlines, name, outline); +} + +int s_playsound(char*name, int loops, int nomultiple, int stop) +{ + sound_t* sound; + SOUNDINFO info; + if(!name) + return 0; + sound = dictionary_lookup(&sounds, name); + if(!sound) + return 0; + + 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); + return 1; } -void s_shape(char*name, char*filename) +void s_includeswf(char*name, char*filename) { int f; SWF swf; @@ -658,15 +1457,15 @@ void s_shape(char*name, char*filename) TAG* s; int level = 0; U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX}; - 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)); - s_box(name, 0, 0, black, 20, black, 0); + s_box(name, 0, 0, black, 20, 0); return; } if (swf_ReadSWF(f,&swf)<0) { warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename); - s_box(name, 0, 0, black, 20, black, 0); + s_box(name, 0, 0, black, 20, 0); return; } close(f); @@ -680,7 +1479,7 @@ void s_shape(char*name, char*filename) s = tag = swf_InsertTag(tag, ST_DEFINESPRITE); swf_SetU16(tag, id); - swf_SetU16(tag, 0); + swf_SetU16(tag, swf.frameCount); swf_Relocate(&swf, idmap); @@ -748,7 +1547,7 @@ void s_startclip(char*instance, char*character, parameters_t p) } i = s_addinstance(instance, c, currentdepth); i->parameters = p; - m = s_instancepos(i, &p); + m = s_instancepos(i->character->size, &p); tag = swf_InsertTag(tag, ST_PLACEOBJECT2); /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */ @@ -769,6 +1568,7 @@ void s_endClip() swf_SetTagPos(stack[stackpos].tag, 0); swf_GetPlaceObject(stack[stackpos].tag, &p); p.clipdepth = currentdepth; + p.name = 0; swf_ClearTag(stack[stackpos].tag); swf_SetPlaceObject(stack[stackpos].tag, &p); currentdepth++; @@ -785,7 +1585,7 @@ void s_put(char*instance, char*character, parameters_t p) i = s_addinstance(instance, c, currentdepth); i->parameters = p; - m = s_instancepos(i, &p); + m = s_instancepos(i->character->size, &p); tag = swf_InsertTag(tag, ST_PLACEOBJECT2); swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance); @@ -803,7 +1603,7 @@ void s_jump(char*instance, parameters_t p) } i->parameters = p; - m = s_instancepos(i, &p); + m = s_instancepos(i->character->size, &p); tag = swf_InsertTag(tag, ST_PLACEOBJECT2); swf_ObjectMove(tag, i->depth, &m, &p.cxform); @@ -861,7 +1661,7 @@ void s_change(char*instance, parameters_t p2) return; } - m = s_instancepos(i, &p2); + m = s_instancepos(i->character->size, &p2); tag = swf_InsertTag(tag, ST_PLACEOBJECT2); swf_ObjectMove(tag, i->depth, &m, &p2.cxform); i->parameters = p2; @@ -879,7 +1679,7 @@ void s_change(char*instance, parameters_t p2) TAG*lt; frame ++; p = s_interpolate(&p1, &p2, frame, allframes); - m = s_instancepos(i, &p); //needed? + m = s_instancepos(i->character->size, &p); //needed? lt = swf_InsertTag(t, ST_PLACEOBJECT2); i->lastFrame = currentframe; swf_ObjectMove(lt, i->depth, &m, &p.cxform); @@ -918,6 +1718,8 @@ void s_end() s_endSprite(); else if(stack[stackpos-1].type == 2) s_endClip(); + else if(stack[stackpos-1].type == 3) + s_endButton(); else syntaxerror("internal error 1"); } @@ -984,11 +1786,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; @@ -999,17 +1808,17 @@ int parseTwip(char*str) if(*s<'0' || *s>'9') syntaxerror("Not a coordinate: \"%s\"", str); } - if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) { - warning("precision loss: %s converted to twip", str); + if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) { + warning("precision loss: %s converted to twip: %s", str, dot); dot[2] = 0; 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; } @@ -1042,10 +1851,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", "magenta", "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; @@ -1054,14 +1876,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;tr = r; color->g = g; color->b = b; color->a = a; return 1; } @@ -1124,6 +1944,19 @@ MULADD mergeMulAdd(MULADD m1, MULADD m2) return r; } +float parsePxOrPercent(char*fontname, char*str) +{ + int l = strlen(str); + if(strchr(str, '%')) + return parsePercent(str); + if(l>2 && str[l-2]=='p' && str[l-1]=='t') { + float p = atof(str); + return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */ + } + syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str); + return 0; +} + float parsePercent(char*str) { int l = strlen(str); @@ -1157,21 +1990,35 @@ 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; } -static int c_swf(map_t*args) +static int c_flash(map_t*args) { - char* name = lu(args, "name"); + char* filename = map_lookup(args, "filename"); char* compressstr = lu(args, "compress"); SRECT bbox = parseBox(lu(args, "bbox")); int version = parseInt(lu(args, "version")); int fps = (int)(parseFloat(lu(args, "fps"))*256); int compress = 0; - if(!strcmp(name, "!default!") || override_outputname) - name = outputname; + RGBA color = parseColor(lu(args, "background")); + + if(!filename || !*filename) { + /* for compatibility */ + filename = map_lookup(args, "name"); + if(!filename || !*filename) { + filename = 0; + } else { + //msg(" line %d: .flash name=... is deprecated, use .flash filename=...", line); + msg(" line %d: .flash name=... is deprecated, use .flash filename=...", line); + } + } + + if(!filename || override_outputname) + filename = outputname; if(!strcmp(compressstr, "default")) compress = version==6; @@ -1181,7 +2028,7 @@ static int c_swf(map_t*args) compress = 0; else syntaxerror("value \"%s\" not supported for the compress argument", compressstr); - s_swf(name, bbox, version, fps, compress); + s_swf(filename, bbox, version, fps, compress, color); return 0; } int isRelative(char*str) @@ -1213,7 +2060,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; @@ -1221,13 +2068,104 @@ 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 texture2(char*name, char*object, map_t*args, int errors) +{ + SPOINT pos,size; + char*xstr = map_lookup(args, "x"); + char*ystr = map_lookup(args, "y"); + char*widthstr = map_lookup(args, "width"); + char*heightstr = map_lookup(args, "height"); + char*scalestr = map_lookup(args, "scale"); + char*scalexstr = map_lookup(args, "scalex"); + char*scaleystr = map_lookup(args, "scaley"); + char*rotatestr = map_lookup(args, "rotate"); + char* shearstr = map_lookup(args, "shear"); + char* radiusstr = map_lookup(args, "r"); + float x=0,y=0; + float scalex = 1.0, scaley = 1.0; + float rotate=0, shear=0; + float r = 0; + if(!*xstr && !*ystr) { + if(errors) + syntaxerror("x and y must be set"); + return 0; + } + if(*scalestr && (*scalexstr || *scaleystr)) { + syntaxerror("scale and scalex/scaley can't both be set"); + return 0; + } + if((*widthstr || *heightstr) && *radiusstr) { + syntaxerror("width/height and radius can't both be set"); + } + if(*radiusstr) { + widthstr = radiusstr; + heightstr = radiusstr; + } + if(!*xstr) xstr="0"; + if(!*ystr) ystr="0"; + if(!*rotatestr) rotatestr="0"; + if(!*shearstr) shearstr="0"; + + if(*scalestr) { + scalex = scaley = parsePercent(scalestr); + } else if(*scalexstr || *scaleystr) { + if(scalexstr) scalex = parsePercent(scalexstr); + if(scaleystr) scaley = parsePercent(scaleystr); + } else if(*widthstr || *heightstr) { + int width=0; + int height=0; + s_getBitmapSize(object, &width, &height); + if(*widthstr) + scalex = (float)parseTwip(widthstr)/(float)width; + if(*heightstr) + scaley = (float)parseTwip(heightstr)/(float)height; + } + x = parseTwip(xstr); + y = parseTwip(ystr); + rotate = parseFloat(rotatestr); + shear = parseFloat(shearstr); + + s_texture(name, object, x,y,scalex,scaley,rotate, shear); + + return 0; +} + +static int c_texture(map_t*args) +{ + char*name = lu(args, "instance"); + char*object = lu(args, "character"); + return texture2(name, object, args, 1); +} + +static int c_gradient(map_t*args) +{ + char*name = lu(args, "name"); + int radial= strcmp(lu(args, "radial"), "radial")?0:1; + int rotate = parseInt(lu(args, "rotate")); + + readToken(); + if(type != RAWDATA) + syntaxerror("colon (:) expected"); + + s_gradient(name, text, radial, rotate); + + /* check whether we also have placement information, + which would make this a positioned gradient. + If there is placement information, texture2() will + add a texture, which has priority over the gradient. + */ + texture2(name, name, args, 0); + return 0; +} static int c_point(map_t*args) { char*name = lu(args, "name"); @@ -1247,6 +2185,60 @@ 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, "name"); + char*loop = lu(args, "loop"); + char*nomultiple = lu(args, "nomultiple"); + int nm = 0; + if(!strcmp(nomultiple, "nomultiple")) + nm = 1; + else + nm = parseInt(nomultiple); + + if(s_playsound(name, parseInt(loop), nm, 0)) { + return 0; + } else if(s_swf3action(name, "play")) { + return 0; + } + return 0; +} + +static int c_stop(map_t*args) +{ + char*name = map_lookup(args, "name"); + + if(s_playsound(name, 0,0,1)) { + return 0; + } else if(s_swf3action(name, "stop")) { + return 0; + } + syntaxerror("I don't know anything about sound/movie \"%s\"", name); + return 0; +} + +static int c_nextframe(map_t*args) +{ + char*name = lu(args, "name"); + + if(s_swf3action(name, "nextframe")) { + return 0; + } + syntaxerror("I don't know anything about movie \"%s\"", name); + return 0; +} + +static int c_previousframe(map_t*args) +{ + char*name = lu(args, "name"); + + if(s_swf3action(name, "previousframe")) { + return 0; + } + syntaxerror("I don't know anything about movie \"%s\"", name); + return 0; +} + static int c_placement(map_t*args, int type) { char*instance = lu(args, (type==0||type==4)?"instance":"name"); @@ -1267,6 +2259,7 @@ static int c_placement(map_t*args, int type) char* bstr = lu(args, "blue"); char* astr = lu(args, "alpha"); char* pinstr = lu(args, "pin"); + char* as = map_lookup(args, "as"); MULADD r,g,b,a; float oldwidth; float oldheight; @@ -1274,7 +2267,7 @@ static int c_placement(map_t*args, int type) MULADD luminance; parameters_t p; - if(type==5) { + if(type==9) { // (?) .rotate or .arcchange pivotstr = lu(args, "pivot"); anglestr = lu(args, "angle"); } else { @@ -1298,6 +2291,10 @@ static int c_placement(map_t*args, int type) // put or startclip character = lu(args, "character"); parameters_clear(&p); + } else if (type == 5) { + character = lu(args, "name"); + parameters_clear(&p); + // button's show } else { p = s_getParameters(instance); } @@ -1418,14 +2415,21 @@ static int c_placement(map_t*args, int type) if(type == 0) s_put(instance, character, p); - if(type == 1) + else if(type == 1) s_change(instance, p); - if(type == 2) + else if(type == 2) s_qchange(instance, p); - if(type == 3) + else if(type == 3) s_jump(instance, p); - if(type == 4) + else if(type == 4) s_startclip(instance, character, p); + else if(type == 5) { + if(as && as[0]) { + s_buttonput(character, as, p); + } else { + s_buttonput(character, "shape", p); + } + } return 0; } static int c_put(map_t*args) @@ -1458,6 +2462,11 @@ static int c_startclip(map_t*args) c_placement(args, 4); return 0; } +static int c_show(map_t*args) +{ + c_placement(args, 5); + return 0; +} static int c_del(map_t*args) { char*instance = lu(args, "name"); @@ -1478,7 +2487,12 @@ static int c_sprite(map_t*args) static int c_frame(map_t*args) { char*framestr = lu(args, "n"); + char*cutstr = lu(args, "cut"); + char*name = lu(args, "name"); int frame; + int cut = 0; + if(strcmp(cutstr, "no")) + cut = 1; if(isRelative(framestr)) { frame = s_getframe(); if(getSign(framestr)<0) @@ -1488,10 +2502,10 @@ static int c_frame(map_t*args) else { frame = parseInt(framestr); if(s_getframe() >= frame - && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0 + && !(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); + s_frame(frame, cut, name); return 0; } static int c_primitive(map_t*args) @@ -1507,10 +2521,11 @@ static int c_primitive(map_t*args) int type=0; char* font; char* text; + char* outline=0; RGBA fill; if(!strcmp(command, "circle")) type = 1; - else if(!strcmp(command, "textshape")) + else if(!strcmp(command, "filled")) type = 2; if(type==0) { @@ -1519,36 +2534,41 @@ static int c_primitive(map_t*args) } else if (type==1) { r = parseTwip(lu(args, "r")); } else if (type==2) { - text = lu(args, "text"); - font = lu(args, "font"); + outline = lu(args, "outline"); } if(!strcmp(fillstr, "fill")) fillstr = colorstr; if(!strcmp(fillstr, "none")) - dofill = 0; + fillstr = 0; if(width<0 || height<0 || linewidth<0 || r<0) syntaxerror("values width, height, line, r must be positive"); - if(!dofill || isColor(fillstr)) { - if(dofill) - fill = parseColor(fillstr); - } else { - /* FIXME - texture fill */ - fill.r = fill.g = 0; - fill.b = fill.a = 255; - warning("texture fill not supported yet. Filling with black."); - } - if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill); - else if(type==1) s_circle(name, r, color, linewidth, fill, dofill); - else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill); + + if(type == 0) s_box(name, width, height, color, linewidth, fillstr); + else if(type==1) s_circle(name, r, color, linewidth, fillstr); + else if(type==2) s_filled(name, outline, color, linewidth, fillstr); + return 0; +} + +static int c_textshape(map_t*args) +{ + char*name = lu(args, "name"); + char*text = lu(args, "text"); + char*font = lu(args, "font"); + float size = parsePxOrPercent(font, lu(args, "size")); + + s_textshape(name, font, size, text); return 0; } -static int c_shape(map_t*args) +static int c_swf(map_t*args) { char*name = lu(args, "name"); char*filename = lu(args, "filename"); - s_shape(name, filename); + char*command = lu(args, "commandname"); + if(!strcmp(command, "shape")) + warning("Please use .swf instead of .shape"); + s_includeswf(name, filename); return 0; } @@ -1560,93 +2580,354 @@ 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"); char*text = lu(args, "text"); char*font = lu(args, "font"); - float size = parsePercent(lu(args, "size")); + float size = parsePxOrPercent(font, lu(args, "size")); RGBA color = parseColor(lu(args, "color")); s_text(name, font, text, (int)(size*100), color); return 0; } +static int c_soundtrack(map_t*args) +{ + return 0; +} + +static int c_quicktime(map_t*args) +{ + char*name = lu(args, "name"); + char*url = lu(args, "url"); + s_quicktime(name, url); + return 0; +} + +static int c_image(map_t*args) +{ + char*command = lu(args, "commandname"); + char*name = lu(args, "name"); + char*filename = lu(args, "filename"); + if(!strcmp(command,"jpeg")) { + int quality = (int)(parsePercent(lu(args, "quality"))*100); + s_image(name, "jpeg", filename, quality); + } else { + s_image(name, "png", filename, 0); + } + return 0; +} + +static int c_outline(map_t*args) +{ + char*name = lu(args, "name"); + char*format = lu(args, "format"); + + readToken(); + if(type != RAWDATA) + syntaxerror("colon (:) expected"); + + s_outline(name, format, text); + return 0; +} + int fakechar(map_t*args) { char*name = lu(args, "name"); - s_box(name, 0, 0, black, 20, black, 0); + s_box(name, 0, 0, black, 20, 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);} -static int c_edittext(map_t*args) {return fakechar(args);} +static int c_button(map_t*args) { + char*name = lu(args, "name"); + s_button(name); + return 0; +} +static int current_button_flags = 0; +static int c_on_press(map_t*args) +{ + char*position = lu(args, "position"); + char*action = ""; + if(!strcmp(position, "inside")) { + current_button_flags |= BC_OVERUP_OVERDOWN; + } else if(!strcmp(position, "outside")) { + //current_button_flags |= BC_IDLE_OUTDOWN; + syntaxerror("IDLE_OVERDOWN not supported by SWF"); + } else if(!strcmp(position, "anywhere")) { + current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN; + } + readToken(); + if(type == RAWDATA) { + action = text; + s_buttonaction(current_button_flags, action); + current_button_flags = 0; + } + else + pushBack(); + return 0; +} +static int c_on_release(map_t*args) +{ + char*position = lu(args, "position"); + char*action = ""; + if(!strcmp(position, "inside")) { + current_button_flags |= BC_OVERDOWN_OVERUP; + } else if(!strcmp(position, "outside")) { + current_button_flags |= BC_OUTDOWN_IDLE; + } else if(!strcmp(position, "anywhere")) { + current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE; + } + readToken(); + if(type == RAWDATA) { + action = text; + s_buttonaction(current_button_flags, action); + current_button_flags = 0; + } + else + pushBack(); + return 0; +} +static int c_on_move_in(map_t*args) +{ + char*position = lu(args, "state"); + char*action = ""; + if(!strcmp(position, "pressed")) { + current_button_flags |= BC_OUTDOWN_OVERDOWN; + } else if(!strcmp(position, "not_pressed")) { + current_button_flags |= BC_IDLE_OVERUP; + } else if(!strcmp(position, "any")) { + current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN; + } + readToken(); + if(type == RAWDATA) { + action = text; + s_buttonaction(current_button_flags, action); + current_button_flags = 0; + } + else + pushBack(); + return 0; +} +static int c_on_move_out(map_t*args) +{ + char*position = lu(args, "state"); + char*action = ""; + if(!strcmp(position, "pressed")) { + current_button_flags |= BC_OVERDOWN_OUTDOWN; + } else if(!strcmp(position, "not_pressed")) { + current_button_flags |= BC_OVERUP_IDLE; + } else if(!strcmp(position, "any")) { + current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE; + } + readToken(); + if(type == RAWDATA) { + action = text; + s_buttonaction(current_button_flags, action); + current_button_flags = 0; + } + else + pushBack(); + return 0; +} +static int c_on_key(map_t*args) +{ + char*key = lu(args, "key"); + char*action = ""; + if(strlen(key)==1) { + /* ascii */ + if(key[0]>=32) { + current_button_flags |= 0x4000 + (key[0]*0x200); + } else { + syntaxerror("invalid character: %c"+key[0]); + return 1; + } + } else { + /* TODO: + = 0x200*(x-'a') + esc = = 0x3600 + space = = 0x4000; + */ + syntaxerror("invalid key: %s",key); + } + readToken(); + if(type == RAWDATA) { + action = text; + s_buttonaction(current_button_flags, action); + current_button_flags = 0; + } + else + pushBack(); + return 0; +} + +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 @autosize=0"}, + char*name = lu(args, "name"); + char*font = lu(args, "font"); + int size = (int)(1024*parsePxOrPercent(font, lu(args, "size"))); + int width = parseTwip(lu(args, "width")); + int height = parseTwip(lu(args, "height")); + char*text = lu(args, "text"); + RGBA color = parseColor(lu(args, "color")); + int maxlength = parseInt(lu(args, "maxlength")); + char*variable = lu(args, "variable"); + char*passwordstr = lu(args, "password"); + char*wordwrapstr = lu(args, "wordwrap"); + char*multilinestr = lu(args, "multiline"); + char*htmlstr = lu(args, "html"); + 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; + if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP; + if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE; + if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY; + if(!strcmp(htmlstr, "html")) flags |= ET_HTML; + if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT; + if(!strcmp(borderstr, "border")) flags |= ET_BORDER; + 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; +} 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 char* readfile(const char*filename) +{ + FILE*fi = fopen(filename, "rb"); + int l; + char*text; + if(!fi) + syntaxerror("Couldn't find file %s: %s", filename, strerror(errno)); + fseek(fi, 0, SEEK_END); + l = ftell(fi); + fseek(fi, 0, SEEK_SET); + text = rfx_alloc(l+1); + fread(text, l, 1, fi); + text[l]=0; + fclose(fi); + return text; +} + +static int c_action(map_t*args) +{ + char* filename = map_lookup(args, "filename"); + if(!filename ||!*filename) { + readToken(); + if(type != RAWDATA) { + syntaxerror("colon (:) expected"); + } + s_action(text); + } else { + s_action(readfile(filename)); + } + + 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;} -static int c_action(map_t*args) {return 0;} +static int c_initaction(map_t*args) +{ + char* character = lu(args, "name"); + char* filename = map_lookup(args, "filename"); + if(!filename ||!*filename) { + readToken(); + if(type != RAWDATA) { + syntaxerror("colon (:) expected"); + } + s_initaction(character, text); + } else { + s_initaction(character, readfile(filename)); + } + + return 0; +} static struct { char*command; command_func_t* func; char*arguments; } arguments[] = -{{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"}, - {"frame", c_frame, "n=1"}, - - // "import" type stuff - {"shape", c_shape, "name filename"}, - {"morphshape", c_morphshape, "name start end"}, +{{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"}, + {"frame", c_frame, "n=1 name= @cut=no"}, + // "import" type stuff + {"swf", c_swf, "name filename"}, + {"shape", c_swf, "name filename"}, {"jpeg", c_image, "name filename quality=80%"}, {"png", c_image, "name filename"}, {"movie", c_movie, "name filename"}, {"sound", c_sound, "name filename"}, {"font", c_font, "name filename"}, {"soundtrack", c_soundtrack, "filename"}, + {"quicktime", c_quicktime, "url"}, + + // generators of primitives + + {"point", c_point, "name x=0 y=0"}, + {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture + {"outline", c_outline, "name format=simple"}, + {"textshape", c_textshape, "name font size=100% text"}, // 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"}, - {"textshape", c_primitive, "name text font color=white line=1 @fill=none"}, + {"filled", c_primitive, "name outline color=white line=1 @fill=none"}, + {"egon", c_egon, "name vertices color=white line=1 @fill=none"}, - {"button", c_button, "name shape over=*shape press=*shape area=*shape"}, {"text", c_text, "name text font size=100% color=white"}, - {"edittext", c_edittext, "name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=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="}, + {"on_press", c_on_press, "position=inside"}, + {"on_release", c_on_release, "position=anywhere"}, + {"on_move_in", c_on_move_in, "state=not_pressed"}, + {"on_move_out", c_on_move_out, "state=not_pressed"}, + {"on_key", c_on_key, "key=any"}, - {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"}, - // control tags - {"play", c_play, "sound loop=0 @nomultiple=0"}, - {"stop", c_stop, "sound"}, + {"play", c_play, "name loop=0 @nomultiple=0"}, + {"stop", c_stop, "name= "}, + {"nextframe", c_nextframe, "name"}, + {"previousframe", c_previousframe, "name"}, // object placement tags - {"put", c_put, " 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, " 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="}, + {"put", c_put, " x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="}, + {"startclip", c_startclip, " x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="}, + {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="}, + {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="}, + {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="}, + {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="}, {"del", c_del, "name"}, // virtual object placement - {"buttonput", c_buttonput, " 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, " x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"}, - {"point", c_point, "name x=0 y=0"}, + {"texture", c_texture, " x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="}, // commands which start a block //startclip (see above) {"sprite", c_sprite, "name"}, - {"action", c_action, ""}, + {"action", c_action, "filename="}, + {"initaction", c_initaction, "name filename="}, {"end", c_end, ""} }; @@ -1681,7 +2962,7 @@ static map_t parseArguments(char*command, char*pattern) if(!strncmp(" ", x, 3)) { readToken(); - if(type == COMMAND || type == LABEL) { + if(type == COMMAND || type == RAWDATA) { pushBack(); syntaxerror("character name expected"); } @@ -1735,13 +3016,13 @@ static map_t parseArguments(char*command, char*pattern) len = pos; /* for(t=0;ttextlen?name[pos].len:textlen)) || (type != ASSIGNMENT && !set[pos])) { if(set[pos]) { - syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len)); + syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len)); } if(type == ASSIGNMENT) readToken(); @@ -1778,19 +3059,19 @@ static map_t parseArguments(char*command, char*pattern) value[pos].len = strlen(text); #if 0//def DEBUG printf("setting parameter %s (to %s)\n", - strndup(name[pos].str, name[pos].len), - strndup(value[pos].str, value[pos].len)); + strdup_n(name[pos].str, name[pos].len), + strdup_n(value[pos].str, value[pos].len)); #endif break; } } if(pos==len) { - syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command); + syntaxerror("Illegal argument \"%s\" to .%s", text, command); } } #if 0//def DEBUG for(t=0;t parse Command: %s (line %d)", command, line); + for(t=0;t