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_JPEGLIB
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)
1524 if(ftag->id != ST_SETBACKGROUNDCOLOR) {
1525 /* We simply dump all tags right after the sprite
1526 header, relying on the fact that swf_OptimizeTagOrder() will
1527 sort things out for us later.
1528 We also rely on the fact that the imported SWF is well-formed.
1530 tag = swf_InsertTag(tag, ftag->id);
1531 swf_SetBlock(tag, ftag->data, ftag->len);
1537 syntaxerror("Included file %s contains errors", filename);
1538 tag = swf_InsertTag(tag, ST_END);
1542 s_addcharacter(name, id, tag, r);
1545 SRECT s_getCharBBox(char*name)
1547 character_t* c = dictionary_lookup(&characters, name);
1548 if(!c) syntaxerror("character '%s' unknown(2)", name);
1551 SRECT s_getInstanceBBox(char*name)
1553 instance_t * i = dictionary_lookup(&instances, name);
1555 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1557 if(!c) syntaxerror("internal error(5)");
1560 parameters_t s_getParameters(char*name)
1562 instance_t * i = dictionary_lookup(&instances, name);
1563 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1564 return i->parameters;
1566 void s_startclip(char*instance, char*character, parameters_t p)
1568 character_t* c = dictionary_lookup(&characters, character);
1572 syntaxerror("character %s not known", character);
1574 i = s_addinstance(instance, c, currentdepth);
1576 m = s_instancepos(i->character->size, &p);
1578 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1579 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1580 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1582 i->lastFrame= currentframe;
1584 stack[stackpos].tag = tag;
1585 stack[stackpos].type = 2;
1594 swf_SetTagPos(stack[stackpos].tag, 0);
1595 swf_GetPlaceObject(stack[stackpos].tag, &p);
1596 p.clipdepth = currentdepth;
1598 swf_ClearTag(stack[stackpos].tag);
1599 swf_SetPlaceObject(stack[stackpos].tag, &p);
1603 void s_put(char*instance, char*character, parameters_t p)
1605 character_t* c = dictionary_lookup(&characters, character);
1609 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1612 i = s_addinstance(instance, c, currentdepth);
1614 m = s_instancepos(i->character->size, &p);
1616 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1617 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1619 i->lastFrame = currentframe;
1623 void s_jump(char*instance, parameters_t p)
1625 instance_t* i = dictionary_lookup(&instances, instance);
1628 syntaxerror("instance %s not known", instance);
1632 m = s_instancepos(i->character->size, &p);
1634 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1635 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1637 i->lastFrame = currentframe;
1640 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1644 if(num==0 || num==1)
1646 ratio = (float)pos/(float)num;
1648 p.x = (p2->x-p1->x)*ratio + p1->x;
1649 p.y = (p2->y-p1->y)*ratio + p1->y;
1650 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1651 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1652 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1653 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1655 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1656 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1657 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1658 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1660 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1661 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1662 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1663 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1665 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1666 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1667 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1668 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1672 void s_change(char*instance, parameters_t p2)
1674 instance_t* i = dictionary_lookup(&instances, instance);
1678 int frame, allframes;
1680 syntaxerror("instance %s not known", instance);
1684 allframes = currentframe - i->lastFrame - 1;
1686 warning(".change ignored. can only .put/.change an object once per frame.");
1690 m = s_instancepos(i->character->size, &p2);
1691 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1692 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1695 /* o.k., we got the start and end point set. Now iterate though all the
1696 tags in between, inserting object changes after each new frame */
1699 if(!t) syntaxerror("internal error(6)");
1701 while(frame < allframes) {
1702 if(t->id == ST_SHOWFRAME) {
1707 p = s_interpolate(&p1, &p2, frame, allframes);
1708 m = s_instancepos(i->character->size, &p); //needed?
1709 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1710 i->lastFrame = currentframe;
1711 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1713 if(frame == allframes)
1718 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1722 void s_delinstance(char*instance)
1724 instance_t* i = dictionary_lookup(&instances, instance);
1726 syntaxerror("instance %s not known", instance);
1728 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1729 swf_SetU16(tag, i->depth);
1730 dictionary_del(&instances, instance);
1733 void s_qchange(char*instance, parameters_t p)
1740 syntaxerror(".end unexpected");
1741 if(stack[stackpos-1].type == 0)
1743 else if(stack[stackpos-1].type == 1)
1745 else if(stack[stackpos-1].type == 2)
1747 else if(stack[stackpos-1].type == 3)
1749 else syntaxerror("internal error 1");
1752 // ------------------------------------------------------------------------
1754 typedef int command_func_t(map_t*args);
1756 SRECT parseBox(char*str)
1758 SRECT r = {0,0,0,0};
1759 float xmin, xmax, ymin, ymax;
1760 char*x = strchr(str, 'x');
1762 if(!strcmp(str, "autocrop")) {
1763 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1767 d1 = strchr(x+1, ':');
1769 d2 = strchr(d1+1, ':');
1771 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1775 else if(d1 && !d2) {
1776 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1782 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1787 r.xmin = (SCOORD)(xmin*20);
1788 r.ymin = (SCOORD)(ymin*20);
1789 r.xmax = (SCOORD)(xmax*20);
1790 r.ymax = (SCOORD)(ymax*20);
1793 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1796 float parseFloat(char*str)
1800 int parseInt(char*str)
1805 if(str[0]=='+' || str[0]=='-')
1809 if(str[t]<'0' || str[t]>'9')
1810 syntaxerror("Not an Integer: \"%s\"", str);
1813 int parseTwip(char*str)
1817 if(str[0]=='+' || str[0]=='-') {
1822 dot = strchr(str, '.');
1826 return sign*parseInt(str)*20;
1828 char* old = strdup(str);
1829 int l=strlen(dot+1);
1832 for(s=str;s<dot-1;s++)
1833 if(*s<'0' || *s>'9')
1834 syntaxerror("Not a coordinate: \"%s\"", str);
1836 if(*s<'0' || *s>'9')
1837 syntaxerror("Not a coordinate: \"%s\"", str);
1839 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1840 dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
1843 warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
1847 return sign*atoi(str)*20;
1849 return sign*atoi(str)*20+atoi(dot)*2;
1851 return sign*atoi(str)*20+atoi(dot)/5;
1856 int isPoint(char*str)
1858 if(strchr(str, '('))
1864 SPOINT parsePoint(char*str)
1868 int l = strlen(str);
1869 char*comma = strchr(str, ',');
1870 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1871 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1872 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1873 p.x = parseTwip(tmp);
1874 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1875 p.y = parseTwip(tmp);
1879 int parseColor2(char*str, RGBA*color)
1881 int l = strlen(str);
1885 struct {unsigned char r,g,b;char*name;} colors[] =
1886 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1887 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1888 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1889 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1890 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1891 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1892 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1893 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1894 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1895 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1896 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1897 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1901 if(str[0]=='#' && (l==7 || l==9)) {
1902 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1904 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1906 color->r = r; color->g = g; color->b = b; color->a = a;
1909 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1910 if(!strcmp(str, colors[t].name)) {
1915 color->r = r; color->g = g; color->b = b; color->a = a;
1921 RGBA parseColor(char*str)
1924 if(!parseColor2(str, &c))
1925 syntaxerror("Expression '%s' is not a color", str);
1929 typedef struct _muladd {
1934 MULADD parseMulAdd(char*str)
1937 char* str2 = (char*)malloc(strlen(str)+5);
1944 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1945 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1946 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1947 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1948 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1949 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1950 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1951 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1952 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1953 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1955 syntaxerror("'%s' is not a valid color transform expression", str);
1957 m.add = (int)(add*256);
1958 m.mul = (int)(mul*256);
1963 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1965 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1966 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1968 if(a<-32768) a=-32768;
1969 if(a>32767) a=32767;
1970 if(m<-32768) m=-32768;
1971 if(m>32767) m=32767;
1977 float parsePxOrPercent(char*fontname, char*str)
1979 int l = strlen(str);
1980 if(strchr(str, '%'))
1981 return parsePercent(str);
1982 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1983 float p = atof(str);
1984 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1986 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1990 float parsePercent(char*str)
1992 int l = strlen(str);
1996 return atoi(str)/100.0;
1998 syntaxerror("Expression '%s' is not a percentage", str);
2001 int isPercent(char*str)
2003 return str[strlen(str)-1]=='%';
2005 int parseNewSize(char*str, int size)
2008 return parsePercent(str)*size;
2010 return (int)(atof(str)*20);
2013 int isColor(char*str)
2016 return parseColor2(str, &c);
2019 static char* lu(map_t* args, char*name)
2021 char* value = map_lookup(args, name);
2023 map_dump(args, stdout, "");
2024 syntaxerror("internal error 2: value %s should be set", name);
2029 static int c_flash(map_t*args)
2031 char* filename = map_lookup(args, "filename");
2032 char* compressstr = lu(args, "compress");
2033 SRECT bbox = parseBox(lu(args, "bbox"));
2034 int version = parseInt(lu(args, "version"));
2035 int fps = (int)(parseFloat(lu(args, "fps"))*256);
2037 RGBA color = parseColor(lu(args, "background"));
2039 if(!filename || !*filename) {
2040 /* for compatibility */
2041 filename = map_lookup(args, "name");
2042 if(!filename || !*filename) {
2045 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2046 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2050 if(!filename || override_outputname)
2051 filename = outputname;
2053 if(!strcmp(compressstr, "default"))
2054 compress = version==6;
2055 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2057 else if(!strcmp(compressstr, "no"))
2059 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2061 s_swf(filename, bbox, version, fps, compress, color);
2064 int isRelative(char*str)
2066 return !strncmp(str, "<plus>", 6) ||
2067 !strncmp(str, "<minus>", 7);
2069 char* getOffset(char*str)
2071 if(!strncmp(str, "<plus>", 6))
2073 if(!strncmp(str, "<minus>", 7))
2075 syntaxerror("internal error (347)");
2078 int getSign(char*str)
2080 if(!strncmp(str, "<plus>", 6))
2082 if(!strncmp(str, "<minus>", 7))
2084 syntaxerror("internal error (348)");
2087 static dictionary_t points;
2088 static mem_t mpoints;
2089 int points_initialized = 0;
2091 SPOINT getPoint(SRECT r, char*name)
2094 if(!strcmp(name, "center")) {
2096 p.x = (r.xmin + r.xmax)/2;
2097 p.y = (r.ymin + r.ymax)/2;
2101 if(points_initialized)
2102 l = (int)dictionary_lookup(&points, name);
2104 syntaxerror("Invalid point: \"%s\".", name);
2107 return *(SPOINT*)&mpoints.buffer[l];
2110 static int texture2(char*name, char*object, map_t*args, int errors)
2113 char*xstr = map_lookup(args, "x");
2114 char*ystr = map_lookup(args, "y");
2115 char*widthstr = map_lookup(args, "width");
2116 char*heightstr = map_lookup(args, "height");
2117 char*scalestr = map_lookup(args, "scale");
2118 char*scalexstr = map_lookup(args, "scalex");
2119 char*scaleystr = map_lookup(args, "scaley");
2120 char*rotatestr = map_lookup(args, "rotate");
2121 char* shearstr = map_lookup(args, "shear");
2122 char* radiusstr = map_lookup(args, "r");
2124 float scalex = 1.0, scaley = 1.0;
2125 float rotate=0, shear=0;
2127 if(!*xstr && !*ystr) {
2129 syntaxerror("x and y must be set");
2132 if(*scalestr && (*scalexstr || *scaleystr)) {
2133 syntaxerror("scale and scalex/scaley can't both be set");
2136 if((*widthstr || *heightstr) && *radiusstr) {
2137 syntaxerror("width/height and radius can't both be set");
2140 widthstr = radiusstr;
2141 heightstr = radiusstr;
2143 if(!*xstr) xstr="0";
2144 if(!*ystr) ystr="0";
2145 if(!*rotatestr) rotatestr="0";
2146 if(!*shearstr) shearstr="0";
2149 scalex = scaley = parsePercent(scalestr);
2150 } else if(*scalexstr || *scaleystr) {
2151 if(scalexstr) scalex = parsePercent(scalexstr);
2152 if(scaleystr) scaley = parsePercent(scaleystr);
2153 } else if(*widthstr || *heightstr) {
2156 s_getBitmapSize(object, &width, &height);
2158 scalex = (float)parseTwip(widthstr)/(float)width;
2160 scaley = (float)parseTwip(heightstr)/(float)height;
2162 x = parseTwip(xstr);
2163 y = parseTwip(ystr);
2164 rotate = parseFloat(rotatestr);
2165 shear = parseFloat(shearstr);
2167 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2172 static int c_texture(map_t*args)
2174 char*name = lu(args, "instance");
2175 char*object = lu(args, "character");
2176 return texture2(name, object, args, 1);
2179 static int c_gradient(map_t*args)
2181 char*name = lu(args, "name");
2182 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2183 int rotate = parseInt(lu(args, "rotate"));
2187 syntaxerror("colon (:) expected");
2189 s_gradient(name, text, radial, rotate);
2191 /* check whether we also have placement information,
2192 which would make this a positioned gradient.
2193 If there is placement information, texture2() will
2194 add a texture, which has priority over the gradient.
2196 texture2(name, name, args, 0);
2199 static int c_point(map_t*args)
2201 char*name = lu(args, "name");
2205 if(!points_initialized) {
2206 dictionary_init(&points);
2208 points_initialized = 1;
2210 p.x = parseTwip(lu(args, "x"));
2211 p.y = parseTwip(lu(args, "y"));
2212 pos = mem_put(&mpoints, &p, sizeof(p));
2213 string_set(&s1, name);
2215 dictionary_put(&points, s1, (void*)pos);
2218 static int c_play(map_t*args)
2220 char*name = lu(args, "name");
2221 char*loop = lu(args, "loop");
2222 char*nomultiple = lu(args, "nomultiple");
2224 if(!strcmp(nomultiple, "nomultiple"))
2227 nm = parseInt(nomultiple);
2229 if(s_playsound(name, parseInt(loop), nm, 0)) {
2231 } else if(s_swf3action(name, "play")) {
2237 static int c_stop(map_t*args)
2239 char*name = map_lookup(args, "name");
2241 if(s_playsound(name, 0,0,1)) {
2243 } else if(s_swf3action(name, "stop")) {
2246 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2250 static int c_nextframe(map_t*args)
2252 char*name = lu(args, "name");
2254 if(s_swf3action(name, "nextframe")) {
2257 syntaxerror("I don't know anything about movie \"%s\"", name);
2261 static int c_previousframe(map_t*args)
2263 char*name = lu(args, "name");
2265 if(s_swf3action(name, "previousframe")) {
2268 syntaxerror("I don't know anything about movie \"%s\"", name);
2272 static int c_placement(map_t*args, int type)
2274 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2277 char* luminancestr = lu(args, "luminance");
2278 char* scalestr = lu(args, "scale");
2279 char* scalexstr = lu(args, "scalex");
2280 char* scaleystr = lu(args, "scaley");
2281 char* rotatestr = lu(args, "rotate");
2282 char* shearstr = lu(args, "shear");
2283 char* xstr="", *pivotstr="";
2284 char* ystr="", *anglestr="";
2285 char*above = lu(args, "above"); /*FIXME*/
2286 char*below = lu(args, "below");
2287 char* rstr = lu(args, "red");
2288 char* gstr = lu(args, "green");
2289 char* bstr = lu(args, "blue");
2290 char* astr = lu(args, "alpha");
2291 char* pinstr = lu(args, "pin");
2292 char* as = map_lookup(args, "as");
2300 if(type==9) { // (?) .rotate or .arcchange
2301 pivotstr = lu(args, "pivot");
2302 anglestr = lu(args, "angle");
2304 xstr = lu(args, "x");
2305 ystr = lu(args, "y");
2308 luminance = parseMulAdd(luminancestr);
2311 luminance.mul = 256;
2315 if(scalexstr[0]||scaleystr[0])
2316 syntaxerror("scalex/scaley and scale cannot both be set");
2317 scalexstr = scaleystr = scalestr;
2320 if(type == 0 || type == 4) {
2322 character = lu(args, "character");
2323 parameters_clear(&p);
2324 } else if (type == 5) {
2325 character = lu(args, "name");
2326 parameters_clear(&p);
2329 p = s_getParameters(instance);
2334 if(isRelative(xstr)) {
2335 if(type == 0 || type == 4)
2336 syntaxerror("relative x values not allowed for initial put or startclip");
2337 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2339 p.x = parseTwip(xstr);
2343 if(isRelative(ystr)) {
2344 if(type == 0 || type == 4)
2345 syntaxerror("relative y values not allowed for initial put or startclip");
2346 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2348 p.y = parseTwip(ystr);
2352 /* scale, scalex, scaley */
2354 oldbbox = s_getCharBBox(character);
2356 oldbbox = s_getInstanceBBox(instance);
2358 oldwidth = oldbbox.xmax - oldbbox.xmin;
2359 oldheight = oldbbox.ymax - oldbbox.ymin;
2361 if(oldwidth==0) p.scalex = 1.0;
2364 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2368 if(oldheight==0) p.scaley = 1.0;
2371 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2377 if(isRelative(rotatestr)) {
2378 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2380 p.rotate = parseFloat(rotatestr);
2386 if(isRelative(shearstr)) {
2387 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2389 p.shear = parseFloat(shearstr);
2394 if(isPoint(pivotstr))
2395 p.pivot = parsePoint(pivotstr);
2397 p.pivot = getPoint(oldbbox, pivotstr);
2401 p.pin = parsePoint(pinstr);
2403 p.pin = getPoint(oldbbox, pinstr);
2406 /* color transform */
2408 if(rstr[0] || luminancestr[0]) {
2411 r = parseMulAdd(rstr);
2413 r.add = p.cxform.r0;
2414 r.mul = p.cxform.r1;
2416 r = mergeMulAdd(r, luminance);
2417 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2419 if(gstr[0] || luminancestr[0]) {
2422 g = parseMulAdd(gstr);
2424 g.add = p.cxform.g0;
2425 g.mul = p.cxform.g1;
2427 g = mergeMulAdd(g, luminance);
2428 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2430 if(bstr[0] || luminancestr[0]) {
2433 b = parseMulAdd(bstr);
2435 b.add = p.cxform.b0;
2436 b.mul = p.cxform.b1;
2438 b = mergeMulAdd(b, luminance);
2439 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2442 MULADD a = parseMulAdd(astr);
2443 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2447 s_put(instance, character, p);
2449 s_change(instance, p);
2451 s_qchange(instance, p);
2453 s_jump(instance, p);
2455 s_startclip(instance, character, p);
2456 else if(type == 5) {
2458 s_buttonput(character, as, p);
2460 s_buttonput(character, "shape", p);
2465 static int c_put(map_t*args)
2467 c_placement(args, 0);
2470 static int c_change(map_t*args)
2472 c_placement(args, 1);
2475 static int c_qchange(map_t*args)
2477 c_placement(args, 2);
2480 static int c_arcchange(map_t*args)
2482 c_placement(args, 2);
2485 static int c_jump(map_t*args)
2487 c_placement(args, 3);
2490 static int c_startclip(map_t*args)
2492 c_placement(args, 4);
2495 static int c_show(map_t*args)
2497 c_placement(args, 5);
2500 static int c_del(map_t*args)
2502 char*instance = lu(args, "name");
2503 s_delinstance(instance);
2506 static int c_end(map_t*args)
2511 static int c_sprite(map_t*args)
2513 char* name = lu(args, "name");
2517 static int c_frame(map_t*args)
2519 char*framestr = lu(args, "n");
2520 char*cutstr = lu(args, "cut");
2522 char*name = lu(args, "name");
2523 char*anchor = lu(args, "anchor");
2526 if(!strcmp(anchor, "anchor") && !*name)
2531 if(strcmp(cutstr, "no"))
2533 if(isRelative(framestr)) {
2534 frame = s_getframe();
2535 if(getSign(framestr)<0)
2536 syntaxerror("relative frame expressions must be positive");
2537 frame += parseInt(getOffset(framestr));
2540 frame = parseInt(framestr);
2541 if(s_getframe() >= frame
2542 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2543 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2545 s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
2548 static int c_primitive(map_t*args)
2550 char*name = lu(args, "name");
2551 char*command = lu(args, "commandname");
2552 int width=0, height=0, r=0;
2553 int linewidth = parseTwip(lu(args, "line"));
2554 char*colorstr = lu(args, "color");
2555 RGBA color = parseColor(colorstr);
2556 char*fillstr = lu(args, "fill");
2563 if(!strcmp(command, "circle"))
2565 else if(!strcmp(command, "filled"))
2569 width = parseTwip(lu(args, "width"));
2570 height = parseTwip(lu(args, "height"));
2571 } else if (type==1) {
2572 r = parseTwip(lu(args, "r"));
2573 } else if (type==2) {
2574 outline = lu(args, "outline");
2577 if(!strcmp(fillstr, "fill"))
2579 if(!strcmp(fillstr, "none"))
2581 if(width<0 || height<0 || linewidth<0 || r<0)
2582 syntaxerror("values width, height, line, r must be positive");
2584 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2585 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2586 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2590 static int c_textshape(map_t*args)
2592 char*name = lu(args, "name");
2593 char*text = lu(args, "text");
2594 char*font = lu(args, "font");
2595 float size = parsePxOrPercent(font, lu(args, "size"));
2597 s_textshape(name, font, size, text);
2601 static int c_swf(map_t*args)
2603 char*name = lu(args, "name");
2604 char*filename = lu(args, "filename");
2605 char*command = lu(args, "commandname");
2606 if(!strcmp(command, "shape"))
2607 warning("Please use .swf instead of .shape");
2608 s_includeswf(name, filename);
2612 static int c_font(map_t*args)
2614 char*name = lu(args, "name");
2615 char*filename = lu(args, "filename");
2616 s_font(name, filename);
2620 static int c_sound(map_t*args)
2622 char*name = lu(args, "name");
2623 char*filename = lu(args, "filename");
2624 s_sound(name, filename);
2628 static int c_text(map_t*args)
2630 char*name = lu(args, "name");
2631 char*text = lu(args, "text");
2632 char*font = lu(args, "font");
2633 float size = parsePxOrPercent(font, lu(args, "size"));
2634 RGBA color = parseColor(lu(args, "color"));
2635 s_text(name, font, text, (int)(size*100), color);
2639 static int c_soundtrack(map_t*args)
2644 static int c_quicktime(map_t*args)
2646 char*name = lu(args, "name");
2647 char*url = lu(args, "url");
2648 s_quicktime(name, url);
2652 static int c_image(map_t*args)
2654 char*command = lu(args, "commandname");
2655 char*name = lu(args, "name");
2656 char*filename = lu(args, "filename");
2657 if(!strcmp(command,"jpeg")) {
2658 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2659 s_image(name, "jpeg", filename, quality);
2661 s_image(name, "png", filename, 0);
2666 static int c_outline(map_t*args)
2668 char*name = lu(args, "name");
2669 char*format = lu(args, "format");
2673 syntaxerror("colon (:) expected");
2675 s_outline(name, format, text);
2679 int fakechar(map_t*args)
2681 char*name = lu(args, "name");
2682 s_box(name, 0, 0, black, 20, 0);
2686 static int c_egon(map_t*args) {return fakechar(args);}
2687 static int c_button(map_t*args) {
2688 char*name = lu(args, "name");
2692 static int current_button_flags = 0;
2693 static int c_on_press(map_t*args)
2695 char*position = lu(args, "position");
2697 if(!strcmp(position, "inside")) {
2698 current_button_flags |= BC_OVERUP_OVERDOWN;
2699 } else if(!strcmp(position, "outside")) {
2700 //current_button_flags |= BC_IDLE_OUTDOWN;
2701 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2702 } else if(!strcmp(position, "anywhere")) {
2703 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2706 if(type == RAWDATA) {
2708 s_buttonaction(current_button_flags, action);
2709 current_button_flags = 0;
2715 static int c_on_release(map_t*args)
2717 char*position = lu(args, "position");
2719 if(!strcmp(position, "inside")) {
2720 current_button_flags |= BC_OVERDOWN_OVERUP;
2721 } else if(!strcmp(position, "outside")) {
2722 current_button_flags |= BC_OUTDOWN_IDLE;
2723 } else if(!strcmp(position, "anywhere")) {
2724 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2727 if(type == RAWDATA) {
2729 s_buttonaction(current_button_flags, action);
2730 current_button_flags = 0;
2736 static int c_on_move_in(map_t*args)
2738 char*position = lu(args, "state");
2740 if(!strcmp(position, "pressed")) {
2741 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2742 } else if(!strcmp(position, "not_pressed")) {
2743 current_button_flags |= BC_IDLE_OVERUP;
2744 } else if(!strcmp(position, "any")) {
2745 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2748 if(type == RAWDATA) {
2750 s_buttonaction(current_button_flags, action);
2751 current_button_flags = 0;
2757 static int c_on_move_out(map_t*args)
2759 char*position = lu(args, "state");
2761 if(!strcmp(position, "pressed")) {
2762 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2763 } else if(!strcmp(position, "not_pressed")) {
2764 current_button_flags |= BC_OVERUP_IDLE;
2765 } else if(!strcmp(position, "any")) {
2766 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2769 if(type == RAWDATA) {
2771 s_buttonaction(current_button_flags, action);
2772 current_button_flags = 0;
2778 static int c_on_key(map_t*args)
2780 char*key = lu(args, "key");
2782 if(strlen(key)==1) {
2785 current_button_flags |= 0x4000 + (key[0]*0x200);
2787 syntaxerror("invalid character: %c"+key[0]);
2792 <ctrl-x> = 0x200*(x-'a')
2796 syntaxerror("invalid key: %s",key);
2799 if(type == RAWDATA) {
2801 s_buttonaction(current_button_flags, action);
2802 current_button_flags = 0;
2809 static int c_edittext(map_t*args)
2811 //"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"},
2812 char*name = lu(args, "name");
2813 char*font = lu(args, "font");
2814 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2815 int width = parseTwip(lu(args, "width"));
2816 int height = parseTwip(lu(args, "height"));
2817 char*text = lu(args, "text");
2818 RGBA color = parseColor(lu(args, "color"));
2819 int maxlength = parseInt(lu(args, "maxlength"));
2820 char*variable = lu(args, "variable");
2821 char*passwordstr = lu(args, "password");
2822 char*wordwrapstr = lu(args, "wordwrap");
2823 char*multilinestr = lu(args, "multiline");
2824 char*htmlstr = lu(args, "html");
2825 char*noselectstr = lu(args, "noselect");
2826 char*readonlystr = lu(args, "readonly");
2827 char*borderstr = lu(args, "border");
2828 char*autosizestr = lu(args, "autosize");
2829 char*alignstr = lu(args, "align");
2833 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2834 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2835 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2836 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2837 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2838 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2839 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2840 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2841 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2842 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2843 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2844 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2845 else syntaxerror("Unknown alignment: %s", alignstr);
2847 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2851 static int c_morphshape(map_t*args) {return fakechar(args);}
2852 static int c_movie(map_t*args) {return fakechar(args);}
2854 static char* readfile(const char*filename)
2856 FILE*fi = fopen(filename, "rb");
2860 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2861 fseek(fi, 0, SEEK_END);
2863 fseek(fi, 0, SEEK_SET);
2864 text = rfx_alloc(l+1);
2865 fread(text, l, 1, fi);
2871 static int c_action(map_t*args)
2873 char* filename = map_lookup(args, "filename");
2874 if(!filename ||!*filename) {
2876 if(type != RAWDATA) {
2877 syntaxerror("colon (:) expected");
2881 s_action(readfile(filename));
2887 static int c_initaction(map_t*args)
2889 char* character = lu(args, "name");
2890 char* filename = map_lookup(args, "filename");
2891 if(!filename ||!*filename) {
2893 if(type != RAWDATA) {
2894 syntaxerror("colon (:) expected");
2896 s_initaction(character, text);
2898 s_initaction(character, readfile(filename));
2906 command_func_t* func;
2909 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2910 {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
2911 // "import" type stuff
2912 {"swf", c_swf, "name filename"},
2913 {"shape", c_swf, "name filename"},
2914 {"jpeg", c_image, "name filename quality=80%"},
2915 {"png", c_image, "name filename"},
2916 {"movie", c_movie, "name filename"},
2917 {"sound", c_sound, "name filename"},
2918 {"font", c_font, "name filename"},
2919 {"soundtrack", c_soundtrack, "filename"},
2920 {"quicktime", c_quicktime, "url"},
2922 // generators of primitives
2924 {"point", c_point, "name x=0 y=0"},
2925 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2926 {"outline", c_outline, "name format=simple"},
2927 {"textshape", c_textshape, "name font size=100% text"},
2929 // character generators
2930 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2931 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2932 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2934 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2935 {"text", c_text, "name text font size=100% color=white"},
2936 {"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="},
2937 {"morphshape", c_morphshape, "name start end"},
2938 {"button", c_button, "name"},
2939 {"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="},
2940 {"on_press", c_on_press, "position=inside"},
2941 {"on_release", c_on_release, "position=anywhere"},
2942 {"on_move_in", c_on_move_in, "state=not_pressed"},
2943 {"on_move_out", c_on_move_out, "state=not_pressed"},
2944 {"on_key", c_on_key, "key=any"},
2947 {"play", c_play, "name loop=0 @nomultiple=0"},
2948 {"stop", c_stop, "name= "},
2949 {"nextframe", c_nextframe, "name"},
2950 {"previousframe", c_previousframe, "name"},
2952 // object placement tags
2953 {"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="},
2954 {"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="},
2955 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2956 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2957 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2958 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2959 {"del", c_del, "name"},
2960 // virtual object placement
2961 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2963 // commands which start a block
2964 //startclip (see above)
2965 {"sprite", c_sprite, "name"},
2966 {"action", c_action, "filename="},
2967 {"initaction", c_initaction, "name filename="},
2973 static map_t parseArguments(char*command, char*pattern)
2989 string_set(&t1, "commandname");
2990 string_set(&t2, command);
2991 map_put(&result, t1, t2);
2993 if(!pattern || !*pattern)
3000 if(!strncmp("<i> ", x, 3)) {
3002 if(type == COMMAND || type == RAWDATA) {
3004 syntaxerror("character name expected");
3006 name[pos].str = "instance";
3008 value[pos].str = text;
3009 value[pos].len = strlen(text);
3013 if(type == ASSIGNMENT)
3016 name[pos].str = "character";
3018 value[pos].str = text;
3019 value[pos].len = strlen(text);
3027 isboolean[pos] = (x[0] =='@');
3040 name[pos].len = d-x;
3045 name[pos].len = e-x;
3046 value[pos].str = e+1;
3047 value[pos].len = d-e-1;
3055 /* for(t=0;t<len;t++) {
3056 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3057 isboolean[t]?"(boolean)":"");
3062 if(type == RAWDATA || type == COMMAND) {
3067 // first, search for boolean arguments
3068 for(pos=0;pos<len;pos++)
3070 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3072 if(type == ASSIGNMENT)
3074 value[pos].str = text;
3075 value[pos].len = strlen(text);
3076 /*printf("setting boolean parameter %s (to %s)\n",
3077 strdup_n(name[pos], namelen[pos]),
3078 strdup_n(value[pos], valuelen[pos]));*/
3083 // second, search for normal arguments
3085 for(pos=0;pos<len;pos++)
3087 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3088 (type != ASSIGNMENT && !set[pos])) {
3090 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3092 if(type == ASSIGNMENT)
3095 value[pos].str = text;
3096 value[pos].len = strlen(text);
3098 printf("setting parameter %s (to %s)\n",
3099 strdup_n(name[pos].str, name[pos].len),
3100 strdup_n(value[pos].str, value[pos].len));
3106 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3110 for(t=0;t<len;t++) {
3111 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3114 for(t=0;t<len;t++) {
3115 if(value[t].str && value[t].str[0] == '*') {
3116 //relative default- take value from some other parameter
3118 for(s=0;s<len;s++) {
3119 if(value[s].len == value[t].len-1 &&
3120 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3121 value[t].str = value[s].str;
3124 if(value[t].str == 0) {
3126 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3130 /* ok, now construct the dictionary from the parameters */
3134 map_put(&result, name[t], value[t]);
3138 static void parseArgumentsForCommand(char*command)
3143 msg("<verbose> parse Command: %s (line %d)", command, line);
3145 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3146 if(!strcmp(arguments[t].command, command)) {
3148 /* ugly hack- will be removed soon (once documentation and .sc generating
3149 utilities have been changed) */
3150 if(!strcmp(command, "swf") && !stackpos) {
3151 warning("Please use .flash instead of .swf- this will be mandatory soon");
3156 args = parseArguments(command, arguments[t].arguments);
3162 syntaxerror("command %s not known", command);
3164 // catch missing .flash directives at the beginning of a file
3165 if(strcmp(command, "flash") && !stackpos)
3167 syntaxerror("No movie defined- use .flash first");
3171 printf(".%s\n", command);fflush(stdout);
3172 map_dump(&args, stdout, "\t");fflush(stdout);
3175 (*arguments[nr].func)(&args);
3177 /*if(!strcmp(command, "button") ||
3178 !strcmp(command, "action")) {
3181 if(type == COMMAND) {
3182 if(!strcmp(text, "end"))
3196 int main (int argc,char ** argv)
3199 processargs(argc, argv);
3200 initLog(0,-1,0,0,-1,verbose);
3203 args_callback_usage(argv[0]);
3207 file = generateTokens(filename);
3209 fprintf(stderr, "parser returned error.\n");
3215 while(!noMoreTokens()) {
3218 syntaxerror("command expected");
3219 parseArgumentsForCommand(text);