2 Compiles swf code (.sc) files into .swf files.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/drawer.h"
32 #include "../lib/log.h"
33 #include "../lib/args.h"
35 #include "../lib/mp3.h"
36 #include "../lib/wav.h"
38 #include "../lib/png.h"
42 static char * filename = 0;
43 static char * outputname = "output.swf";
44 static int verbose = 2;
45 static int optimize = 0;
46 static int override_outputname = 0;
47 static int do_cgi = 0;
49 static struct options_t options[] = {
57 int args_callback_option(char*name,char*val)
59 if(!strcmp(name, "V")) {
60 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
63 else if(!strcmp(name, "o")) {
65 override_outputname = 1;
68 else if(!strcmp(name, "O")) {
72 else if(!strcmp(name, "C")) {
76 else if(!strcmp(name, "v")) {
81 printf("Unknown option: -%s\n", name);
86 int args_callback_longoption(char*name,char*val)
88 return args_long2shortoption(options, name, val);
90 void args_callback_usage(char *name)
93 printf("Usage: %s [-o file.swf] file.sc\n", name);
95 printf("-h , --help Print short help message and exit\n");
96 printf("-V , --version Print version info and exit\n");
97 printf("-v , --verbose Increase verbosity. \n");
98 printf("-o , --output <filename> Set output file to <filename>.\n");
101 int args_callback_command(char*name,char*val)
104 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
111 static struct token_t* file;
120 static void syntaxerror(char*format, ...)
124 va_start(arglist, format);
125 vsprintf(buf, format, arglist);
127 fprintf(stderr, "\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
131 static void warning(char*format, ...)
135 va_start(arglist, format);
136 vsprintf(buf, format, arglist);
138 fprintf(stderr, "\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
141 static void readToken()
143 type = file[pos].type;
145 syntaxerror("unexpected end of file");
147 text = file[pos].text;
148 textlen = strlen(text);
149 line = file[pos].line;
150 column = file[pos].column;
152 //printf("---> %d(%s) %s\n", type, type_names[type], text);
155 static void pushBack()
158 if(!pos) syntaxerror("internal error 3");
163 textlen = strlen(text);
166 column = file[p].column;
169 static int noMoreTokens()
171 if(file[pos].type == END)
176 // ------------------------------ swf routines ----------------------------
180 int type; //0=swf, 1=sprite, 2=clip, 3=button
186 /* for sprites (1): */
192 dictionary_t oldinstances;
197 static int stackpos = 0;
199 static dictionary_t characters;
200 static dictionary_t images;
201 static dictionary_t textures;
202 static dictionary_t outlines;
203 static dictionary_t gradients;
204 static char idmap[65536];
205 static TAG*tag = 0; //current tag
207 static int id; //current character id
208 static int currentframe; //current frame in current level
209 static SRECT currentrect; //current bounding box in current level
210 static U16 currentdepth;
211 static dictionary_t instances;
212 static dictionary_t fonts;
213 static dictionary_t sounds;
215 typedef struct _parameters {
217 float scalex, scaley;
225 typedef struct _character {
231 typedef struct _instance {
232 character_t*character;
234 parameters_t parameters;
235 TAG* lastTag; //last tag which set the object
236 U16 lastFrame; //frame lastTag is in
239 typedef struct _outline {
244 typedef struct _gradient {
250 typedef struct _texture {
254 static void character_init(character_t*c)
256 memset(c, 0, sizeof(character_t));
258 static character_t* character_new()
261 c = (character_t*)malloc(sizeof(character_t));
265 static void instance_init(instance_t*i)
267 memset(i, 0, sizeof(instance_t));
269 static instance_t* instance_new()
272 c = (instance_t*)malloc(sizeof(instance_t));
277 static void incrementid()
281 syntaxerror("Out of character ids.");
286 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
288 character_t* c = character_new();
290 c->definingTag = ctag;
293 if(dictionary_lookup(&characters, name))
294 syntaxerror("character %s defined twice", name);
295 dictionary_put2(&characters, name, c);
297 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
299 swf_SetString(tag, name);
300 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
303 swf_SetString(tag, name);
305 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
307 character_t* c = character_new();
308 c->definingTag = ctag;
312 if(dictionary_lookup(&images, name))
313 syntaxerror("image %s defined twice", name);
314 dictionary_put2(&images, name, c);
316 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
318 instance_t* i = instance_new();
321 //swf_GetMatrix(0, &i->matrix);
322 if(dictionary_lookup(&instances, name))
323 syntaxerror("object %s defined twice", name);
324 dictionary_put2(&instances, name, i);
328 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)
331 p->scalex = scalex; p->scaley = scaley;
332 p->pin = pin; p->pivot = pivot;
333 p->rotate = rotate; p->cxform = cxform;
337 static void parameters_clear(parameters_t*p)
340 p->scalex = 1.0; p->scaley = 1.0;
343 p->pivot.x = 0; p->pivot.y = 0;
346 swf_GetCXForm(0, &p->cxform, 1);
349 static void makeMatrix(MATRIX*m, parameters_t*p)
358 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
359 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
360 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
361 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
363 m->sx = (int)(sx*65536+0.5);
364 m->r1 = (int)(r1*65536+0.5);
365 m->r0 = (int)(r0*65536+0.5);
366 m->sy = (int)(sy*65536+0.5);
370 h = swf_TurnPoint(p->pin, m);
375 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
380 r = swf_TurnRect(rect, &m);
381 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
382 currentrect.xmax == 0 && currentrect.ymax == 0)
385 swf_ExpandRect2(¤trect, &r);
389 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
391 SWF*swf = (SWF*)malloc(sizeof(SWF));
394 syntaxerror(".swf blocks can't be nested");
396 memset(swf, 0, sizeof(swf));
397 swf->fileVersion = version;
399 swf->frameRate = fps;
400 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
401 swf->compressed = compress;
402 swf_SetRGB(tag,&background);
404 if(stackpos==sizeof(stack)/sizeof(stack[0]))
405 syntaxerror("too many levels of recursion");
407 dictionary_init(&characters);
408 dictionary_init(&images);
409 dictionary_init(&textures);
410 dictionary_init(&outlines);
411 dictionary_init(&gradients);
412 dictionary_init(&instances);
413 dictionary_init(&fonts);
414 dictionary_init(&sounds);
416 memset(&stack[stackpos], 0, sizeof(stack[0]));
417 stack[stackpos].type = 0;
418 stack[stackpos].filename = strdup(name);
419 stack[stackpos].swf = swf;
420 stack[stackpos].oldframe = -1;
425 memset(¤trect, 0, sizeof(currentrect));
428 memset(idmap, 0, sizeof(idmap));
432 void s_sprite(char*name)
434 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
435 swf_SetU16(tag, id); //id
436 swf_SetU16(tag, 0); //frames
438 memset(&stack[stackpos], 0, sizeof(stack[0]));
439 stack[stackpos].type = 1;
440 stack[stackpos].oldframe = currentframe;
441 stack[stackpos].olddepth = currentdepth;
442 stack[stackpos].oldrect = currentrect;
443 stack[stackpos].oldinstances = instances;
444 stack[stackpos].tag = tag;
445 stack[stackpos].id = id;
446 stack[stackpos].name = strdup(name);
448 /* FIXME: those four fields should be bundled together */
449 dictionary_init(&instances);
452 memset(¤trect, 0, sizeof(currentrect));
458 typedef struct _buttonrecord
466 typedef struct _button
470 buttonrecord_t records[4];
473 static button_t mybutton;
475 void s_button(char*name)
477 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
478 swf_SetU16(tag, id); //id
479 swf_ButtonSetFlags(tag, 0); //menu=no
481 memset(&mybutton, 0, sizeof(mybutton));
483 memset(&stack[stackpos], 0, sizeof(stack[0]));
484 stack[stackpos].type = 3;
485 stack[stackpos].tag = tag;
486 stack[stackpos].id = id;
487 stack[stackpos].name = strdup(name);
488 stack[stackpos].oldrect = currentrect;
489 memset(¤trect, 0, sizeof(currentrect));
494 void s_buttonput(char*character, char*as, parameters_t p)
496 character_t* c = dictionary_lookup(&characters, character);
501 if(!stackpos || (stack[stackpos-1].type != 3)) {
502 syntaxerror(".show may only appear in .button");
505 syntaxerror("character %s not known (in .shape %s)", character, character);
507 if(mybutton.endofshapes) {
508 syntaxerror("a .do may not precede a .show", character, character);
511 m = s_instancepos(c->size, &p);
519 if(*s==',' || *s==0) {
520 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
521 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
522 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
523 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
524 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
525 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
532 static void setbuttonrecords(TAG*tag)
534 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
535 if(!mybutton.endofshapes) {
538 if(!mybutton.records[3].set) {
539 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
543 if(mybutton.records[t].set) {
544 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
547 swf_SetU8(tag,0); // end of button records
548 mybutton.endofshapes = 1;
552 void s_buttonaction(int flags, char*action)
558 setbuttonrecords(stack[stackpos-1].tag);
560 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
562 syntaxerror("Couldn't compile ActionScript");
565 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
566 swf_ActionSet(stack[stackpos-1].tag, a);
567 mybutton.nr_actions++;
572 static void setactionend(TAG*tag)
574 if(!mybutton.nr_actions) {
575 /* no actions means we didn't have an actionoffset,
576 which means we can't signal the end of the
577 buttonaction records, so, *sigh*, we have
578 to insert a dummy record */
579 swf_SetU16(tag, 0); //offset
580 swf_SetU16(tag, 0); //condition
581 swf_SetU8(tag, 0); //action
585 static void s_endButton()
588 setbuttonrecords(stack[stackpos-1].tag);
589 setactionend(stack[stackpos-1].tag);
592 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
596 tag = stack[stackpos].tag;
597 currentrect = stack[stackpos].oldrect;
599 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
600 free(stack[stackpos].name);
603 TAG* removeFromTo(TAG*from, TAG*to)
605 TAG*save = from->prev;
607 TAG*next = from->next;
615 static void s_endSprite()
617 SRECT r = currentrect;
619 if(stack[stackpos].cut)
620 tag = removeFromTo(stack[stackpos].cut, tag);
624 /* TODO: before clearing, prepend "<spritename>." to names and
625 copy into old instances dict */
626 dictionary_clear(&instances);
628 currentframe = stack[stackpos].oldframe;
629 currentrect = stack[stackpos].oldrect;
630 currentdepth = stack[stackpos].olddepth;
631 instances = stack[stackpos].oldinstances;
633 tag = swf_InsertTag(tag, ST_SHOWFRAME);
634 tag = swf_InsertTag(tag, ST_END);
636 tag = stack[stackpos].tag;
639 syntaxerror("internal error(7)");
641 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
642 free(stack[stackpos].name);
645 static void s_endSWF()
651 if(stack[stackpos].cut)
652 tag = removeFromTo(stack[stackpos].cut, tag);
656 swf = stack[stackpos].swf;
657 filename = stack[stackpos].filename;
659 //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
660 // tag = swf_InsertTag(tag, ST_SHOWFRAME);
661 tag = swf_InsertTag(tag, ST_SHOWFRAME);
663 tag = swf_InsertTag(tag, ST_END);
665 swf_OptimizeTagOrder(swf);
671 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
672 swf->movieSize = currentrect; /* "autocrop" */
675 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
676 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
677 swf->movieSize.ymax += 20;
678 warning("Empty bounding box for movie");
684 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
686 syntaxerror("couldn't create output file %s", filename);
689 {if(swf_WriteCGI(swf)<0) syntaxerror("WriteCGI() failed.\n");}
690 else if(swf->compressed)
691 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
693 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
697 dictionary_clear(&instances);
698 dictionary_clear(&characters);
699 dictionary_clear(&images);
700 dictionary_clear(&textures);
701 dictionary_clear(&outlines);
702 dictionary_clear(&gradients);
703 dictionary_clear(&fonts);
704 dictionary_clear(&sounds);
714 if(stack[stackpos-1].type == 0)
715 syntaxerror("End of file encountered in .flash block");
716 if(stack[stackpos-1].type == 1)
717 syntaxerror("End of file encountered in .sprite block");
718 if(stack[stackpos-1].type == 2)
719 syntaxerror("End of file encountered in .clip block");
725 return currentframe+1;
728 void s_frame(int nr, int cut, char*name, char anchor)
734 syntaxerror("Illegal frame number");
735 nr--; // internally, frame 1 is frame 0
737 for(t=currentframe;t<nr;t++) {
738 tag = swf_InsertTag(tag, ST_SHOWFRAME);
739 if(t==nr-1 && name && *name) {
740 tag = swf_InsertTag(tag, ST_FRAMELABEL);
741 swf_SetString(tag, name);
743 swf_SetU8(tag, 1); //make this an anchor
746 if(nr == 0 && currentframe == 0 && name && *name) {
747 tag = swf_InsertTag(tag, ST_FRAMELABEL);
748 swf_SetString(tag, name);
750 swf_SetU8(tag, 1); //make this an anchor
755 syntaxerror("Can't cut, frame empty");
757 stack[stackpos].cut = tag;
763 int parseColor2(char*str, RGBA*color);
765 int addFillStyle(SHAPE*s, SRECT*r, char*name)
772 parseColor2(name, &color);
773 return swf_ShapeAddSolidFillStyle(s, &color);
774 } else if ((texture = dictionary_lookup(&textures, name))) {
775 return swf_ShapeAddFillStyle2(s, &texture->fs);
776 } else if((image = dictionary_lookup(&images, name))) {
778 swf_GetMatrix(0, &m);
779 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
780 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
783 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
784 } else if ((gradient = dictionary_lookup(&gradients, name))) {
788 swf_GetMatrix(0, &rot);
789 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
790 csin = sin(-gradient->rotate*2*3.14159265358979/360);
792 rot.r1 = -csin*65536;
795 r2 = swf_TurnRect(*r, &rot);
796 swf_GetMatrix(0, &m);
797 m.sx = (r2.xmax - r2.xmin)*2*ccos;
798 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
799 m.r0 = (r2.ymax - r2.ymin)*2*csin;
800 m.sy = (r2.ymax - r2.ymin)*2*ccos;
801 m.tx = r->xmin + (r->xmax - r->xmin)/2;
802 m.ty = r->ymin + (r->ymax - r->ymin)/2;
803 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
804 } else if (parseColor2(name, &color)) {
805 return swf_ShapeAddSolidFillStyle(s, &color);
807 syntaxerror("not a color/fillstyle: %s", name);
812 RGBA black={r:0,g:0,b:0,a:0};
813 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
822 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
825 linewidth = linewidth>=20?linewidth-20:0;
826 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
829 fs1 = addFillStyle(s, &r2, texture);
832 r.xmin = r2.xmin-linewidth/2;
833 r.ymin = r2.ymin-linewidth/2;
834 r.xmax = r2.xmax+linewidth/2;
835 r.ymax = r2.ymax+linewidth/2;
837 swf_SetShapeHeader(tag,s);
838 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
839 swf_ShapeSetLine(tag,s,width,0);
840 swf_ShapeSetLine(tag,s,0,height);
841 swf_ShapeSetLine(tag,s,-width,0);
842 swf_ShapeSetLine(tag,s,0,-height);
843 swf_ShapeSetEnd(tag);
846 s_addcharacter(name, id, tag, r);
850 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
856 outline = dictionary_lookup(&outlines, outlinename);
858 syntaxerror("outline %s not defined", outlinename);
862 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
865 linewidth = linewidth>=20?linewidth-20:0;
866 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
869 fs1 = addFillStyle(s, &r2, texture);
872 rect.xmin = r2.xmin-linewidth/2;
873 rect.ymin = r2.ymin-linewidth/2;
874 rect.xmax = r2.xmax+linewidth/2;
875 rect.ymax = r2.ymax+linewidth/2;
877 swf_SetRect(tag,&rect);
878 swf_SetShapeStyles(tag, s);
879 swf_ShapeCountBits(s,0,0);
880 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
881 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
882 swf_SetShapeBits(tag, s);
883 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
886 s_addcharacter(name, id, tag, rect);
890 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
895 r2.xmin = r2.ymin = 0;
899 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
902 linewidth = linewidth>=20?linewidth-20:0;
903 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
906 fs1 = addFillStyle(s, &r2, texture);
908 rect.xmin = r2.xmin-linewidth/2;
909 rect.ymin = r2.ymin-linewidth/2;
910 rect.xmax = r2.xmax+linewidth/2;
911 rect.ymax = r2.ymax+linewidth/2;
913 swf_SetRect(tag,&rect);
914 swf_SetShapeHeader(tag,s);
915 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
916 swf_ShapeSetCircle(tag, s, r,r,r,r);
917 swf_ShapeSetEnd(tag);
920 s_addcharacter(name, id, tag, rect);
924 void s_textshape(char*name, char*fontname, float size, char*_text)
927 U8*text = (U8*)_text;
931 font = dictionary_lookup(&fonts, fontname);
933 syntaxerror("font \"%s\" not known!", fontname);
935 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
936 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
937 s_box(name, 0, 0, black, 20, 0);
940 g = font->ascii2glyph[text[0]];
942 outline = malloc(sizeof(outline_t));
943 memset(outline, 0, sizeof(outline_t));
944 outline->shape = font->glyph[g].shape;
945 outline->bbox = font->layout->bounds[g];
949 swf_Shape11DrawerInit(&draw, 0);
950 swf_DrawText(&draw, font, (int)(size*100), _text);
952 outline->shape = swf_ShapeDrawerToShape(&draw);
953 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
957 if(dictionary_lookup(&outlines, name))
958 syntaxerror("outline %s defined twice", name);
959 dictionary_put2(&outlines, name, outline);
962 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
967 font = dictionary_lookup(&fonts, fontname);
969 syntaxerror("font \"%s\" not known!", fontname);
971 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
973 if(!font->numchars) {
974 s_box(name, 0, 0, black, 20, 0);
977 r = swf_SetDefineText(tag, font, &color, text, size);
979 s_addcharacter(name, id, tag, r);
983 void s_quicktime(char*name, char*url)
988 memset(&r, 0, sizeof(r));
990 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
992 swf_SetString(tag, url);
994 s_addcharacter(name, id, tag, r);
998 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)
1001 EditTextLayout layout;
1004 if(fontname && *fontname) {
1005 flags |= ET_USEOUTLINES;
1006 font = dictionary_lookup(&fonts, fontname);
1008 syntaxerror("font \"%s\" not known!", fontname);
1010 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
1011 swf_SetU16(tag, id);
1012 layout.align = align;
1013 layout.leftmargin = 0;
1014 layout.rightmargin = 0;
1022 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1024 s_addcharacter(name, id, tag, r);
1028 /* type: either "jpeg" or "png"
1030 void s_image(char*name, char*type, char*filename, int quality)
1032 /* an image is actually two folded: 1st bitmap, 2nd character.
1033 Both of them can be used separately */
1035 /* step 1: the bitmap */
1039 if(!strcmp(type,"jpeg")) {
1040 #ifndef HAVE_LIBJPEG
1041 warning("no jpeg support compiled in");
1042 s_box(name, 0, 0, black, 20, 0);
1045 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1046 swf_SetU16(tag, imageID);
1048 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1049 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1052 swf_GetJPEGSize(filename, &width, &height);
1059 s_addimage(name, id, tag, r);
1062 } else if(!strcmp(type,"png")) {
1064 swf_SetU16(tag, imageID);
1066 getPNG(filename, &width, &height, (unsigned char**)&data);
1069 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1072 /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
1073 tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
1074 swf_SetU16(tag, imageID);
1075 swf_SetLosslessImage(tag, data, width, height);
1081 s_addimage(name, id, tag, r);
1084 warning("image type \"%s\" not supported yet!", type);
1085 s_box(name, 0, 0, black, 20, 0);
1089 /* step 2: the character */
1090 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1091 swf_SetU16(tag, id);
1092 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1094 s_addcharacter(name, id, tag, r);
1098 void s_getBitmapSize(char*name, int*width, int*height)
1100 character_t* image = dictionary_lookup(&images, name);
1101 gradient_t* gradient = dictionary_lookup(&gradients,name);
1103 *width = image->size.xmax;
1104 *height = image->size.ymax;
1108 /* internal SWF gradient size */
1109 if(gradient->radial) {
1118 syntaxerror("No such bitmap/gradient: %s", name);
1121 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1123 gradient_t* gradient = dictionary_lookup(&gradients, object);
1124 character_t* bitmap = dictionary_lookup(&images, object);
1125 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1127 FILLSTYLE*fs = &texture->fs;
1129 memset(&p, 0, sizeof(parameters_t));
1132 fs->type = FILL_TILED;
1133 fs->id_bitmap = bitmap->id;
1134 } else if(gradient) {
1135 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1136 fs->gradient = gradient->gradient;
1138 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1139 makeMatrix(&fs->m, &p);
1140 if(gradient && !gradient->radial) {
1147 p2 = swf_TurnPoint(p1, &m);
1152 if(dictionary_lookup(&textures, name))
1153 syntaxerror("texture %s defined twice", name);
1154 dictionary_put2(&textures, name, texture);
1157 void dumpSWF(SWF*swf)
1159 TAG* tag = swf->firstTag;
1160 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1162 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1165 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1168 void s_font(char*name, char*filename)
1171 font = swf_LoadFont(filename);
1174 warning("Couldn't open font file \"%s\"", filename);
1175 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1176 memset(font, 0, sizeof(SWFFONT));
1177 dictionary_put2(&fonts, name, font);
1183 /* fix the layout. Only needed for old fonts */
1185 for(t=0;t<font->numchars;t++) {
1186 font->glyph[t].advance = 0;
1189 swf_FontCreateLayout(font);
1191 /* just in case this thing is used in .edittext later on */
1192 swf_FontPrepareForEditText(font);
1195 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1196 swf_FontSetDefine2(tag, font);
1197 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1199 swf_SetU16(tag, id);
1200 swf_SetString(tag, name);
1203 if(dictionary_lookup(&fonts, name))
1204 syntaxerror("font %s defined twice", name);
1205 dictionary_put2(&fonts, name, font);
1210 typedef struct _sound_t
1216 void s_sound(char*name, char*filename)
1218 struct WAV wav, wav2;
1222 unsigned numsamples;
1223 unsigned blocksize = 1152;
1226 if(wav_read(&wav, filename)) {
1228 wav_convert2mono(&wav, &wav2, 44100);
1229 samples = (U16*)wav2.data;
1230 numsamples = wav2.size/2;
1232 #ifdef WORDS_BIGENDIAN
1234 for(t=0;t<numsamples;t++) {
1235 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1238 } else if(mp3_read(&mp3, filename)) {
1239 fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1245 warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1250 if(numsamples%blocksize != 0)
1252 // apply padding, so that block is a multiple of blocksize
1253 int numblocks = (numsamples+blocksize-1)/blocksize;
1256 numsamples2 = numblocks * blocksize;
1257 samples2 = malloc(sizeof(U16)*numsamples2);
1258 memcpy(samples2, samples, numsamples*sizeof(U16));
1259 memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1260 numsamples = numsamples2;
1264 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1265 swf_SetU16(tag, id); //id
1268 swf_SetSoundDefineMP3(
1269 tag, mp3.data, mp3.size,
1277 swf_SetSoundDefine(tag, samples, numsamples);
1280 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1281 swf_SetU16(tag, id);
1282 swf_SetString(tag, name);
1283 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1285 swf_SetU16(tag, id);
1286 swf_SetString(tag, name);
1288 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1292 if(dictionary_lookup(&sounds, name))
1293 syntaxerror("sound %s defined twice", name);
1294 dictionary_put2(&sounds, name, sound);
1302 static char* gradient_getToken(const char**p)
1306 while(**p && strchr(" \t\n\r", **p)) {
1310 while(**p && !strchr(" \t\n\r", **p)) {
1313 result = malloc((*p)-start+1);
1314 memcpy(result,start,(*p)-start+1);
1315 result[(*p)-start] = 0;
1319 float parsePercent(char*str);
1320 RGBA parseColor(char*str);
1322 GRADIENT parseGradient(const char*str)
1326 const char* p = str;
1327 memset(&gradient, 0, sizeof(GRADIENT));
1329 char*posstr,*colorstr;
1332 posstr = gradient_getToken(&p);
1335 pos = (int)(parsePercent(posstr)*255.0);
1338 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1339 colorstr = gradient_getToken(&p);
1340 color = parseColor(colorstr);
1341 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1342 warning("gradient record too big- max size is 8, rest ignored");
1345 gradient.ratios[gradient.num] = pos;
1346 gradient.rgba[gradient.num] = color;
1355 void s_gradient(char*name, const char*text, int radial, int rotate)
1357 gradient_t* gradient;
1358 gradient = malloc(sizeof(gradient_t));
1359 memset(gradient, 0, sizeof(gradient_t));
1360 gradient->gradient = parseGradient(text);
1361 gradient->radial = radial;
1362 gradient->rotate = rotate;
1364 if(dictionary_lookup(&gradients, name))
1365 syntaxerror("gradient %s defined twice", name);
1366 dictionary_put2(&gradients, name, gradient);
1369 void s_action(const char*text)
1372 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1374 syntaxerror("Couldn't compile ActionScript");
1377 tag = swf_InsertTag(tag, ST_DOACTION);
1379 swf_ActionSet(tag, a);
1384 void s_initaction(const char*character, const char*text)
1388 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1390 syntaxerror("Couldn't compile ActionScript");
1393 c = (character_t*)dictionary_lookup(&characters, character);
1395 tag = swf_InsertTag(tag, ST_DOINITACTION);
1396 swf_SetU16(tag, c->id);
1397 swf_ActionSet(tag, a);
1402 int s_swf3action(char*name, char*action)
1405 instance_t* object = 0;
1407 object = (instance_t*)dictionary_lookup(&instances, name);
1408 if(!object && name && *name) {
1409 /* we have a name, but couldn't find it. Abort. */
1412 a = action_SetTarget(0, name);
1413 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1414 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1415 else if(!strcmp(action, "stop")) a = action_Stop(a);
1416 else if(!strcmp(action, "play")) a = action_Play(a);
1417 a = action_SetTarget(a, "");
1420 tag = swf_InsertTag(tag, ST_DOACTION);
1421 swf_ActionSet(tag, a);
1426 void s_outline(char*name, char*format, char*source)
1435 //swf_Shape10DrawerInit(&draw, 0);
1436 swf_Shape11DrawerInit(&draw, 0);
1438 draw_string(&draw, source);
1440 shape = swf_ShapeDrawerToShape(&draw);
1441 bounds = swf_ShapeDrawerGetBBox(&draw);
1442 draw.dealloc(&draw);
1444 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1445 outline->shape = shape;
1446 outline->bbox = bounds;
1448 if(dictionary_lookup(&outlines, name))
1449 syntaxerror("outline %s defined twice", name);
1450 dictionary_put2(&outlines, name, outline);
1453 int s_playsound(char*name, int loops, int nomultiple, int stop)
1459 sound = dictionary_lookup(&sounds, name);
1463 tag = swf_InsertTag(tag, ST_STARTSOUND);
1464 swf_SetU16(tag, sound->id); //id
1465 memset(&info, 0, sizeof(info));
1468 info.nomultiple = nomultiple;
1469 swf_SetSoundInfo(tag, &info);
1473 void s_includeswf(char*name, char*filename)
1481 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1482 f = open(filename,O_RDONLY|O_BINARY);
1484 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1485 s_box(name, 0, 0, black, 20, 0);
1488 if (swf_ReadSWF(f,&swf)<0) {
1489 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1490 s_box(name, 0, 0, black, 20, 0);
1495 /* FIXME: The following sets the bounding Box for the character.
1496 It is wrong for two reasons:
1497 a) It may be too small (in case objects in the movie clip at the borders)
1498 b) it may be too big (because the poor movie never got autocropped)
1502 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1503 swf_SetU16(tag, id);
1504 swf_SetU16(tag, swf.frameCount);
1506 swf_Relocate(&swf, idmap);
1508 ftag = swf.firstTag;
1512 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1513 if(cutout[t] == ftag->id) {
1517 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1519 if(ftag->id == ST_END)
1523 /* We simply dump all tags right after the sprite
1524 header, relying on the fact that swf_OptimizeTagOrder() will
1525 sort things out for us later.
1526 We also rely on the fact that the imported SWF is well-formed.
1528 tag = swf_InsertTag(tag, ftag->id);
1529 swf_SetBlock(tag, ftag->data, ftag->len);
1533 syntaxerror("Included file %s contains errors", filename);
1534 tag = swf_InsertTag(tag, ST_END);
1538 s_addcharacter(name, id, tag, r);
1541 SRECT s_getCharBBox(char*name)
1543 character_t* c = dictionary_lookup(&characters, name);
1544 if(!c) syntaxerror("character '%s' unknown(2)", name);
1547 SRECT s_getInstanceBBox(char*name)
1549 instance_t * i = dictionary_lookup(&instances, name);
1551 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1553 if(!c) syntaxerror("internal error(5)");
1556 parameters_t s_getParameters(char*name)
1558 instance_t * i = dictionary_lookup(&instances, name);
1559 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1560 return i->parameters;
1562 void s_startclip(char*instance, char*character, parameters_t p)
1564 character_t* c = dictionary_lookup(&characters, character);
1568 syntaxerror("character %s not known", character);
1570 i = s_addinstance(instance, c, currentdepth);
1572 m = s_instancepos(i->character->size, &p);
1574 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1575 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1576 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1578 i->lastFrame= currentframe;
1580 stack[stackpos].tag = tag;
1581 stack[stackpos].type = 2;
1590 swf_SetTagPos(stack[stackpos].tag, 0);
1591 swf_GetPlaceObject(stack[stackpos].tag, &p);
1592 p.clipdepth = currentdepth;
1594 swf_ClearTag(stack[stackpos].tag);
1595 swf_SetPlaceObject(stack[stackpos].tag, &p);
1599 void s_put(char*instance, char*character, parameters_t p)
1601 character_t* c = dictionary_lookup(&characters, character);
1605 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1608 i = s_addinstance(instance, c, currentdepth);
1610 m = s_instancepos(i->character->size, &p);
1612 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1613 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1615 i->lastFrame = currentframe;
1619 void s_jump(char*instance, parameters_t p)
1621 instance_t* i = dictionary_lookup(&instances, instance);
1624 syntaxerror("instance %s not known", instance);
1628 m = s_instancepos(i->character->size, &p);
1630 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1631 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1633 i->lastFrame = currentframe;
1636 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1640 if(num==0 || num==1)
1642 ratio = (float)pos/(float)num;
1644 p.x = (p2->x-p1->x)*ratio + p1->x;
1645 p.y = (p2->y-p1->y)*ratio + p1->y;
1646 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1647 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1648 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1649 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1651 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1652 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1653 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1654 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1656 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1657 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1658 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1659 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1661 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1662 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1663 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1664 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1668 void s_change(char*instance, parameters_t p2)
1670 instance_t* i = dictionary_lookup(&instances, instance);
1674 int frame, allframes;
1676 syntaxerror("instance %s not known", instance);
1680 allframes = currentframe - i->lastFrame - 1;
1682 warning(".change ignored. can only .put/.change an object once per frame.");
1686 m = s_instancepos(i->character->size, &p2);
1687 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1688 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1691 /* o.k., we got the start and end point set. Now iterate though all the
1692 tags in between, inserting object changes after each new frame */
1695 if(!t) syntaxerror("internal error(6)");
1697 while(frame < allframes) {
1698 if(t->id == ST_SHOWFRAME) {
1703 p = s_interpolate(&p1, &p2, frame, allframes);
1704 m = s_instancepos(i->character->size, &p); //needed?
1705 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1706 i->lastFrame = currentframe;
1707 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1709 if(frame == allframes)
1714 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1718 void s_delinstance(char*instance)
1720 instance_t* i = dictionary_lookup(&instances, instance);
1722 syntaxerror("instance %s not known", instance);
1724 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1725 swf_SetU16(tag, i->depth);
1726 dictionary_del(&instances, instance);
1729 void s_qchange(char*instance, parameters_t p)
1736 syntaxerror(".end unexpected");
1737 if(stack[stackpos-1].type == 0)
1739 else if(stack[stackpos-1].type == 1)
1741 else if(stack[stackpos-1].type == 2)
1743 else if(stack[stackpos-1].type == 3)
1745 else syntaxerror("internal error 1");
1748 // ------------------------------------------------------------------------
1750 typedef int command_func_t(map_t*args);
1752 SRECT parseBox(char*str)
1755 float xmin, xmax, ymin, ymax;
1756 char*x = strchr(str, 'x');
1758 if(!strcmp(str, "autocrop")) {
1759 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1763 d1 = strchr(x+1, ':');
1765 d2 = strchr(d1+1, ':');
1767 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1771 else if(d1 && !d2) {
1772 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1778 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1783 r.xmin = (SCOORD)(xmin*20);
1784 r.ymin = (SCOORD)(ymin*20);
1785 r.xmax = (SCOORD)(xmax*20);
1786 r.ymax = (SCOORD)(ymax*20);
1789 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1792 float parseFloat(char*str)
1796 int parseInt(char*str)
1801 if(str[0]=='+' || str[0]=='-')
1805 if(str[t]<'0' || str[t]>'9')
1806 syntaxerror("Not an Integer: \"%s\"", str);
1809 int parseTwip(char*str)
1813 if(str[0]=='+' || str[0]=='-') {
1818 dot = strchr(str, '.');
1822 return sign*parseInt(str)*20;
1824 char* old = strdup(str);
1825 int l=strlen(dot+1);
1828 for(s=str;s<dot-1;s++)
1829 if(*s<'0' || *s>'9')
1830 syntaxerror("Not a coordinate: \"%s\"", str);
1832 if(*s<'0' || *s>'9')
1833 syntaxerror("Not a coordinate: \"%s\"", str);
1835 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1836 dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
1839 warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
1843 return sign*atoi(str)*20;
1845 return sign*atoi(str)*20+atoi(dot)*2;
1847 return sign*atoi(str)*20+atoi(dot)/5;
1852 int isPoint(char*str)
1854 if(strchr(str, '('))
1860 SPOINT parsePoint(char*str)
1864 int l = strlen(str);
1865 char*comma = strchr(str, ',');
1866 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1867 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1868 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1869 p.x = parseTwip(tmp);
1870 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1871 p.y = parseTwip(tmp);
1875 int parseColor2(char*str, RGBA*color)
1877 int l = strlen(str);
1881 struct {unsigned char r,g,b;char*name;} colors[] =
1882 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1883 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1884 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1885 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1886 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1887 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1888 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1889 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1890 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1891 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1892 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1893 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1897 if(str[0]=='#' && (l==7 || l==9)) {
1898 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1900 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1902 color->r = r; color->g = g; color->b = b; color->a = a;
1905 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1906 if(!strcmp(str, colors[t].name)) {
1911 color->r = r; color->g = g; color->b = b; color->a = a;
1917 RGBA parseColor(char*str)
1920 if(!parseColor2(str, &c))
1921 syntaxerror("Expression '%s' is not a color", str);
1925 typedef struct _muladd {
1930 MULADD parseMulAdd(char*str)
1933 char* str2 = (char*)malloc(strlen(str)+5);
1940 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1941 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1942 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1943 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1944 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1945 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1946 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1947 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1948 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1949 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1951 syntaxerror("'%s' is not a valid color transform expression", str);
1953 m.add = (int)(add*256);
1954 m.mul = (int)(mul*256);
1959 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1961 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1962 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1964 if(a<-32768) a=-32768;
1965 if(a>32767) a=32767;
1966 if(m<-32768) m=-32768;
1967 if(m>32767) m=32767;
1973 float parsePxOrPercent(char*fontname, char*str)
1975 int l = strlen(str);
1976 if(strchr(str, '%'))
1977 return parsePercent(str);
1978 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1979 float p = atof(str);
1980 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1982 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1986 float parsePercent(char*str)
1988 int l = strlen(str);
1992 return atoi(str)/100.0;
1994 syntaxerror("Expression '%s' is not a percentage", str);
1997 int isPercent(char*str)
1999 return str[strlen(str)-1]=='%';
2001 int parseNewSize(char*str, int size)
2004 return parsePercent(str)*size;
2006 return (int)(atof(str)*20);
2009 int isColor(char*str)
2012 return parseColor2(str, &c);
2015 static char* lu(map_t* args, char*name)
2017 char* value = map_lookup(args, name);
2019 map_dump(args, stdout, "");
2020 syntaxerror("internal error 2: value %s should be set", name);
2025 static int c_flash(map_t*args)
2027 char* filename = map_lookup(args, "filename");
2028 char* compressstr = lu(args, "compress");
2029 SRECT bbox = parseBox(lu(args, "bbox"));
2030 int version = parseInt(lu(args, "version"));
2031 int fps = (int)(parseFloat(lu(args, "fps"))*256);
2033 RGBA color = parseColor(lu(args, "background"));
2035 if(!filename || !*filename) {
2036 /* for compatibility */
2037 filename = map_lookup(args, "name");
2038 if(!filename || !*filename) {
2041 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2042 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2046 if(!filename || override_outputname)
2047 filename = outputname;
2049 if(!strcmp(compressstr, "default"))
2050 compress = version==6;
2051 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2053 else if(!strcmp(compressstr, "no"))
2055 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2057 s_swf(filename, bbox, version, fps, compress, color);
2060 int isRelative(char*str)
2062 return !strncmp(str, "<plus>", 6) ||
2063 !strncmp(str, "<minus>", 7);
2065 char* getOffset(char*str)
2067 if(!strncmp(str, "<plus>", 6))
2069 if(!strncmp(str, "<minus>", 7))
2071 syntaxerror("internal error (347)");
2074 int getSign(char*str)
2076 if(!strncmp(str, "<plus>", 6))
2078 if(!strncmp(str, "<minus>", 7))
2080 syntaxerror("internal error (348)");
2083 static dictionary_t points;
2084 static mem_t mpoints;
2085 int points_initialized = 0;
2087 SPOINT getPoint(SRECT r, char*name)
2090 if(!strcmp(name, "center")) {
2092 p.x = (r.xmin + r.xmax)/2;
2093 p.y = (r.ymin + r.ymax)/2;
2097 if(points_initialized)
2098 l = (int)dictionary_lookup(&points, name);
2100 syntaxerror("Invalid point: \"%s\".", name);
2103 return *(SPOINT*)&mpoints.buffer[l];
2106 static int texture2(char*name, char*object, map_t*args, int errors)
2109 char*xstr = map_lookup(args, "x");
2110 char*ystr = map_lookup(args, "y");
2111 char*widthstr = map_lookup(args, "width");
2112 char*heightstr = map_lookup(args, "height");
2113 char*scalestr = map_lookup(args, "scale");
2114 char*scalexstr = map_lookup(args, "scalex");
2115 char*scaleystr = map_lookup(args, "scaley");
2116 char*rotatestr = map_lookup(args, "rotate");
2117 char* shearstr = map_lookup(args, "shear");
2118 char* radiusstr = map_lookup(args, "r");
2120 float scalex = 1.0, scaley = 1.0;
2121 float rotate=0, shear=0;
2123 if(!*xstr && !*ystr) {
2125 syntaxerror("x and y must be set");
2128 if(*scalestr && (*scalexstr || *scaleystr)) {
2129 syntaxerror("scale and scalex/scaley can't both be set");
2132 if((*widthstr || *heightstr) && *radiusstr) {
2133 syntaxerror("width/height and radius can't both be set");
2136 widthstr = radiusstr;
2137 heightstr = radiusstr;
2139 if(!*xstr) xstr="0";
2140 if(!*ystr) ystr="0";
2141 if(!*rotatestr) rotatestr="0";
2142 if(!*shearstr) shearstr="0";
2145 scalex = scaley = parsePercent(scalestr);
2146 } else if(*scalexstr || *scaleystr) {
2147 if(scalexstr) scalex = parsePercent(scalexstr);
2148 if(scaleystr) scaley = parsePercent(scaleystr);
2149 } else if(*widthstr || *heightstr) {
2152 s_getBitmapSize(object, &width, &height);
2154 scalex = (float)parseTwip(widthstr)/(float)width;
2156 scaley = (float)parseTwip(heightstr)/(float)height;
2158 x = parseTwip(xstr);
2159 y = parseTwip(ystr);
2160 rotate = parseFloat(rotatestr);
2161 shear = parseFloat(shearstr);
2163 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2168 static int c_texture(map_t*args)
2170 char*name = lu(args, "instance");
2171 char*object = lu(args, "character");
2172 return texture2(name, object, args, 1);
2175 static int c_gradient(map_t*args)
2177 char*name = lu(args, "name");
2178 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2179 int rotate = parseInt(lu(args, "rotate"));
2183 syntaxerror("colon (:) expected");
2185 s_gradient(name, text, radial, rotate);
2187 /* check whether we also have placement information,
2188 which would make this a positioned gradient.
2189 If there is placement information, texture2() will
2190 add a texture, which has priority over the gradient.
2192 texture2(name, name, args, 0);
2195 static int c_point(map_t*args)
2197 char*name = lu(args, "name");
2201 if(!points_initialized) {
2202 dictionary_init(&points);
2204 points_initialized = 1;
2206 p.x = parseTwip(lu(args, "x"));
2207 p.y = parseTwip(lu(args, "y"));
2208 pos = mem_put(&mpoints, &p, sizeof(p));
2209 string_set(&s1, name);
2211 dictionary_put(&points, s1, (void*)pos);
2214 static int c_play(map_t*args)
2216 char*name = lu(args, "name");
2217 char*loop = lu(args, "loop");
2218 char*nomultiple = lu(args, "nomultiple");
2220 if(!strcmp(nomultiple, "nomultiple"))
2223 nm = parseInt(nomultiple);
2225 if(s_playsound(name, parseInt(loop), nm, 0)) {
2227 } else if(s_swf3action(name, "play")) {
2233 static int c_stop(map_t*args)
2235 char*name = map_lookup(args, "name");
2237 if(s_playsound(name, 0,0,1)) {
2239 } else if(s_swf3action(name, "stop")) {
2242 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2246 static int c_nextframe(map_t*args)
2248 char*name = lu(args, "name");
2250 if(s_swf3action(name, "nextframe")) {
2253 syntaxerror("I don't know anything about movie \"%s\"", name);
2257 static int c_previousframe(map_t*args)
2259 char*name = lu(args, "name");
2261 if(s_swf3action(name, "previousframe")) {
2264 syntaxerror("I don't know anything about movie \"%s\"", name);
2268 static int c_placement(map_t*args, int type)
2270 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2273 char* luminancestr = lu(args, "luminance");
2274 char* scalestr = lu(args, "scale");
2275 char* scalexstr = lu(args, "scalex");
2276 char* scaleystr = lu(args, "scaley");
2277 char* rotatestr = lu(args, "rotate");
2278 char* shearstr = lu(args, "shear");
2279 char* xstr="", *pivotstr="";
2280 char* ystr="", *anglestr="";
2281 char*above = lu(args, "above"); /*FIXME*/
2282 char*below = lu(args, "below");
2283 char* rstr = lu(args, "red");
2284 char* gstr = lu(args, "green");
2285 char* bstr = lu(args, "blue");
2286 char* astr = lu(args, "alpha");
2287 char* pinstr = lu(args, "pin");
2288 char* as = map_lookup(args, "as");
2296 if(type==9) { // (?) .rotate or .arcchange
2297 pivotstr = lu(args, "pivot");
2298 anglestr = lu(args, "angle");
2300 xstr = lu(args, "x");
2301 ystr = lu(args, "y");
2304 luminance = parseMulAdd(luminancestr);
2307 luminance.mul = 256;
2311 if(scalexstr[0]||scaleystr[0])
2312 syntaxerror("scalex/scaley and scale cannot both be set");
2313 scalexstr = scaleystr = scalestr;
2316 if(type == 0 || type == 4) {
2318 character = lu(args, "character");
2319 parameters_clear(&p);
2320 } else if (type == 5) {
2321 character = lu(args, "name");
2322 parameters_clear(&p);
2325 p = s_getParameters(instance);
2330 if(isRelative(xstr)) {
2331 if(type == 0 || type == 4)
2332 syntaxerror("relative x values not allowed for initial put or startclip");
2333 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2335 p.x = parseTwip(xstr);
2339 if(isRelative(ystr)) {
2340 if(type == 0 || type == 4)
2341 syntaxerror("relative y values not allowed for initial put or startclip");
2342 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2344 p.y = parseTwip(ystr);
2348 /* scale, scalex, scaley */
2350 oldbbox = s_getCharBBox(character);
2352 oldbbox = s_getInstanceBBox(instance);
2354 oldwidth = oldbbox.xmax - oldbbox.xmin;
2355 oldheight = oldbbox.ymax - oldbbox.ymin;
2357 if(oldwidth==0) p.scalex = 1.0;
2360 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2364 if(oldheight==0) p.scaley = 1.0;
2367 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2373 if(isRelative(rotatestr)) {
2374 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2376 p.rotate = parseFloat(rotatestr);
2382 if(isRelative(shearstr)) {
2383 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2385 p.shear = parseFloat(shearstr);
2390 if(isPoint(pivotstr))
2391 p.pivot = parsePoint(pivotstr);
2393 p.pivot = getPoint(oldbbox, pivotstr);
2397 p.pin = parsePoint(pinstr);
2399 p.pin = getPoint(oldbbox, pinstr);
2402 /* color transform */
2404 if(rstr[0] || luminancestr[0]) {
2407 r = parseMulAdd(rstr);
2409 r.add = p.cxform.r0;
2410 r.mul = p.cxform.r1;
2412 r = mergeMulAdd(r, luminance);
2413 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2415 if(gstr[0] || luminancestr[0]) {
2418 g = parseMulAdd(gstr);
2420 g.add = p.cxform.g0;
2421 g.mul = p.cxform.g1;
2423 g = mergeMulAdd(g, luminance);
2424 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2426 if(bstr[0] || luminancestr[0]) {
2429 b = parseMulAdd(bstr);
2431 b.add = p.cxform.b0;
2432 b.mul = p.cxform.b1;
2434 b = mergeMulAdd(b, luminance);
2435 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2438 MULADD a = parseMulAdd(astr);
2439 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2443 s_put(instance, character, p);
2445 s_change(instance, p);
2447 s_qchange(instance, p);
2449 s_jump(instance, p);
2451 s_startclip(instance, character, p);
2452 else if(type == 5) {
2454 s_buttonput(character, as, p);
2456 s_buttonput(character, "shape", p);
2461 static int c_put(map_t*args)
2463 c_placement(args, 0);
2466 static int c_change(map_t*args)
2468 c_placement(args, 1);
2471 static int c_qchange(map_t*args)
2473 c_placement(args, 2);
2476 static int c_arcchange(map_t*args)
2478 c_placement(args, 2);
2481 static int c_jump(map_t*args)
2483 c_placement(args, 3);
2486 static int c_startclip(map_t*args)
2488 c_placement(args, 4);
2491 static int c_show(map_t*args)
2493 c_placement(args, 5);
2496 static int c_del(map_t*args)
2498 char*instance = lu(args, "name");
2499 s_delinstance(instance);
2502 static int c_end(map_t*args)
2507 static int c_sprite(map_t*args)
2509 char* name = lu(args, "name");
2513 static int c_frame(map_t*args)
2515 char*framestr = lu(args, "n");
2516 char*cutstr = lu(args, "cut");
2518 char*name = lu(args, "name");
2519 char*anchor = lu(args, "anchor");
2522 if(!strcmp(anchor, "anchor") && !*name)
2527 if(strcmp(cutstr, "no"))
2529 if(isRelative(framestr)) {
2530 frame = s_getframe();
2531 if(getSign(framestr)<0)
2532 syntaxerror("relative frame expressions must be positive");
2533 frame += parseInt(getOffset(framestr));
2536 frame = parseInt(framestr);
2537 if(s_getframe() >= frame
2538 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2539 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2541 s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
2544 static int c_primitive(map_t*args)
2546 char*name = lu(args, "name");
2547 char*command = lu(args, "commandname");
2548 int width=0, height=0, r=0;
2549 int linewidth = parseTwip(lu(args, "line"));
2550 char*colorstr = lu(args, "color");
2551 RGBA color = parseColor(colorstr);
2552 char*fillstr = lu(args, "fill");
2559 if(!strcmp(command, "circle"))
2561 else if(!strcmp(command, "filled"))
2565 width = parseTwip(lu(args, "width"));
2566 height = parseTwip(lu(args, "height"));
2567 } else if (type==1) {
2568 r = parseTwip(lu(args, "r"));
2569 } else if (type==2) {
2570 outline = lu(args, "outline");
2573 if(!strcmp(fillstr, "fill"))
2575 if(!strcmp(fillstr, "none"))
2577 if(width<0 || height<0 || linewidth<0 || r<0)
2578 syntaxerror("values width, height, line, r must be positive");
2580 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2581 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2582 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2586 static int c_textshape(map_t*args)
2588 char*name = lu(args, "name");
2589 char*text = lu(args, "text");
2590 char*font = lu(args, "font");
2591 float size = parsePxOrPercent(font, lu(args, "size"));
2593 s_textshape(name, font, size, text);
2597 static int c_swf(map_t*args)
2599 char*name = lu(args, "name");
2600 char*filename = lu(args, "filename");
2601 char*command = lu(args, "commandname");
2602 if(!strcmp(command, "shape"))
2603 warning("Please use .swf instead of .shape");
2604 s_includeswf(name, filename);
2608 static int c_font(map_t*args)
2610 char*name = lu(args, "name");
2611 char*filename = lu(args, "filename");
2612 s_font(name, filename);
2616 static int c_sound(map_t*args)
2618 char*name = lu(args, "name");
2619 char*filename = lu(args, "filename");
2620 s_sound(name, filename);
2624 static int c_text(map_t*args)
2626 char*name = lu(args, "name");
2627 char*text = lu(args, "text");
2628 char*font = lu(args, "font");
2629 float size = parsePxOrPercent(font, lu(args, "size"));
2630 RGBA color = parseColor(lu(args, "color"));
2631 s_text(name, font, text, (int)(size*100), color);
2635 static int c_soundtrack(map_t*args)
2640 static int c_quicktime(map_t*args)
2642 char*name = lu(args, "name");
2643 char*url = lu(args, "url");
2644 s_quicktime(name, url);
2648 static int c_image(map_t*args)
2650 char*command = lu(args, "commandname");
2651 char*name = lu(args, "name");
2652 char*filename = lu(args, "filename");
2653 if(!strcmp(command,"jpeg")) {
2654 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2655 s_image(name, "jpeg", filename, quality);
2657 s_image(name, "png", filename, 0);
2662 static int c_outline(map_t*args)
2664 char*name = lu(args, "name");
2665 char*format = lu(args, "format");
2669 syntaxerror("colon (:) expected");
2671 s_outline(name, format, text);
2675 int fakechar(map_t*args)
2677 char*name = lu(args, "name");
2678 s_box(name, 0, 0, black, 20, 0);
2682 static int c_egon(map_t*args) {return fakechar(args);}
2683 static int c_button(map_t*args) {
2684 char*name = lu(args, "name");
2688 static int current_button_flags = 0;
2689 static int c_on_press(map_t*args)
2691 char*position = lu(args, "position");
2693 if(!strcmp(position, "inside")) {
2694 current_button_flags |= BC_OVERUP_OVERDOWN;
2695 } else if(!strcmp(position, "outside")) {
2696 //current_button_flags |= BC_IDLE_OUTDOWN;
2697 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2698 } else if(!strcmp(position, "anywhere")) {
2699 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2702 if(type == RAWDATA) {
2704 s_buttonaction(current_button_flags, action);
2705 current_button_flags = 0;
2711 static int c_on_release(map_t*args)
2713 char*position = lu(args, "position");
2715 if(!strcmp(position, "inside")) {
2716 current_button_flags |= BC_OVERDOWN_OVERUP;
2717 } else if(!strcmp(position, "outside")) {
2718 current_button_flags |= BC_OUTDOWN_IDLE;
2719 } else if(!strcmp(position, "anywhere")) {
2720 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2723 if(type == RAWDATA) {
2725 s_buttonaction(current_button_flags, action);
2726 current_button_flags = 0;
2732 static int c_on_move_in(map_t*args)
2734 char*position = lu(args, "state");
2736 if(!strcmp(position, "pressed")) {
2737 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2738 } else if(!strcmp(position, "not_pressed")) {
2739 current_button_flags |= BC_IDLE_OVERUP;
2740 } else if(!strcmp(position, "any")) {
2741 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2744 if(type == RAWDATA) {
2746 s_buttonaction(current_button_flags, action);
2747 current_button_flags = 0;
2753 static int c_on_move_out(map_t*args)
2755 char*position = lu(args, "state");
2757 if(!strcmp(position, "pressed")) {
2758 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2759 } else if(!strcmp(position, "not_pressed")) {
2760 current_button_flags |= BC_OVERUP_IDLE;
2761 } else if(!strcmp(position, "any")) {
2762 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2765 if(type == RAWDATA) {
2767 s_buttonaction(current_button_flags, action);
2768 current_button_flags = 0;
2774 static int c_on_key(map_t*args)
2776 char*key = lu(args, "key");
2778 if(strlen(key)==1) {
2781 current_button_flags |= 0x4000 + (key[0]*0x200);
2783 syntaxerror("invalid character: %c"+key[0]);
2788 <ctrl-x> = 0x200*(x-'a')
2792 syntaxerror("invalid key: %s",key);
2795 if(type == RAWDATA) {
2797 s_buttonaction(current_button_flags, action);
2798 current_button_flags = 0;
2805 static int c_edittext(map_t*args)
2807 //"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"},
2808 char*name = lu(args, "name");
2809 char*font = lu(args, "font");
2810 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2811 int width = parseTwip(lu(args, "width"));
2812 int height = parseTwip(lu(args, "height"));
2813 char*text = lu(args, "text");
2814 RGBA color = parseColor(lu(args, "color"));
2815 int maxlength = parseInt(lu(args, "maxlength"));
2816 char*variable = lu(args, "variable");
2817 char*passwordstr = lu(args, "password");
2818 char*wordwrapstr = lu(args, "wordwrap");
2819 char*multilinestr = lu(args, "multiline");
2820 char*htmlstr = lu(args, "html");
2821 char*noselectstr = lu(args, "noselect");
2822 char*readonlystr = lu(args, "readonly");
2823 char*borderstr = lu(args, "border");
2824 char*autosizestr = lu(args, "autosize");
2825 char*alignstr = lu(args, "align");
2829 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2830 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2831 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2832 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2833 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2834 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2835 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2836 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2837 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2838 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2839 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2840 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2841 else syntaxerror("Unknown alignment: %s", alignstr);
2843 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2847 static int c_morphshape(map_t*args) {return fakechar(args);}
2848 static int c_movie(map_t*args) {return fakechar(args);}
2850 static char* readfile(const char*filename)
2852 FILE*fi = fopen(filename, "rb");
2856 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2857 fseek(fi, 0, SEEK_END);
2859 fseek(fi, 0, SEEK_SET);
2860 text = rfx_alloc(l+1);
2861 fread(text, l, 1, fi);
2867 static int c_action(map_t*args)
2869 char* filename = map_lookup(args, "filename");
2870 if(!filename ||!*filename) {
2872 if(type != RAWDATA) {
2873 syntaxerror("colon (:) expected");
2877 s_action(readfile(filename));
2883 static int c_initaction(map_t*args)
2885 char* character = lu(args, "name");
2886 char* filename = map_lookup(args, "filename");
2887 if(!filename ||!*filename) {
2889 if(type != RAWDATA) {
2890 syntaxerror("colon (:) expected");
2892 s_initaction(character, text);
2894 s_initaction(character, readfile(filename));
2902 command_func_t* func;
2905 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2906 {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
2907 // "import" type stuff
2908 {"swf", c_swf, "name filename"},
2909 {"shape", c_swf, "name filename"},
2910 {"jpeg", c_image, "name filename quality=80%"},
2911 {"png", c_image, "name filename"},
2912 {"movie", c_movie, "name filename"},
2913 {"sound", c_sound, "name filename"},
2914 {"font", c_font, "name filename"},
2915 {"soundtrack", c_soundtrack, "filename"},
2916 {"quicktime", c_quicktime, "url"},
2918 // generators of primitives
2920 {"point", c_point, "name x=0 y=0"},
2921 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2922 {"outline", c_outline, "name format=simple"},
2923 {"textshape", c_textshape, "name font size=100% text"},
2925 // character generators
2926 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2927 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2928 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2930 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2931 {"text", c_text, "name text font size=100% color=white"},
2932 {"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="},
2933 {"morphshape", c_morphshape, "name start end"},
2934 {"button", c_button, "name"},
2935 {"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="},
2936 {"on_press", c_on_press, "position=inside"},
2937 {"on_release", c_on_release, "position=anywhere"},
2938 {"on_move_in", c_on_move_in, "state=not_pressed"},
2939 {"on_move_out", c_on_move_out, "state=not_pressed"},
2940 {"on_key", c_on_key, "key=any"},
2943 {"play", c_play, "name loop=0 @nomultiple=0"},
2944 {"stop", c_stop, "name= "},
2945 {"nextframe", c_nextframe, "name"},
2946 {"previousframe", c_previousframe, "name"},
2948 // object placement tags
2949 {"put", c_put, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2950 {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2951 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2952 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2953 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2954 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2955 {"del", c_del, "name"},
2956 // virtual object placement
2957 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2959 // commands which start a block
2960 //startclip (see above)
2961 {"sprite", c_sprite, "name"},
2962 {"action", c_action, "filename="},
2963 {"initaction", c_initaction, "name filename="},
2969 static map_t parseArguments(char*command, char*pattern)
2985 string_set(&t1, "commandname");
2986 string_set(&t2, command);
2987 map_put(&result, t1, t2);
2989 if(!pattern || !*pattern)
2996 if(!strncmp("<i> ", x, 3)) {
2998 if(type == COMMAND || type == RAWDATA) {
3000 syntaxerror("character name expected");
3002 name[pos].str = "instance";
3004 value[pos].str = text;
3005 value[pos].len = strlen(text);
3009 if(type == ASSIGNMENT)
3012 name[pos].str = "character";
3014 value[pos].str = text;
3015 value[pos].len = strlen(text);
3023 isboolean[pos] = (x[0] =='@');
3036 name[pos].len = d-x;
3041 name[pos].len = e-x;
3042 value[pos].str = e+1;
3043 value[pos].len = d-e-1;
3051 /* for(t=0;t<len;t++) {
3052 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3053 isboolean[t]?"(boolean)":"");
3058 if(type == RAWDATA || type == COMMAND) {
3063 // first, search for boolean arguments
3064 for(pos=0;pos<len;pos++)
3066 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3068 if(type == ASSIGNMENT)
3070 value[pos].str = text;
3071 value[pos].len = strlen(text);
3072 /*printf("setting boolean parameter %s (to %s)\n",
3073 strdup_n(name[pos], namelen[pos]),
3074 strdup_n(value[pos], valuelen[pos]));*/
3079 // second, search for normal arguments
3081 for(pos=0;pos<len;pos++)
3083 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3084 (type != ASSIGNMENT && !set[pos])) {
3086 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3088 if(type == ASSIGNMENT)
3091 value[pos].str = text;
3092 value[pos].len = strlen(text);
3094 printf("setting parameter %s (to %s)\n",
3095 strdup_n(name[pos].str, name[pos].len),
3096 strdup_n(value[pos].str, value[pos].len));
3102 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3106 for(t=0;t<len;t++) {
3107 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3110 for(t=0;t<len;t++) {
3111 if(value[t].str && value[t].str[0] == '*') {
3112 //relative default- take value from some other parameter
3114 for(s=0;s<len;s++) {
3115 if(value[s].len == value[t].len-1 &&
3116 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3117 value[t].str = value[s].str;
3120 if(value[t].str == 0) {
3122 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3126 /* ok, now construct the dictionary from the parameters */
3130 map_put(&result, name[t], value[t]);
3134 static void parseArgumentsForCommand(char*command)
3139 msg("<verbose> parse Command: %s (line %d)", command, line);
3141 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3142 if(!strcmp(arguments[t].command, command)) {
3144 /* ugly hack- will be removed soon (once documentation and .sc generating
3145 utilities have been changed) */
3146 if(!strcmp(command, "swf") && !stackpos) {
3147 warning("Please use .flash instead of .swf- this will be mandatory soon");
3152 args = parseArguments(command, arguments[t].arguments);
3158 syntaxerror("command %s not known", command);
3160 // catch missing .flash directives at the beginning of a file
3161 if(strcmp(command, "flash") && !stackpos)
3163 syntaxerror("No movie defined- use .flash first");
3167 printf(".%s\n", command);fflush(stdout);
3168 map_dump(&args, stdout, "\t");fflush(stdout);
3171 (*arguments[nr].func)(&args);
3173 /*if(!strcmp(command, "button") ||
3174 !strcmp(command, "action")) {
3177 if(type == COMMAND) {
3178 if(!strcmp(text, "end"))
3192 int main (int argc,char ** argv)
3195 processargs(argc, argv);
3196 initLog(0,-1,0,0,-1,verbose);
3199 args_callback_usage(argv[0]);
3203 file = generateTokens(filename);
3205 fprintf(stderr, "parser returned error.\n");
3211 while(!noMoreTokens()) {
3214 syntaxerror("command expected");
3215 parseArgumentsForCommand(text);