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[] = {
58 int args_callback_option(char*name,char*val)
60 if(!strcmp(name, "V")) {
61 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
64 else if(!strcmp(name, "o")) {
66 override_outputname = 1;
69 else if(!strcmp(name, "O")) {
73 else if(!strcmp(name, "C")) {
77 else if(!strcmp(name, "v")) {
82 printf("Unknown option: -%s\n", name);
87 int args_callback_longoption(char*name,char*val)
89 return args_long2shortoption(options, name, val);
91 void args_callback_usage(char *name)
94 printf("Usage: %s [-o file.swf] file.sc\n", name);
96 printf("-h , --help Print short help message and exit\n");
97 printf("-V , --version Print version info and exit\n");
98 printf("-C , --cgi Output to stdout (for use in CGI environments)\n");
99 printf("-v , --verbose Increase verbosity. \n");
100 printf("-o , --output <filename> Set output file to <filename>.\n");
103 int args_callback_command(char*name,char*val)
106 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
113 static struct token_t* file;
122 static void syntaxerror(char*format, ...)
126 va_start(arglist, format);
127 vsprintf(buf, format, arglist);
129 fprintf(stderr, "\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
133 static void warning(char*format, ...)
137 va_start(arglist, format);
138 vsprintf(buf, format, arglist);
140 fprintf(stderr, "\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
143 static void readToken()
145 type = file[pos].type;
147 syntaxerror("unexpected end of file");
149 text = file[pos].text;
150 textlen = strlen(text);
151 line = file[pos].line;
152 column = file[pos].column;
154 //printf("---> %d(%s) %s\n", type, type_names[type], text);
157 static void pushBack()
160 if(!pos) syntaxerror("internal error 3");
165 textlen = strlen(text);
168 column = file[p].column;
171 static int noMoreTokens()
173 if(file[pos].type == END)
178 // ------------------------------ swf routines ----------------------------
182 int type; //0=swf, 1=sprite, 2=clip, 3=button
188 /* for sprites (1): */
194 dictionary_t oldinstances;
199 static int stackpos = 0;
201 static dictionary_t characters;
202 static dictionary_t images;
203 static dictionary_t textures;
204 static dictionary_t outlines;
205 static dictionary_t gradients;
206 static char idmap[65536];
207 static TAG*tag = 0; //current tag
209 static int id; //current character id
210 static int currentframe; //current frame in current level
211 static SRECT currentrect; //current bounding box in current level
212 static U16 currentdepth;
213 static dictionary_t instances;
214 static dictionary_t fonts;
215 static dictionary_t sounds;
217 typedef struct _parameters {
219 float scalex, scaley;
225 U8 blendmode; //not interpolated
228 typedef struct _character {
234 typedef struct _instance {
235 character_t*character;
237 parameters_t parameters;
238 TAG* lastTag; //last tag which set the object
239 U16 lastFrame; //frame lastTag is in
242 typedef struct _outline {
247 typedef struct _gradient {
253 typedef struct _texture {
257 static void character_init(character_t*c)
259 memset(c, 0, sizeof(character_t));
261 static character_t* character_new()
264 c = (character_t*)malloc(sizeof(character_t));
268 static void instance_init(instance_t*i)
270 memset(i, 0, sizeof(instance_t));
272 static instance_t* instance_new()
275 c = (instance_t*)malloc(sizeof(instance_t));
280 static void incrementid()
284 syntaxerror("Out of character ids.");
289 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
291 character_t* c = character_new();
293 c->definingTag = ctag;
296 if(dictionary_lookup(&characters, name))
297 syntaxerror("character %s defined twice", name);
298 dictionary_put2(&characters, name, c);
300 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
302 swf_SetString(tag, name);
303 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
306 swf_SetString(tag, name);
308 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
310 character_t* c = character_new();
311 c->definingTag = ctag;
315 if(dictionary_lookup(&images, name))
316 syntaxerror("image %s defined twice", name);
317 dictionary_put2(&images, name, c);
319 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
321 instance_t* i = instance_new();
324 //swf_GetMatrix(0, &i->matrix);
325 if(dictionary_lookup(&instances, name))
326 syntaxerror("object %s defined twice", name);
327 dictionary_put2(&instances, name, i);
331 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)
334 p->scalex = scalex; p->scaley = scaley;
335 p->pin = pin; p->pivot = pivot;
336 p->rotate = rotate; p->cxform = cxform;
340 static void parameters_clear(parameters_t*p)
343 p->scalex = 1.0; p->scaley = 1.0;
346 p->pivot.x = 0; p->pivot.y = 0;
350 swf_GetCXForm(0, &p->cxform, 1);
353 static void makeMatrix(MATRIX*m, parameters_t*p)
362 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
363 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
364 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
365 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
367 m->sx = (int)(sx*65536+0.5);
368 m->r1 = (int)(r1*65536+0.5);
369 m->r0 = (int)(r0*65536+0.5);
370 m->sy = (int)(sy*65536+0.5);
374 h = swf_TurnPoint(p->pin, m);
379 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
384 r = swf_TurnRect(rect, &m);
385 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
386 currentrect.xmax == 0 && currentrect.ymax == 0)
389 swf_ExpandRect2(¤trect, &r);
393 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
395 SWF*swf = (SWF*)malloc(sizeof(SWF));
398 syntaxerror(".swf blocks can't be nested");
400 memset(swf, 0, sizeof(swf));
401 swf->fileVersion = version;
403 swf->frameRate = fps;
404 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
405 swf->compressed = compress;
406 swf_SetRGB(tag,&background);
408 if(stackpos==sizeof(stack)/sizeof(stack[0]))
409 syntaxerror("too many levels of recursion");
411 dictionary_init(&characters);
412 dictionary_init(&images);
413 dictionary_init(&textures);
414 dictionary_init(&outlines);
415 dictionary_init(&gradients);
416 dictionary_init(&instances);
417 dictionary_init(&fonts);
418 dictionary_init(&sounds);
420 memset(&stack[stackpos], 0, sizeof(stack[0]));
421 stack[stackpos].type = 0;
422 stack[stackpos].filename = strdup(name);
423 stack[stackpos].swf = swf;
424 stack[stackpos].oldframe = -1;
429 memset(¤trect, 0, sizeof(currentrect));
432 memset(idmap, 0, sizeof(idmap));
436 void s_sprite(char*name)
438 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
439 swf_SetU16(tag, id); //id
440 swf_SetU16(tag, 0); //frames
442 memset(&stack[stackpos], 0, sizeof(stack[0]));
443 stack[stackpos].type = 1;
444 stack[stackpos].oldframe = currentframe;
445 stack[stackpos].olddepth = currentdepth;
446 stack[stackpos].oldrect = currentrect;
447 stack[stackpos].oldinstances = instances;
448 stack[stackpos].tag = tag;
449 stack[stackpos].id = id;
450 stack[stackpos].name = strdup(name);
452 /* FIXME: those four fields should be bundled together */
453 dictionary_init(&instances);
456 memset(¤trect, 0, sizeof(currentrect));
462 typedef struct _buttonrecord
470 typedef struct _button
474 buttonrecord_t records[4];
477 static button_t mybutton;
479 void s_button(char*name)
481 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
482 swf_SetU16(tag, id); //id
483 swf_ButtonSetFlags(tag, 0); //menu=no
485 memset(&mybutton, 0, sizeof(mybutton));
487 memset(&stack[stackpos], 0, sizeof(stack[0]));
488 stack[stackpos].type = 3;
489 stack[stackpos].tag = tag;
490 stack[stackpos].id = id;
491 stack[stackpos].name = strdup(name);
492 stack[stackpos].oldrect = currentrect;
493 memset(¤trect, 0, sizeof(currentrect));
498 void s_buttonput(char*character, char*as, parameters_t p)
500 character_t* c = dictionary_lookup(&characters, character);
505 if(!stackpos || (stack[stackpos-1].type != 3)) {
506 syntaxerror(".show may only appear in .button");
509 syntaxerror("character %s not known (in .shape %s)", character, character);
511 if(mybutton.endofshapes) {
512 syntaxerror("a .do may not precede a .show", character, character);
515 m = s_instancepos(c->size, &p);
523 if(*s==',' || *s==0) {
524 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
525 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
526 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
527 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
528 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
529 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
536 static void setbuttonrecords(TAG*tag)
538 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
539 if(!mybutton.endofshapes) {
542 if(!mybutton.records[3].set) {
543 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
547 if(mybutton.records[t].set) {
548 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
551 swf_SetU8(tag,0); // end of button records
552 mybutton.endofshapes = 1;
556 void s_buttonaction(int flags, char*action)
562 setbuttonrecords(stack[stackpos-1].tag);
564 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
566 syntaxerror("Couldn't compile ActionScript");
569 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
570 swf_ActionSet(stack[stackpos-1].tag, a);
571 mybutton.nr_actions++;
576 static void setactionend(TAG*tag)
578 if(!mybutton.nr_actions) {
579 /* no actions means we didn't have an actionoffset,
580 which means we can't signal the end of the
581 buttonaction records, so, *sigh*, we have
582 to insert a dummy record */
583 swf_SetU16(tag, 0); //offset
584 swf_SetU16(tag, 0); //condition
585 swf_SetU8(tag, 0); //action
589 static void s_endButton()
592 setbuttonrecords(stack[stackpos-1].tag);
593 setactionend(stack[stackpos-1].tag);
596 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
600 tag = stack[stackpos].tag;
601 currentrect = stack[stackpos].oldrect;
603 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
604 free(stack[stackpos].name);
607 TAG* removeFromTo(TAG*from, TAG*to)
609 TAG*save = from->prev;
611 TAG*next = from->next;
619 static void s_endSprite()
621 SRECT r = currentrect;
623 if(stack[stackpos].cut)
624 tag = removeFromTo(stack[stackpos].cut, tag);
628 /* TODO: before clearing, prepend "<spritename>." to names and
629 copy into old instances dict */
630 dictionary_clear(&instances);
632 currentframe = stack[stackpos].oldframe;
633 currentrect = stack[stackpos].oldrect;
634 currentdepth = stack[stackpos].olddepth;
635 instances = stack[stackpos].oldinstances;
637 tag = swf_InsertTag(tag, ST_SHOWFRAME);
638 tag = swf_InsertTag(tag, ST_END);
640 tag = stack[stackpos].tag;
643 syntaxerror("internal error(7)");
645 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
646 free(stack[stackpos].name);
649 static void s_endSWF()
655 if(stack[stackpos].cut)
656 tag = removeFromTo(stack[stackpos].cut, tag);
660 swf = stack[stackpos].swf;
661 filename = stack[stackpos].filename;
663 //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
664 // tag = swf_InsertTag(tag, ST_SHOWFRAME);
665 tag = swf_InsertTag(tag, ST_SHOWFRAME);
667 tag = swf_InsertTag(tag, ST_END);
669 swf_OptimizeTagOrder(swf);
675 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
676 swf->movieSize = currentrect; /* "autocrop" */
679 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
680 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
681 swf->movieSize.ymax += 20;
682 warning("Empty bounding box for movie");
688 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
690 syntaxerror("couldn't create output file %s", filename);
693 {if(swf_WriteCGI(swf)<0) syntaxerror("WriteCGI() failed.\n");}
694 else if(swf->compressed)
695 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
697 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
701 dictionary_clear(&instances);
702 dictionary_clear(&characters);
703 dictionary_clear(&images);
704 dictionary_clear(&textures);
705 dictionary_clear(&outlines);
706 dictionary_clear(&gradients);
707 dictionary_clear(&fonts);
708 dictionary_clear(&sounds);
718 if(stack[stackpos-1].type == 0)
719 syntaxerror("End of file encountered in .flash block");
720 if(stack[stackpos-1].type == 1)
721 syntaxerror("End of file encountered in .sprite block");
722 if(stack[stackpos-1].type == 2)
723 syntaxerror("End of file encountered in .clip block");
729 return currentframe+1;
732 void s_frame(int nr, int cut, char*name, char anchor)
738 syntaxerror("Illegal frame number");
739 nr--; // internally, frame 1 is frame 0
741 for(t=currentframe;t<nr;t++) {
742 tag = swf_InsertTag(tag, ST_SHOWFRAME);
743 if(t==nr-1 && name && *name) {
744 tag = swf_InsertTag(tag, ST_FRAMELABEL);
745 swf_SetString(tag, name);
747 swf_SetU8(tag, 1); //make this an anchor
750 if(nr == 0 && currentframe == 0 && name && *name) {
751 tag = swf_InsertTag(tag, ST_FRAMELABEL);
752 swf_SetString(tag, name);
754 swf_SetU8(tag, 1); //make this an anchor
759 syntaxerror("Can't cut, frame empty");
761 stack[stackpos].cut = tag;
767 int parseColor2(char*str, RGBA*color);
769 int addFillStyle(SHAPE*s, SRECT*r, char*name)
776 parseColor2(name, &color);
777 return swf_ShapeAddSolidFillStyle(s, &color);
778 } else if ((texture = dictionary_lookup(&textures, name))) {
779 return swf_ShapeAddFillStyle2(s, &texture->fs);
780 } else if((image = dictionary_lookup(&images, name))) {
782 swf_GetMatrix(0, &m);
783 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
784 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
787 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
788 } else if ((gradient = dictionary_lookup(&gradients, name))) {
792 swf_GetMatrix(0, &rot);
793 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
794 csin = sin(-gradient->rotate*2*3.14159265358979/360);
796 rot.r1 = -csin*65536;
799 r2 = swf_TurnRect(*r, &rot);
800 swf_GetMatrix(0, &m);
801 m.sx = (r2.xmax - r2.xmin)*2*ccos;
802 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
803 m.r0 = (r2.ymax - r2.ymin)*2*csin;
804 m.sy = (r2.ymax - r2.ymin)*2*ccos;
805 m.tx = r->xmin + (r->xmax - r->xmin)/2;
806 m.ty = r->ymin + (r->ymax - r->ymin)/2;
807 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
808 } else if (parseColor2(name, &color)) {
809 return swf_ShapeAddSolidFillStyle(s, &color);
811 syntaxerror("not a color/fillstyle: %s", name);
816 RGBA black={r:0,g:0,b:0,a:0};
817 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
826 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
829 linewidth = linewidth>=20?linewidth-20:0;
830 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
833 fs1 = addFillStyle(s, &r2, texture);
836 r.xmin = r2.xmin-linewidth/2;
837 r.ymin = r2.ymin-linewidth/2;
838 r.xmax = r2.xmax+linewidth/2;
839 r.ymax = r2.ymax+linewidth/2;
841 swf_SetShapeHeader(tag,s);
842 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
843 swf_ShapeSetLine(tag,s,width,0);
844 swf_ShapeSetLine(tag,s,0,height);
845 swf_ShapeSetLine(tag,s,-width,0);
846 swf_ShapeSetLine(tag,s,0,-height);
847 swf_ShapeSetEnd(tag);
850 s_addcharacter(name, id, tag, r);
854 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
860 outline = dictionary_lookup(&outlines, outlinename);
862 syntaxerror("outline %s not defined", outlinename);
866 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
869 linewidth = linewidth>=20?linewidth-20:0;
870 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
873 fs1 = addFillStyle(s, &r2, texture);
876 rect.xmin = r2.xmin-linewidth/2;
877 rect.ymin = r2.ymin-linewidth/2;
878 rect.xmax = r2.xmax+linewidth/2;
879 rect.ymax = r2.ymax+linewidth/2;
881 swf_SetRect(tag,&rect);
882 swf_SetShapeStyles(tag, s);
883 swf_ShapeCountBits(s,0,0);
884 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
885 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
886 swf_SetShapeBits(tag, s);
887 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
890 s_addcharacter(name, id, tag, rect);
894 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
899 r2.xmin = r2.ymin = 0;
903 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
906 linewidth = linewidth>=20?linewidth-20:0;
907 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
910 fs1 = addFillStyle(s, &r2, texture);
912 rect.xmin = r2.xmin-linewidth/2;
913 rect.ymin = r2.ymin-linewidth/2;
914 rect.xmax = r2.xmax+linewidth/2;
915 rect.ymax = r2.ymax+linewidth/2;
917 swf_SetRect(tag,&rect);
918 swf_SetShapeHeader(tag,s);
919 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
920 swf_ShapeSetCircle(tag, s, r,r,r,r);
921 swf_ShapeSetEnd(tag);
924 s_addcharacter(name, id, tag, rect);
928 void s_textshape(char*name, char*fontname, float size, char*_text)
931 U8*text = (U8*)_text;
935 font = dictionary_lookup(&fonts, fontname);
937 syntaxerror("font \"%s\" not known!", fontname);
939 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
940 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
941 s_box(name, 0, 0, black, 20, 0);
944 g = font->ascii2glyph[text[0]];
946 outline = malloc(sizeof(outline_t));
947 memset(outline, 0, sizeof(outline_t));
948 outline->shape = font->glyph[g].shape;
949 outline->bbox = font->layout->bounds[g];
953 swf_Shape11DrawerInit(&draw, 0);
954 swf_DrawText(&draw, font, (int)(size*100), _text);
956 outline->shape = swf_ShapeDrawerToShape(&draw);
957 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
961 if(dictionary_lookup(&outlines, name))
962 syntaxerror("outline %s defined twice", name);
963 dictionary_put2(&outlines, name, outline);
966 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
971 font = dictionary_lookup(&fonts, fontname);
973 syntaxerror("font \"%s\" not known!", fontname);
975 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
977 if(!font->numchars) {
978 s_box(name, 0, 0, black, 20, 0);
981 r = swf_SetDefineText(tag, font, &color, text, size);
983 s_addcharacter(name, id, tag, r);
987 void s_quicktime(char*name, char*url)
992 memset(&r, 0, sizeof(r));
994 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
996 swf_SetString(tag, url);
998 s_addcharacter(name, id, tag, r);
1002 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)
1005 EditTextLayout layout;
1008 if(fontname && *fontname) {
1009 flags |= ET_USEOUTLINES;
1010 font = dictionary_lookup(&fonts, fontname);
1012 syntaxerror("font \"%s\" not known!", fontname);
1014 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
1015 swf_SetU16(tag, id);
1016 layout.align = align;
1017 layout.leftmargin = 0;
1018 layout.rightmargin = 0;
1026 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1028 s_addcharacter(name, id, tag, r);
1032 /* type: either "jpeg" or "png"
1034 void s_image(char*name, char*type, char*filename, int quality)
1036 /* an image is actually two folded: 1st bitmap, 2nd character.
1037 Both of them can be used separately */
1039 /* step 1: the bitmap */
1043 if(!strcmp(type,"jpeg")) {
1044 #ifndef HAVE_JPEGLIB
1045 warning("no jpeg support compiled in");
1046 s_box(name, 0, 0, black, 20, 0);
1049 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1050 swf_SetU16(tag, imageID);
1052 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1053 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1056 swf_GetJPEGSize(filename, &width, &height);
1063 s_addimage(name, id, tag, r);
1066 } else if(!strcmp(type,"png")) {
1068 swf_SetU16(tag, imageID);
1070 getPNG(filename, &width, &height, (unsigned char**)&data);
1073 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1076 /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
1077 tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
1078 swf_SetU16(tag, imageID);
1079 swf_SetLosslessImage(tag, data, width, height);
1085 s_addimage(name, id, tag, r);
1088 warning("image type \"%s\" not supported yet!", type);
1089 s_box(name, 0, 0, black, 20, 0);
1093 /* step 2: the character */
1094 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1095 swf_SetU16(tag, id);
1096 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1098 s_addcharacter(name, id, tag, r);
1102 void s_getBitmapSize(char*name, int*width, int*height)
1104 character_t* image = dictionary_lookup(&images, name);
1105 gradient_t* gradient = dictionary_lookup(&gradients,name);
1107 *width = image->size.xmax;
1108 *height = image->size.ymax;
1112 /* internal SWF gradient size */
1113 if(gradient->radial) {
1122 syntaxerror("No such bitmap/gradient: %s", name);
1125 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1127 gradient_t* gradient = dictionary_lookup(&gradients, object);
1128 character_t* bitmap = dictionary_lookup(&images, object);
1129 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1131 FILLSTYLE*fs = &texture->fs;
1133 memset(&p, 0, sizeof(parameters_t));
1136 fs->type = FILL_TILED;
1137 fs->id_bitmap = bitmap->id;
1138 } else if(gradient) {
1139 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1140 fs->gradient = gradient->gradient;
1142 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1143 makeMatrix(&fs->m, &p);
1144 if(gradient && !gradient->radial) {
1151 p2 = swf_TurnPoint(p1, &m);
1156 if(dictionary_lookup(&textures, name))
1157 syntaxerror("texture %s defined twice", name);
1158 dictionary_put2(&textures, name, texture);
1161 void dumpSWF(SWF*swf)
1163 TAG* tag = swf->firstTag;
1164 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1166 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1169 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1172 void s_font(char*name, char*filename)
1175 font = swf_LoadFont(filename);
1178 warning("Couldn't open font file \"%s\"", filename);
1179 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1180 memset(font, 0, sizeof(SWFFONT));
1181 dictionary_put2(&fonts, name, font);
1187 /* fix the layout. Only needed for old fonts */
1189 for(t=0;t<font->numchars;t++) {
1190 font->glyph[t].advance = 0;
1193 swf_FontCreateLayout(font);
1195 /* just in case this thing is used in .edittext later on */
1196 swf_FontPrepareForEditText(font);
1199 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1200 swf_FontSetDefine2(tag, font);
1201 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1203 swf_SetU16(tag, id);
1204 swf_SetString(tag, name);
1207 if(dictionary_lookup(&fonts, name))
1208 syntaxerror("font %s defined twice", name);
1209 dictionary_put2(&fonts, name, font);
1214 typedef struct _sound_t
1220 void s_sound(char*name, char*filename)
1222 struct WAV wav, wav2;
1226 unsigned numsamples;
1227 unsigned blocksize = 1152;
1230 if(wav_read(&wav, filename)) {
1232 wav_convert2mono(&wav, &wav2, 44100);
1233 samples = (U16*)wav2.data;
1234 numsamples = wav2.size/2;
1236 #ifdef WORDS_BIGENDIAN
1238 for(t=0;t<numsamples;t++) {
1239 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1242 } else if(mp3_read(&mp3, filename)) {
1243 fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1249 warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1254 if(numsamples%blocksize != 0)
1256 // apply padding, so that block is a multiple of blocksize
1257 int numblocks = (numsamples+blocksize-1)/blocksize;
1260 numsamples2 = numblocks * blocksize;
1261 samples2 = malloc(sizeof(U16)*numsamples2);
1262 memcpy(samples2, samples, numsamples*sizeof(U16));
1263 memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1264 numsamples = numsamples2;
1268 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1269 swf_SetU16(tag, id); //id
1272 swf_SetSoundDefineMP3(
1273 tag, mp3.data, mp3.size,
1281 swf_SetSoundDefine(tag, samples, numsamples);
1284 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1285 swf_SetU16(tag, id);
1286 swf_SetString(tag, name);
1287 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1289 swf_SetU16(tag, id);
1290 swf_SetString(tag, name);
1292 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1296 if(dictionary_lookup(&sounds, name))
1297 syntaxerror("sound %s defined twice", name);
1298 dictionary_put2(&sounds, name, sound);
1306 static char* gradient_getToken(const char**p)
1310 while(**p && strchr(" \t\n\r", **p)) {
1314 while(**p && !strchr(" \t\n\r", **p)) {
1317 result = malloc((*p)-start+1);
1318 memcpy(result,start,(*p)-start+1);
1319 result[(*p)-start] = 0;
1323 float parsePercent(char*str);
1324 RGBA parseColor(char*str);
1326 GRADIENT parseGradient(const char*str)
1330 const char* p = str;
1331 memset(&gradient, 0, sizeof(GRADIENT));
1333 char*posstr,*colorstr;
1336 posstr = gradient_getToken(&p);
1339 pos = (int)(parsePercent(posstr)*255.0);
1342 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1343 colorstr = gradient_getToken(&p);
1344 color = parseColor(colorstr);
1345 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1346 warning("gradient record too big- max size is 8, rest ignored");
1349 gradient.ratios[gradient.num] = pos;
1350 gradient.rgba[gradient.num] = color;
1359 void s_gradient(char*name, const char*text, int radial, int rotate)
1361 gradient_t* gradient;
1362 gradient = malloc(sizeof(gradient_t));
1363 memset(gradient, 0, sizeof(gradient_t));
1364 gradient->gradient = parseGradient(text);
1365 gradient->radial = radial;
1366 gradient->rotate = rotate;
1368 if(dictionary_lookup(&gradients, name))
1369 syntaxerror("gradient %s defined twice", name);
1370 dictionary_put2(&gradients, name, gradient);
1373 void s_action(const char*text)
1376 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1378 syntaxerror("Couldn't compile ActionScript");
1381 tag = swf_InsertTag(tag, ST_DOACTION);
1383 swf_ActionSet(tag, a);
1388 void s_initaction(const char*character, const char*text)
1392 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1394 syntaxerror("Couldn't compile ActionScript");
1397 c = (character_t*)dictionary_lookup(&characters, character);
1399 tag = swf_InsertTag(tag, ST_DOINITACTION);
1400 swf_SetU16(tag, c->id);
1401 swf_ActionSet(tag, a);
1406 int s_swf3action(char*name, char*action)
1409 instance_t* object = 0;
1411 object = (instance_t*)dictionary_lookup(&instances, name);
1412 if(!object && name && *name) {
1413 /* we have a name, but couldn't find it. Abort. */
1416 a = action_SetTarget(0, name);
1417 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1418 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1419 else if(!strcmp(action, "stop")) a = action_Stop(a);
1420 else if(!strcmp(action, "play")) a = action_Play(a);
1421 a = action_SetTarget(a, "");
1424 tag = swf_InsertTag(tag, ST_DOACTION);
1425 swf_ActionSet(tag, a);
1430 void s_outline(char*name, char*format, char*source)
1439 //swf_Shape10DrawerInit(&draw, 0);
1440 swf_Shape11DrawerInit(&draw, 0);
1442 draw_string(&draw, source);
1444 shape = swf_ShapeDrawerToShape(&draw);
1445 bounds = swf_ShapeDrawerGetBBox(&draw);
1446 draw.dealloc(&draw);
1448 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1449 outline->shape = shape;
1450 outline->bbox = bounds;
1452 if(dictionary_lookup(&outlines, name))
1453 syntaxerror("outline %s defined twice", name);
1454 dictionary_put2(&outlines, name, outline);
1457 int s_playsound(char*name, int loops, int nomultiple, int stop)
1463 sound = dictionary_lookup(&sounds, name);
1467 tag = swf_InsertTag(tag, ST_STARTSOUND);
1468 swf_SetU16(tag, sound->id); //id
1469 memset(&info, 0, sizeof(info));
1472 info.nomultiple = nomultiple;
1473 swf_SetSoundInfo(tag, &info);
1477 void s_includeswf(char*name, char*filename)
1485 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1486 f = open(filename,O_RDONLY|O_BINARY);
1488 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1489 s_box(name, 0, 0, black, 20, 0);
1492 if (swf_ReadSWF(f,&swf)<0) {
1493 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1494 s_box(name, 0, 0, black, 20, 0);
1499 /* FIXME: The following sets the bounding Box for the character.
1500 It is wrong for two reasons:
1501 a) It may be too small (in case objects in the movie clip at the borders)
1502 b) it may be too big (because the poor movie never got autocropped)
1506 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1507 swf_SetU16(tag, id);
1508 swf_SetU16(tag, swf.frameCount);
1510 swf_Relocate(&swf, idmap);
1512 ftag = swf.firstTag;
1516 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1517 if(cutout[t] == ftag->id) {
1521 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1523 if(ftag->id == ST_END)
1528 if(ftag->id != ST_SETBACKGROUNDCOLOR) {
1529 /* We simply dump all tags right after the sprite
1530 header, relying on the fact that swf_OptimizeTagOrder() will
1531 sort things out for us later.
1532 We also rely on the fact that the imported SWF is well-formed.
1534 tag = swf_InsertTag(tag, ftag->id);
1535 swf_SetBlock(tag, ftag->data, ftag->len);
1541 syntaxerror("Included file %s contains errors", filename);
1542 tag = swf_InsertTag(tag, ST_END);
1546 s_addcharacter(name, id, tag, r);
1549 SRECT s_getCharBBox(char*name)
1551 character_t* c = dictionary_lookup(&characters, name);
1552 if(!c) syntaxerror("character '%s' unknown(2)", name);
1555 SRECT s_getInstanceBBox(char*name)
1557 instance_t * i = dictionary_lookup(&instances, name);
1559 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1561 if(!c) syntaxerror("internal error(5)");
1564 parameters_t s_getParameters(char*name)
1566 instance_t * i = dictionary_lookup(&instances, name);
1567 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1568 return i->parameters;
1570 void s_startclip(char*instance, char*character, parameters_t p)
1572 character_t* c = dictionary_lookup(&characters, character);
1576 syntaxerror("character %s not known", character);
1578 i = s_addinstance(instance, c, currentdepth);
1580 m = s_instancepos(i->character->size, &p);
1582 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1583 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1584 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1586 i->lastFrame= currentframe;
1588 stack[stackpos].tag = tag;
1589 stack[stackpos].type = 2;
1598 swf_SetTagPos(stack[stackpos].tag, 0);
1599 swf_GetPlaceObject(stack[stackpos].tag, &p);
1600 p.clipdepth = currentdepth;
1602 swf_ClearTag(stack[stackpos].tag);
1603 swf_SetPlaceObject(stack[stackpos].tag, &p);
1607 void s_put(char*instance, char*character, parameters_t p)
1609 character_t* c = dictionary_lookup(&characters, character);
1613 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1616 i = s_addinstance(instance, c, currentdepth);
1618 m = s_instancepos(i->character->size, &p);
1621 tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
1622 swf_ObjectPlaceBlend(tag, c->id, currentdepth, &m, &p.cxform, instance, p.blendmode);
1624 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1625 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1629 i->lastFrame = currentframe;
1633 void s_jump(char*instance, parameters_t p)
1635 instance_t* i = dictionary_lookup(&instances, instance);
1638 syntaxerror("instance %s not known", instance);
1642 m = s_instancepos(i->character->size, &p);
1644 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1645 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1647 i->lastFrame = currentframe;
1650 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1654 if(num==0 || num==1)
1656 ratio = (float)pos/(float)num;
1658 p.x = (p2->x-p1->x)*ratio + p1->x;
1659 p.y = (p2->y-p1->y)*ratio + p1->y;
1660 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1661 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1662 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1663 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1665 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1666 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1667 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1668 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1670 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1671 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1672 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1673 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1675 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1676 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1677 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1678 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1682 void s_change(char*instance, parameters_t p2)
1684 instance_t* i = dictionary_lookup(&instances, instance);
1688 int frame, allframes;
1690 syntaxerror("instance %s not known", instance);
1694 allframes = currentframe - i->lastFrame - 1;
1696 warning(".change ignored. can only .put/.change an object once per frame.");
1700 m = s_instancepos(i->character->size, &p2);
1701 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1702 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1705 /* o.k., we got the start and end point set. Now iterate though all the
1706 tags in between, inserting object changes after each new frame */
1709 if(!t) syntaxerror("internal error(6)");
1711 while(frame < allframes) {
1712 if(t->id == ST_SHOWFRAME) {
1717 p = s_interpolate(&p1, &p2, frame, allframes);
1718 m = s_instancepos(i->character->size, &p); //needed?
1719 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1720 i->lastFrame = currentframe;
1721 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1723 if(frame == allframes)
1728 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1732 void s_delinstance(char*instance)
1734 instance_t* i = dictionary_lookup(&instances, instance);
1736 syntaxerror("instance %s not known", instance);
1738 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1739 swf_SetU16(tag, i->depth);
1740 dictionary_del(&instances, instance);
1743 void s_qchange(char*instance, parameters_t p)
1750 syntaxerror(".end unexpected");
1751 if(stack[stackpos-1].type == 0)
1753 else if(stack[stackpos-1].type == 1)
1755 else if(stack[stackpos-1].type == 2)
1757 else if(stack[stackpos-1].type == 3)
1759 else syntaxerror("internal error 1");
1762 // ------------------------------------------------------------------------
1764 typedef int command_func_t(map_t*args);
1766 SRECT parseBox(char*str)
1768 SRECT r = {0,0,0,0};
1769 float xmin, xmax, ymin, ymax;
1770 char*x = strchr(str, 'x');
1772 if(!strcmp(str, "autocrop")) {
1773 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1777 d1 = strchr(x+1, ':');
1779 d2 = strchr(d1+1, ':');
1781 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1785 else if(d1 && !d2) {
1786 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1792 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1797 r.xmin = (SCOORD)(xmin*20);
1798 r.ymin = (SCOORD)(ymin*20);
1799 r.xmax = (SCOORD)(xmax*20);
1800 r.ymax = (SCOORD)(ymax*20);
1803 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1806 float parseFloat(char*str)
1810 int parseInt(char*str)
1815 if(str[0]=='+' || str[0]=='-')
1819 if(str[t]<'0' || str[t]>'9')
1820 syntaxerror("Not an Integer: \"%s\"", str);
1823 int parseTwip(char*str)
1827 if(str[0]=='+' || str[0]=='-') {
1832 dot = strchr(str, '.');
1836 return sign*parseInt(str)*20;
1838 char* old = strdup(str);
1839 int l=strlen(dot+1);
1842 for(s=str;s<dot-1;s++)
1843 if(*s<'0' || *s>'9')
1844 syntaxerror("Not a coordinate: \"%s\"", str);
1846 if(*s<'0' || *s>'9')
1847 syntaxerror("Not a coordinate: \"%s\"", str);
1849 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1850 dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
1853 warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
1857 return sign*atoi(str)*20;
1859 return sign*atoi(str)*20+atoi(dot)*2;
1861 return sign*atoi(str)*20+atoi(dot)/5;
1866 int isPoint(char*str)
1868 if(strchr(str, '('))
1874 SPOINT parsePoint(char*str)
1878 int l = strlen(str);
1879 char*comma = strchr(str, ',');
1880 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1881 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1882 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1883 p.x = parseTwip(tmp);
1884 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1885 p.y = parseTwip(tmp);
1889 int parseColor2(char*str, RGBA*color)
1891 int l = strlen(str);
1895 struct {unsigned char r,g,b;char*name;} colors[] =
1896 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1897 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1898 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1899 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1900 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1901 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1902 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1903 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1904 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1905 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1906 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1907 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1911 if(str[0]=='#' && (l==7 || l==9)) {
1912 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1914 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1916 color->r = r; color->g = g; color->b = b; color->a = a;
1919 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1920 if(!strcmp(str, colors[t].name)) {
1925 color->r = r; color->g = g; color->b = b; color->a = a;
1931 RGBA parseColor(char*str)
1934 if(!parseColor2(str, &c))
1935 syntaxerror("Expression '%s' is not a color", str);
1939 typedef struct _muladd {
1944 MULADD parseMulAdd(char*str)
1947 char* str2 = (char*)malloc(strlen(str)+5);
1954 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1955 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1956 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1957 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1958 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1959 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1960 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1961 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1962 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1963 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1965 syntaxerror("'%s' is not a valid color transform expression", str);
1967 m.add = (int)(add*256);
1968 m.mul = (int)(mul*256);
1973 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1975 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1976 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1978 if(a<-32768) a=-32768;
1979 if(a>32767) a=32767;
1980 if(m<-32768) m=-32768;
1981 if(m>32767) m=32767;
1987 float parsePxOrPercent(char*fontname, char*str)
1989 int l = strlen(str);
1990 if(strchr(str, '%'))
1991 return parsePercent(str);
1992 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1993 float p = atof(str);
1994 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1996 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
2000 float parsePercent(char*str)
2002 int l = strlen(str);
2006 return atoi(str)/100.0;
2008 syntaxerror("Expression '%s' is not a percentage", str);
2011 int isPercent(char*str)
2013 return str[strlen(str)-1]=='%';
2015 int parseNewSize(char*str, int size)
2018 return parsePercent(str)*size;
2020 return (int)(atof(str)*20);
2023 int isColor(char*str)
2026 return parseColor2(str, &c);
2029 static char* lu(map_t* args, char*name)
2031 char* value = map_lookup(args, name);
2033 map_dump(args, stdout, "");
2034 syntaxerror("internal error 2: value %s should be set", name);
2039 static int c_flash(map_t*args)
2041 char* filename = map_lookup(args, "filename");
2042 char* compressstr = lu(args, "compress");
2043 SRECT bbox = parseBox(lu(args, "bbox"));
2044 int version = parseInt(lu(args, "version"));
2045 int fps = (int)(parseFloat(lu(args, "fps"))*256);
2047 RGBA color = parseColor(lu(args, "background"));
2049 if(!filename || !*filename) {
2050 /* for compatibility */
2051 filename = map_lookup(args, "name");
2052 if(!filename || !*filename) {
2055 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2056 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2060 if(!filename || override_outputname)
2061 filename = outputname;
2063 if(!strcmp(compressstr, "default"))
2064 compress = version==6;
2065 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2067 else if(!strcmp(compressstr, "no"))
2069 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2071 s_swf(filename, bbox, version, fps, compress, color);
2074 int isRelative(char*str)
2076 return !strncmp(str, "<plus>", 6) ||
2077 !strncmp(str, "<minus>", 7);
2079 char* getOffset(char*str)
2081 if(!strncmp(str, "<plus>", 6))
2083 if(!strncmp(str, "<minus>", 7))
2085 syntaxerror("internal error (347)");
2088 int getSign(char*str)
2090 if(!strncmp(str, "<plus>", 6))
2092 if(!strncmp(str, "<minus>", 7))
2094 syntaxerror("internal error (348)");
2097 static dictionary_t points;
2098 static mem_t mpoints;
2099 int points_initialized = 0;
2101 SPOINT getPoint(SRECT r, char*name)
2104 if(!strcmp(name, "center")) {
2106 p.x = (r.xmin + r.xmax)/2;
2107 p.y = (r.ymin + r.ymax)/2;
2111 if(points_initialized)
2112 l = (int)dictionary_lookup(&points, name);
2114 syntaxerror("Invalid point: \"%s\".", name);
2117 return *(SPOINT*)&mpoints.buffer[l];
2120 static int texture2(char*name, char*object, map_t*args, int errors)
2123 char*xstr = map_lookup(args, "x");
2124 char*ystr = map_lookup(args, "y");
2125 char*widthstr = map_lookup(args, "width");
2126 char*heightstr = map_lookup(args, "height");
2127 char*scalestr = map_lookup(args, "scale");
2128 char*scalexstr = map_lookup(args, "scalex");
2129 char*scaleystr = map_lookup(args, "scaley");
2130 char*rotatestr = map_lookup(args, "rotate");
2131 char* shearstr = map_lookup(args, "shear");
2132 char* radiusstr = map_lookup(args, "r");
2134 float scalex = 1.0, scaley = 1.0;
2135 float rotate=0, shear=0;
2137 if(!*xstr && !*ystr) {
2139 syntaxerror("x and y must be set");
2142 if(*scalestr && (*scalexstr || *scaleystr)) {
2143 syntaxerror("scale and scalex/scaley can't both be set");
2146 if((*widthstr || *heightstr) && *radiusstr) {
2147 syntaxerror("width/height and radius can't both be set");
2150 widthstr = radiusstr;
2151 heightstr = radiusstr;
2153 if(!*xstr) xstr="0";
2154 if(!*ystr) ystr="0";
2155 if(!*rotatestr) rotatestr="0";
2156 if(!*shearstr) shearstr="0";
2159 scalex = scaley = parsePercent(scalestr);
2160 } else if(*scalexstr || *scaleystr) {
2161 if(scalexstr) scalex = parsePercent(scalexstr);
2162 if(scaleystr) scaley = parsePercent(scaleystr);
2163 } else if(*widthstr || *heightstr) {
2166 s_getBitmapSize(object, &width, &height);
2168 scalex = (float)parseTwip(widthstr)/(float)width;
2170 scaley = (float)parseTwip(heightstr)/(float)height;
2172 x = parseTwip(xstr);
2173 y = parseTwip(ystr);
2174 rotate = parseFloat(rotatestr);
2175 shear = parseFloat(shearstr);
2177 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2182 static int c_texture(map_t*args)
2184 char*name = lu(args, "instance");
2185 char*object = lu(args, "character");
2186 return texture2(name, object, args, 1);
2189 static int c_gradient(map_t*args)
2191 char*name = lu(args, "name");
2192 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2193 int rotate = parseInt(lu(args, "rotate"));
2197 syntaxerror("colon (:) expected");
2199 s_gradient(name, text, radial, rotate);
2201 /* check whether we also have placement information,
2202 which would make this a positioned gradient.
2203 If there is placement information, texture2() will
2204 add a texture, which has priority over the gradient.
2206 texture2(name, name, args, 0);
2209 static int c_point(map_t*args)
2211 char*name = lu(args, "name");
2215 if(!points_initialized) {
2216 dictionary_init(&points);
2218 points_initialized = 1;
2220 p.x = parseTwip(lu(args, "x"));
2221 p.y = parseTwip(lu(args, "y"));
2222 pos = mem_put(&mpoints, &p, sizeof(p));
2223 string_set(&s1, name);
2225 dictionary_put(&points, s1, (void*)pos);
2228 static int c_play(map_t*args)
2230 char*name = lu(args, "name");
2231 char*loop = lu(args, "loop");
2232 char*nomultiple = lu(args, "nomultiple");
2234 if(!strcmp(nomultiple, "nomultiple"))
2237 nm = parseInt(nomultiple);
2239 if(s_playsound(name, parseInt(loop), nm, 0)) {
2241 } else if(s_swf3action(name, "play")) {
2247 static int c_stop(map_t*args)
2249 char*name = map_lookup(args, "name");
2251 if(s_playsound(name, 0,0,1)) {
2253 } else if(s_swf3action(name, "stop")) {
2256 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2260 static int c_nextframe(map_t*args)
2262 char*name = lu(args, "name");
2264 if(s_swf3action(name, "nextframe")) {
2267 syntaxerror("I don't know anything about movie \"%s\"", name);
2271 static int c_previousframe(map_t*args)
2273 char*name = lu(args, "name");
2275 if(s_swf3action(name, "previousframe")) {
2278 syntaxerror("I don't know anything about movie \"%s\"", name);
2282 static int c_placement(map_t*args, int type)
2284 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2287 char* luminancestr = lu(args, "luminance");
2288 char* scalestr = lu(args, "scale");
2289 char* scalexstr = lu(args, "scalex");
2290 char* scaleystr = lu(args, "scaley");
2291 char* rotatestr = lu(args, "rotate");
2292 char* shearstr = lu(args, "shear");
2293 char* xstr="", *pivotstr="";
2294 char* ystr="", *anglestr="";
2295 char*above = lu(args, "above"); /*FIXME*/
2296 char*below = lu(args, "below");
2297 char* rstr = lu(args, "red");
2298 char* gstr = lu(args, "green");
2299 char* bstr = lu(args, "blue");
2300 char* astr = lu(args, "alpha");
2301 char* pinstr = lu(args, "pin");
2302 char* as = map_lookup(args, "as");
2303 char* blendmode = lu(args, "blend");
2312 if(type==9) { // (?) .rotate or .arcchange
2313 pivotstr = lu(args, "pivot");
2314 anglestr = lu(args, "angle");
2316 xstr = lu(args, "x");
2317 ystr = lu(args, "y");
2320 luminance = parseMulAdd(luminancestr);
2323 luminance.mul = 256;
2327 if(scalexstr[0]||scaleystr[0])
2328 syntaxerror("scalex/scaley and scale cannot both be set");
2329 scalexstr = scaleystr = scalestr;
2332 if(type == 0 || type == 4) {
2334 character = lu(args, "character");
2335 parameters_clear(&p);
2336 } else if (type == 5) {
2337 character = lu(args, "name");
2338 parameters_clear(&p);
2341 p = s_getParameters(instance);
2346 if(isRelative(xstr)) {
2347 if(type == 0 || type == 4)
2348 syntaxerror("relative x values not allowed for initial put or startclip");
2349 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2351 p.x = parseTwip(xstr);
2355 if(isRelative(ystr)) {
2356 if(type == 0 || type == 4)
2357 syntaxerror("relative y values not allowed for initial put or startclip");
2358 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2360 p.y = parseTwip(ystr);
2364 /* scale, scalex, scaley */
2366 oldbbox = s_getCharBBox(character);
2368 oldbbox = s_getInstanceBBox(instance);
2370 oldwidth = oldbbox.xmax - oldbbox.xmin;
2371 oldheight = oldbbox.ymax - oldbbox.ymin;
2373 if(oldwidth==0) p.scalex = 1.0;
2376 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2380 if(oldheight==0) p.scaley = 1.0;
2383 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2389 if(isRelative(rotatestr)) {
2390 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2392 p.rotate = parseFloat(rotatestr);
2398 if(isRelative(shearstr)) {
2399 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2401 p.shear = parseFloat(shearstr);
2406 if(isPoint(pivotstr))
2407 p.pivot = parsePoint(pivotstr);
2409 p.pivot = getPoint(oldbbox, pivotstr);
2413 p.pin = parsePoint(pinstr);
2415 p.pin = getPoint(oldbbox, pinstr);
2418 /* color transform */
2420 if(rstr[0] || luminancestr[0]) {
2423 r = parseMulAdd(rstr);
2425 r.add = p.cxform.r0;
2426 r.mul = p.cxform.r1;
2428 r = mergeMulAdd(r, luminance);
2429 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2431 if(gstr[0] || luminancestr[0]) {
2434 g = parseMulAdd(gstr);
2436 g.add = p.cxform.g0;
2437 g.mul = p.cxform.g1;
2439 g = mergeMulAdd(g, luminance);
2440 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2442 if(bstr[0] || luminancestr[0]) {
2445 b = parseMulAdd(bstr);
2447 b.add = p.cxform.b0;
2448 b.mul = p.cxform.b1;
2450 b = mergeMulAdd(b, luminance);
2451 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2454 MULADD a = parseMulAdd(astr);
2455 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2461 for(t=0;blendModeNames[t];t++) {
2462 if(!strcmp(blendModeNames[t], blendmode)) {
2468 syntaxerror("unknown blend mode: '%s'", blendmode);
2470 p.blendmode = blend;
2474 s_put(instance, character, p);
2476 s_change(instance, p);
2478 s_qchange(instance, p);
2480 s_jump(instance, p);
2482 s_startclip(instance, character, p);
2483 else if(type == 5) {
2485 s_buttonput(character, as, p);
2487 s_buttonput(character, "shape", p);
2492 static int c_put(map_t*args)
2494 c_placement(args, 0);
2497 static int c_change(map_t*args)
2499 c_placement(args, 1);
2502 static int c_qchange(map_t*args)
2504 c_placement(args, 2);
2507 static int c_arcchange(map_t*args)
2509 c_placement(args, 2);
2512 static int c_jump(map_t*args)
2514 c_placement(args, 3);
2517 static int c_startclip(map_t*args)
2519 c_placement(args, 4);
2522 static int c_show(map_t*args)
2524 c_placement(args, 5);
2527 static int c_del(map_t*args)
2529 char*instance = lu(args, "name");
2530 s_delinstance(instance);
2533 static int c_end(map_t*args)
2538 static int c_sprite(map_t*args)
2540 char* name = lu(args, "name");
2544 static int c_frame(map_t*args)
2546 char*framestr = lu(args, "n");
2547 char*cutstr = lu(args, "cut");
2549 char*name = lu(args, "name");
2550 char*anchor = lu(args, "anchor");
2553 if(!strcmp(anchor, "anchor") && !*name)
2558 if(strcmp(cutstr, "no"))
2560 if(isRelative(framestr)) {
2561 frame = s_getframe();
2562 if(getSign(framestr)<0)
2563 syntaxerror("relative frame expressions must be positive");
2564 frame += parseInt(getOffset(framestr));
2567 frame = parseInt(framestr);
2568 if(s_getframe() >= frame
2569 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2570 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2572 s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
2575 static int c_primitive(map_t*args)
2577 char*name = lu(args, "name");
2578 char*command = lu(args, "commandname");
2579 int width=0, height=0, r=0;
2580 int linewidth = parseTwip(lu(args, "line"));
2581 char*colorstr = lu(args, "color");
2582 RGBA color = parseColor(colorstr);
2583 char*fillstr = lu(args, "fill");
2590 if(!strcmp(command, "circle"))
2592 else if(!strcmp(command, "filled"))
2596 width = parseTwip(lu(args, "width"));
2597 height = parseTwip(lu(args, "height"));
2598 } else if (type==1) {
2599 r = parseTwip(lu(args, "r"));
2600 } else if (type==2) {
2601 outline = lu(args, "outline");
2604 if(!strcmp(fillstr, "fill"))
2606 if(!strcmp(fillstr, "none"))
2608 if(width<0 || height<0 || linewidth<0 || r<0)
2609 syntaxerror("values width, height, line, r must be positive");
2611 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2612 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2613 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2617 static int c_textshape(map_t*args)
2619 char*name = lu(args, "name");
2620 char*text = lu(args, "text");
2621 char*font = lu(args, "font");
2622 float size = parsePxOrPercent(font, lu(args, "size"));
2624 s_textshape(name, font, size, text);
2628 static int c_swf(map_t*args)
2630 char*name = lu(args, "name");
2631 char*filename = lu(args, "filename");
2632 char*command = lu(args, "commandname");
2633 if(!strcmp(command, "shape"))
2634 warning("Please use .swf instead of .shape");
2635 s_includeswf(name, filename);
2639 static int c_font(map_t*args)
2641 char*name = lu(args, "name");
2642 char*filename = lu(args, "filename");
2643 s_font(name, filename);
2647 static int c_sound(map_t*args)
2649 char*name = lu(args, "name");
2650 char*filename = lu(args, "filename");
2651 s_sound(name, filename);
2655 static int c_text(map_t*args)
2657 char*name = lu(args, "name");
2658 char*text = lu(args, "text");
2659 char*font = lu(args, "font");
2660 float size = parsePxOrPercent(font, lu(args, "size"));
2661 RGBA color = parseColor(lu(args, "color"));
2662 s_text(name, font, text, (int)(size*100), color);
2666 static int c_soundtrack(map_t*args)
2671 static int c_quicktime(map_t*args)
2673 char*name = lu(args, "name");
2674 char*url = lu(args, "url");
2675 s_quicktime(name, url);
2679 static int c_image(map_t*args)
2681 char*command = lu(args, "commandname");
2682 char*name = lu(args, "name");
2683 char*filename = lu(args, "filename");
2684 if(!strcmp(command,"jpeg")) {
2685 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2686 s_image(name, "jpeg", filename, quality);
2688 s_image(name, "png", filename, 0);
2693 static int c_outline(map_t*args)
2695 char*name = lu(args, "name");
2696 char*format = lu(args, "format");
2700 syntaxerror("colon (:) expected");
2702 s_outline(name, format, text);
2706 int fakechar(map_t*args)
2708 char*name = lu(args, "name");
2709 s_box(name, 0, 0, black, 20, 0);
2713 static int c_egon(map_t*args) {return fakechar(args);}
2714 static int c_button(map_t*args) {
2715 char*name = lu(args, "name");
2719 static int current_button_flags = 0;
2720 static int c_on_press(map_t*args)
2722 char*position = lu(args, "position");
2724 if(!strcmp(position, "inside")) {
2725 current_button_flags |= BC_OVERUP_OVERDOWN;
2726 } else if(!strcmp(position, "outside")) {
2727 //current_button_flags |= BC_IDLE_OUTDOWN;
2728 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2729 } else if(!strcmp(position, "anywhere")) {
2730 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2733 if(type == RAWDATA) {
2735 s_buttonaction(current_button_flags, action);
2736 current_button_flags = 0;
2742 static int c_on_release(map_t*args)
2744 char*position = lu(args, "position");
2746 if(!strcmp(position, "inside")) {
2747 current_button_flags |= BC_OVERDOWN_OVERUP;
2748 } else if(!strcmp(position, "outside")) {
2749 current_button_flags |= BC_OUTDOWN_IDLE;
2750 } else if(!strcmp(position, "anywhere")) {
2751 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2754 if(type == RAWDATA) {
2756 s_buttonaction(current_button_flags, action);
2757 current_button_flags = 0;
2763 static int c_on_move_in(map_t*args)
2765 char*position = lu(args, "state");
2767 if(!strcmp(position, "pressed")) {
2768 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2769 } else if(!strcmp(position, "not_pressed")) {
2770 current_button_flags |= BC_IDLE_OVERUP;
2771 } else if(!strcmp(position, "any")) {
2772 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2775 if(type == RAWDATA) {
2777 s_buttonaction(current_button_flags, action);
2778 current_button_flags = 0;
2784 static int c_on_move_out(map_t*args)
2786 char*position = lu(args, "state");
2788 if(!strcmp(position, "pressed")) {
2789 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2790 } else if(!strcmp(position, "not_pressed")) {
2791 current_button_flags |= BC_OVERUP_IDLE;
2792 } else if(!strcmp(position, "any")) {
2793 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2796 if(type == RAWDATA) {
2798 s_buttonaction(current_button_flags, action);
2799 current_button_flags = 0;
2805 static int c_on_key(map_t*args)
2807 char*key = lu(args, "key");
2809 if(strlen(key)==1) {
2812 current_button_flags |= 0x4000 + (key[0]*0x200);
2814 syntaxerror("invalid character: %c"+key[0]);
2819 <ctrl-x> = 0x200*(x-'a')
2823 syntaxerror("invalid key: %s",key);
2826 if(type == RAWDATA) {
2828 s_buttonaction(current_button_flags, action);
2829 current_button_flags = 0;
2836 static int c_edittext(map_t*args)
2838 //"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"},
2839 char*name = lu(args, "name");
2840 char*font = lu(args, "font");
2841 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2842 int width = parseTwip(lu(args, "width"));
2843 int height = parseTwip(lu(args, "height"));
2844 char*text = lu(args, "text");
2845 RGBA color = parseColor(lu(args, "color"));
2846 int maxlength = parseInt(lu(args, "maxlength"));
2847 char*variable = lu(args, "variable");
2848 char*passwordstr = lu(args, "password");
2849 char*wordwrapstr = lu(args, "wordwrap");
2850 char*multilinestr = lu(args, "multiline");
2851 char*htmlstr = lu(args, "html");
2852 char*noselectstr = lu(args, "noselect");
2853 char*readonlystr = lu(args, "readonly");
2854 char*borderstr = lu(args, "border");
2855 char*autosizestr = lu(args, "autosize");
2856 char*alignstr = lu(args, "align");
2860 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2861 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2862 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2863 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2864 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2865 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2866 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2867 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2868 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2869 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2870 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2871 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2872 else syntaxerror("Unknown alignment: %s", alignstr);
2874 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2878 static int c_morphshape(map_t*args) {return fakechar(args);}
2879 static int c_movie(map_t*args) {return fakechar(args);}
2881 static char* readfile(const char*filename)
2883 FILE*fi = fopen(filename, "rb");
2887 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2888 fseek(fi, 0, SEEK_END);
2890 fseek(fi, 0, SEEK_SET);
2891 text = rfx_alloc(l+1);
2892 fread(text, l, 1, fi);
2898 static int c_action(map_t*args)
2900 char* filename = map_lookup(args, "filename");
2901 if(!filename ||!*filename) {
2903 if(type != RAWDATA) {
2904 syntaxerror("colon (:) expected");
2908 s_action(readfile(filename));
2914 static int c_initaction(map_t*args)
2916 char* character = lu(args, "name");
2917 char* filename = map_lookup(args, "filename");
2918 if(!filename ||!*filename) {
2920 if(type != RAWDATA) {
2921 syntaxerror("colon (:) expected");
2923 s_initaction(character, text);
2925 s_initaction(character, readfile(filename));
2933 command_func_t* func;
2936 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2937 {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
2938 // "import" type stuff
2939 {"swf", c_swf, "name filename"},
2940 {"shape", c_swf, "name filename"},
2941 {"jpeg", c_image, "name filename quality=80%"},
2942 {"png", c_image, "name filename"},
2943 {"movie", c_movie, "name filename"},
2944 {"sound", c_sound, "name filename"},
2945 {"font", c_font, "name filename"},
2946 {"soundtrack", c_soundtrack, "filename"},
2947 {"quicktime", c_quicktime, "url"},
2949 // generators of primitives
2951 {"point", c_point, "name x=0 y=0"},
2952 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2953 {"outline", c_outline, "name format=simple"},
2954 {"textshape", c_textshape, "name font size=100% text"},
2956 // character generators
2957 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2958 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2959 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2961 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2962 {"text", c_text, "name text font size=100% color=white"},
2963 {"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="},
2964 {"morphshape", c_morphshape, "name start end"},
2965 {"button", c_button, "name"},
2966 {"show", c_show, "name x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= pivot= pin= shear= rotate= ratio= above= below= as="},
2967 {"on_press", c_on_press, "position=inside"},
2968 {"on_release", c_on_release, "position=anywhere"},
2969 {"on_move_in", c_on_move_in, "state=not_pressed"},
2970 {"on_move_out", c_on_move_out, "state=not_pressed"},
2971 {"on_key", c_on_key, "key=any"},
2974 {"play", c_play, "name loop=0 @nomultiple=0"},
2975 {"stop", c_stop, "name= "},
2976 {"nextframe", c_nextframe, "name"},
2977 {"previousframe", c_previousframe, "name"},
2979 // object placement tags
2980 {"put", c_put, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= pivot= pin= shear= rotate= ratio= above= below="},
2981 {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= pivot= pin= shear= rotate= ratio= above= below="},
2982 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= pivot= pin= shear= rotate= ratio= above= below="},
2983 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= pivot= pin= shear= rotate= ratio= above= below="},
2984 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= pivot= pin= shear= rotate= ratio= above= below="},
2985 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= pivot= pin= shear= rotate= ratio= above= below="},
2986 {"del", c_del, "name"},
2987 // virtual object placement
2988 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2990 // commands which start a block
2991 //startclip (see above)
2992 {"sprite", c_sprite, "name"},
2993 {"action", c_action, "filename="},
2994 {"initaction", c_initaction, "name filename="},
3000 static map_t parseArguments(char*command, char*pattern)
3016 string_set(&t1, "commandname");
3017 string_set(&t2, command);
3018 map_put(&result, t1, t2);
3020 if(!pattern || !*pattern)
3027 if(!strncmp("<i> ", x, 3)) {
3029 if(type == COMMAND || type == RAWDATA) {
3031 syntaxerror("character name expected");
3033 name[pos].str = "instance";
3035 value[pos].str = text;
3036 value[pos].len = strlen(text);
3040 if(type == ASSIGNMENT)
3043 name[pos].str = "character";
3045 value[pos].str = text;
3046 value[pos].len = strlen(text);
3054 isboolean[pos] = (x[0] =='@');
3067 name[pos].len = d-x;
3072 name[pos].len = e-x;
3073 value[pos].str = e+1;
3074 value[pos].len = d-e-1;
3082 /* for(t=0;t<len;t++) {
3083 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3084 isboolean[t]?"(boolean)":"");
3089 if(type == RAWDATA || type == COMMAND) {
3094 // first, search for boolean arguments
3095 for(pos=0;pos<len;pos++)
3097 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3099 if(type == ASSIGNMENT)
3101 value[pos].str = text;
3102 value[pos].len = strlen(text);
3103 /*printf("setting boolean parameter %s (to %s)\n",
3104 strdup_n(name[pos], namelen[pos]),
3105 strdup_n(value[pos], valuelen[pos]));*/
3110 // second, search for normal arguments
3112 for(pos=0;pos<len;pos++)
3114 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3115 (type != ASSIGNMENT && !set[pos])) {
3117 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3119 if(type == ASSIGNMENT)
3122 value[pos].str = text;
3123 value[pos].len = strlen(text);
3125 printf("setting parameter %s (to %s)\n",
3126 strdup_n(name[pos].str, name[pos].len),
3127 strdup_n(value[pos].str, value[pos].len));
3133 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3137 for(t=0;t<len;t++) {
3138 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3141 for(t=0;t<len;t++) {
3142 if(value[t].str && value[t].str[0] == '*') {
3143 //relative default- take value from some other parameter
3145 for(s=0;s<len;s++) {
3146 if(value[s].len == value[t].len-1 &&
3147 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3148 value[t].str = value[s].str;
3151 if(value[t].str == 0) {
3153 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3157 /* ok, now construct the dictionary from the parameters */
3161 map_put(&result, name[t], value[t]);
3165 static void parseArgumentsForCommand(char*command)
3170 msg("<verbose> parse Command: %s (line %d)", command, line);
3172 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3173 if(!strcmp(arguments[t].command, command)) {
3175 /* ugly hack- will be removed soon (once documentation and .sc generating
3176 utilities have been changed) */
3177 if(!strcmp(command, "swf") && !stackpos) {
3178 warning("Please use .flash instead of .swf- this will be mandatory soon");
3183 args = parseArguments(command, arguments[t].arguments);
3189 syntaxerror("command %s not known", command);
3191 // catch missing .flash directives at the beginning of a file
3192 if(strcmp(command, "flash") && !stackpos)
3194 syntaxerror("No movie defined- use .flash first");
3198 printf(".%s\n", command);fflush(stdout);
3199 map_dump(&args, stdout, "\t");fflush(stdout);
3202 (*arguments[nr].func)(&args);
3204 /*if(!strcmp(command, "button") ||
3205 !strcmp(command, "action")) {
3208 if(type == COMMAND) {
3209 if(!strcmp(text, "end"))
3223 int main (int argc,char ** argv)
3226 processargs(argc, argv);
3227 initLog(0,-1,0,0,-1,verbose);
3230 args_callback_usage(argv[0]);
3234 file = generateTokens(filename);
3236 fprintf(stderr, "parser returned error.\n");
3242 while(!noMoreTokens()) {
3245 syntaxerror("command expected");
3246 parseArgumentsForCommand(text);