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;
48 static struct options_t options[] = {
56 int args_callback_option(char*name,char*val)
58 if(!strcmp(name, "V")) {
59 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
62 else if(!strcmp(name, "o")) {
64 override_outputname = 1;
67 else if(!strcmp(name, "O")) {
71 else if(!strcmp(name, "v")) {
76 printf("Unknown option: -%s\n", name);
81 int args_callback_longoption(char*name,char*val)
83 return args_long2shortoption(options, name, val);
85 void args_callback_usage(char *name)
88 printf("Usage: %s [-o file.swf] file.sc\n", name);
90 printf("-h , --help Print short help message and exit\n");
91 printf("-V , --version Print version info and exit\n");
92 printf("-v , --verbose Increase verbosity. \n");
93 printf("-o , --output <filename> Set output file to <filename>.\n");
96 int args_callback_command(char*name,char*val)
99 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
106 static struct token_t* file;
115 static void syntaxerror(char*format, ...)
119 va_start(arglist, format);
120 vsprintf(buf, format, arglist);
122 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
126 static void warning(char*format, ...)
130 va_start(arglist, format);
131 vsprintf(buf, format, arglist);
133 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
136 static void readToken()
138 type = file[pos].type;
140 syntaxerror("unexpected end of file");
142 text = file[pos].text;
143 textlen = strlen(text);
144 line = file[pos].line;
145 column = file[pos].column;
147 //printf("---> %d(%s) %s\n", type, type_names[type], text);
150 static void pushBack()
153 if(!pos) syntaxerror("internal error 3");
158 textlen = strlen(text);
161 column = file[p].column;
164 static int noMoreTokens()
166 if(file[pos].type == END)
171 // ------------------------------ swf routines ----------------------------
175 int type; //0=swf, 1=sprite, 2=clip, 3=button
181 /* for sprites (1): */
187 dictionary_t oldinstances;
192 static int stackpos = 0;
194 static dictionary_t characters;
195 static dictionary_t images;
196 static dictionary_t textures;
197 static dictionary_t outlines;
198 static dictionary_t gradients;
199 static char idmap[65536];
200 static TAG*tag = 0; //current tag
202 static int id; //current character id
203 static int currentframe; //current frame in current level
204 static SRECT currentrect; //current bounding box in current level
205 static U16 currentdepth;
206 static dictionary_t instances;
207 static dictionary_t fonts;
208 static dictionary_t sounds;
210 typedef struct _parameters {
212 float scalex, scaley;
220 typedef struct _character {
226 typedef struct _instance {
227 character_t*character;
229 parameters_t parameters;
230 TAG* lastTag; //last tag which set the object
231 U16 lastFrame; //frame lastTag is in
234 typedef struct _outline {
239 typedef struct _gradient {
245 typedef struct _texture {
249 static void character_init(character_t*c)
251 memset(c, 0, sizeof(character_t));
253 static character_t* character_new()
256 c = (character_t*)malloc(sizeof(character_t));
260 static void instance_init(instance_t*i)
262 memset(i, 0, sizeof(instance_t));
264 static instance_t* instance_new()
267 c = (instance_t*)malloc(sizeof(instance_t));
272 static void incrementid()
276 syntaxerror("Out of character ids.");
281 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
283 character_t* c = character_new();
285 c->definingTag = ctag;
288 if(dictionary_lookup(&characters, name))
289 syntaxerror("character %s defined twice", name);
290 dictionary_put2(&characters, name, c);
292 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
294 swf_SetString(tag, name);
295 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
298 swf_SetString(tag, name);
300 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
302 character_t* c = character_new();
303 c->definingTag = ctag;
307 if(dictionary_lookup(&images, name))
308 syntaxerror("image %s defined twice", name);
309 dictionary_put2(&images, name, c);
311 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
313 instance_t* i = instance_new();
316 //swf_GetMatrix(0, &i->matrix);
317 if(dictionary_lookup(&instances, name))
318 syntaxerror("object %s defined twice", name);
319 dictionary_put2(&instances, name, i);
323 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)
326 p->scalex = scalex; p->scaley = scaley;
327 p->pin = pin; p->pivot = pivot;
328 p->rotate = rotate; p->cxform = cxform;
332 static void parameters_clear(parameters_t*p)
335 p->scalex = 1.0; p->scaley = 1.0;
338 p->pivot.x = 0; p->pivot.y = 0;
341 swf_GetCXForm(0, &p->cxform, 1);
344 static void makeMatrix(MATRIX*m, parameters_t*p)
353 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
354 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
355 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
356 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
358 m->sx = (int)(sx*65536+0.5);
359 m->r1 = (int)(r1*65536+0.5);
360 m->r0 = (int)(r0*65536+0.5);
361 m->sy = (int)(sy*65536+0.5);
365 h = swf_TurnPoint(p->pin, m);
370 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
375 r = swf_TurnRect(rect, &m);
376 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
377 currentrect.xmax == 0 && currentrect.ymax == 0)
380 swf_ExpandRect2(¤trect, &r);
384 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
386 SWF*swf = (SWF*)malloc(sizeof(SWF));
389 syntaxerror(".swf blocks can't be nested");
391 memset(swf, 0, sizeof(swf));
392 swf->fileVersion = version;
394 swf->frameRate = fps;
395 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
396 swf->compressed = compress;
397 swf_SetRGB(tag,&background);
399 if(stackpos==sizeof(stack)/sizeof(stack[0]))
400 syntaxerror("too many levels of recursion");
402 dictionary_init(&characters);
403 dictionary_init(&images);
404 dictionary_init(&textures);
405 dictionary_init(&outlines);
406 dictionary_init(&gradients);
407 dictionary_init(&instances);
408 dictionary_init(&fonts);
409 dictionary_init(&sounds);
411 memset(&stack[stackpos], 0, sizeof(stack[0]));
412 stack[stackpos].type = 0;
413 stack[stackpos].filename = strdup(name);
414 stack[stackpos].swf = swf;
415 stack[stackpos].oldframe = -1;
420 memset(¤trect, 0, sizeof(currentrect));
423 memset(idmap, 0, sizeof(idmap));
427 void s_sprite(char*name)
429 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
430 swf_SetU16(tag, id); //id
431 swf_SetU16(tag, 0); //frames
433 memset(&stack[stackpos], 0, sizeof(stack[0]));
434 stack[stackpos].type = 1;
435 stack[stackpos].oldframe = currentframe;
436 stack[stackpos].olddepth = currentdepth;
437 stack[stackpos].oldrect = currentrect;
438 stack[stackpos].oldinstances = instances;
439 stack[stackpos].tag = tag;
440 stack[stackpos].id = id;
441 stack[stackpos].name = strdup(name);
443 /* FIXME: those four fields should be bundled together */
444 dictionary_init(&instances);
447 memset(¤trect, 0, sizeof(currentrect));
453 typedef struct _buttonrecord
461 typedef struct _button
465 buttonrecord_t records[4];
468 static button_t mybutton;
470 void s_button(char*name)
472 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
473 swf_SetU16(tag, id); //id
474 swf_ButtonSetFlags(tag, 0); //menu=no
476 memset(&mybutton, 0, sizeof(mybutton));
478 memset(&stack[stackpos], 0, sizeof(stack[0]));
479 stack[stackpos].type = 3;
480 stack[stackpos].tag = tag;
481 stack[stackpos].id = id;
482 stack[stackpos].name = strdup(name);
483 stack[stackpos].oldrect = currentrect;
484 memset(¤trect, 0, sizeof(currentrect));
489 void s_buttonput(char*character, char*as, parameters_t p)
491 character_t* c = dictionary_lookup(&characters, character);
496 if(!stackpos || (stack[stackpos-1].type != 3)) {
497 syntaxerror(".show may only appear in .button");
500 syntaxerror("character %s not known (in .shape %s)", character, character);
502 if(mybutton.endofshapes) {
503 syntaxerror("a .do may not precede a .show", character, character);
506 m = s_instancepos(c->size, &p);
514 if(*s==',' || *s==0) {
515 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
516 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
517 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
518 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
519 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
520 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
527 static void setbuttonrecords(TAG*tag)
529 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
530 if(!mybutton.endofshapes) {
533 if(!mybutton.records[3].set) {
534 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
538 if(mybutton.records[t].set) {
539 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
542 swf_SetU8(tag,0); // end of button records
543 mybutton.endofshapes = 1;
547 void s_buttonaction(int flags, char*action)
553 setbuttonrecords(stack[stackpos-1].tag);
555 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
557 syntaxerror("Couldn't compile ActionScript");
560 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
561 swf_ActionSet(stack[stackpos-1].tag, a);
562 mybutton.nr_actions++;
567 static void setactionend(TAG*tag)
569 if(!mybutton.nr_actions) {
570 /* no actions means we didn't have an actionoffset,
571 which means we can't signal the end of the
572 buttonaction records, so, *sigh*, we have
573 to insert a dummy record */
574 swf_SetU16(tag, 0); //offset
575 swf_SetU16(tag, 0); //condition
576 swf_SetU8(tag, 0); //action
580 static void s_endButton()
583 setbuttonrecords(stack[stackpos-1].tag);
584 setactionend(stack[stackpos-1].tag);
587 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
591 tag = stack[stackpos].tag;
592 currentrect = stack[stackpos].oldrect;
594 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
595 free(stack[stackpos].name);
598 TAG* removeFromTo(TAG*from, TAG*to)
600 TAG*save = from->prev;
602 TAG*next = from->next;
610 static void s_endSprite()
612 SRECT r = currentrect;
614 if(stack[stackpos].cut)
615 tag = removeFromTo(stack[stackpos].cut, tag);
619 /* TODO: before clearing, prepend "<spritename>." to names and
620 copy into old instances dict */
621 dictionary_clear(&instances);
623 currentframe = stack[stackpos].oldframe;
624 currentrect = stack[stackpos].oldrect;
625 currentdepth = stack[stackpos].olddepth;
626 instances = stack[stackpos].oldinstances;
628 tag = swf_InsertTag(tag, ST_SHOWFRAME);
629 tag = swf_InsertTag(tag, ST_END);
631 tag = stack[stackpos].tag;
634 syntaxerror("internal error(7)");
636 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
637 free(stack[stackpos].name);
640 static void s_endSWF()
646 if(stack[stackpos].cut)
647 tag = removeFromTo(stack[stackpos].cut, tag);
651 swf = stack[stackpos].swf;
652 filename = stack[stackpos].filename;
654 //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
655 // tag = swf_InsertTag(tag, ST_SHOWFRAME);
656 tag = swf_InsertTag(tag, ST_SHOWFRAME);
658 tag = swf_InsertTag(tag, ST_END);
660 swf_OptimizeTagOrder(swf);
666 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
667 swf->movieSize = currentrect; /* "autocrop" */
670 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
671 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
672 swf->movieSize.ymax += 20;
673 warning("Empty bounding box for movie");
676 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
678 syntaxerror("couldn't create output file %s", filename);
681 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
683 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
687 dictionary_clear(&instances);
688 dictionary_clear(&characters);
689 dictionary_clear(&images);
690 dictionary_clear(&textures);
691 dictionary_clear(&outlines);
692 dictionary_clear(&gradients);
693 dictionary_clear(&fonts);
694 dictionary_clear(&sounds);
704 if(stack[stackpos-1].type == 0)
705 syntaxerror("End of file encountered in .flash block");
706 if(stack[stackpos-1].type == 1)
707 syntaxerror("End of file encountered in .sprite block");
708 if(stack[stackpos-1].type == 2)
709 syntaxerror("End of file encountered in .clip block");
715 return currentframe+1;
718 void s_frame(int nr, int cut, char*name, char anchor)
724 syntaxerror("Illegal frame number");
725 nr--; // internally, frame 1 is frame 0
727 for(t=currentframe;t<nr;t++) {
728 tag = swf_InsertTag(tag, ST_SHOWFRAME);
729 if(t==nr-1 && name && *name) {
730 tag = swf_InsertTag(tag, ST_FRAMELABEL);
731 swf_SetString(tag, name);
733 swf_SetU8(tag, 1); //make this an anchor
736 if(nr == 0 && currentframe == 0 && name && *name) {
737 tag = swf_InsertTag(tag, ST_FRAMELABEL);
738 swf_SetString(tag, name);
740 swf_SetU8(tag, 1); //make this an anchor
745 syntaxerror("Can't cut, frame empty");
747 stack[stackpos].cut = tag;
753 int parseColor2(char*str, RGBA*color);
755 int addFillStyle(SHAPE*s, SRECT*r, char*name)
762 parseColor2(name, &color);
763 return swf_ShapeAddSolidFillStyle(s, &color);
764 } else if ((texture = dictionary_lookup(&textures, name))) {
765 return swf_ShapeAddFillStyle2(s, &texture->fs);
766 } else if((image = dictionary_lookup(&images, name))) {
768 swf_GetMatrix(0, &m);
769 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
770 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
773 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
774 } else if ((gradient = dictionary_lookup(&gradients, name))) {
778 swf_GetMatrix(0, &rot);
779 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
780 csin = sin(-gradient->rotate*2*3.14159265358979/360);
782 rot.r1 = -csin*65536;
785 r2 = swf_TurnRect(*r, &rot);
786 swf_GetMatrix(0, &m);
787 m.sx = (r2.xmax - r2.xmin)*2*ccos;
788 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
789 m.r0 = (r2.ymax - r2.ymin)*2*csin;
790 m.sy = (r2.ymax - r2.ymin)*2*ccos;
791 m.tx = r->xmin + (r->xmax - r->xmin)/2;
792 m.ty = r->ymin + (r->ymax - r->ymin)/2;
793 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
794 } else if (parseColor2(name, &color)) {
795 return swf_ShapeAddSolidFillStyle(s, &color);
797 syntaxerror("not a color/fillstyle: %s", name);
802 RGBA black={r:0,g:0,b:0,a:0};
803 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
812 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
815 linewidth = linewidth>=20?linewidth-20:0;
816 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
819 fs1 = addFillStyle(s, &r2, texture);
822 r.xmin = r2.xmin-linewidth/2;
823 r.ymin = r2.ymin-linewidth/2;
824 r.xmax = r2.xmax+linewidth/2;
825 r.ymax = r2.ymax+linewidth/2;
827 swf_SetShapeHeader(tag,s);
828 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
829 swf_ShapeSetLine(tag,s,width,0);
830 swf_ShapeSetLine(tag,s,0,height);
831 swf_ShapeSetLine(tag,s,-width,0);
832 swf_ShapeSetLine(tag,s,0,-height);
833 swf_ShapeSetEnd(tag);
836 s_addcharacter(name, id, tag, r);
840 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
846 outline = dictionary_lookup(&outlines, outlinename);
848 syntaxerror("outline %s not defined", outlinename);
852 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
855 linewidth = linewidth>=20?linewidth-20:0;
856 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
859 fs1 = addFillStyle(s, &r2, texture);
862 rect.xmin = r2.xmin-linewidth/2;
863 rect.ymin = r2.ymin-linewidth/2;
864 rect.xmax = r2.xmax+linewidth/2;
865 rect.ymax = r2.ymax+linewidth/2;
867 swf_SetRect(tag,&rect);
868 swf_SetShapeStyles(tag, s);
869 swf_ShapeCountBits(s,0,0);
870 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
871 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
872 swf_SetShapeBits(tag, s);
873 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
876 s_addcharacter(name, id, tag, rect);
880 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
885 r2.xmin = r2.ymin = 0;
889 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
892 linewidth = linewidth>=20?linewidth-20:0;
893 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
896 fs1 = addFillStyle(s, &r2, texture);
898 rect.xmin = r2.xmin-linewidth/2;
899 rect.ymin = r2.ymin-linewidth/2;
900 rect.xmax = r2.xmax+linewidth/2;
901 rect.ymax = r2.ymax+linewidth/2;
903 swf_SetRect(tag,&rect);
904 swf_SetShapeHeader(tag,s);
905 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
906 swf_ShapeSetCircle(tag, s, r,r,r,r);
907 swf_ShapeSetEnd(tag);
910 s_addcharacter(name, id, tag, rect);
914 void s_textshape(char*name, char*fontname, float size, char*_text)
917 U8*text = (U8*)_text;
921 font = dictionary_lookup(&fonts, fontname);
923 syntaxerror("font \"%s\" not known!", fontname);
925 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
926 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
927 s_box(name, 0, 0, black, 20, 0);
930 g = font->ascii2glyph[text[0]];
932 outline = malloc(sizeof(outline_t));
933 memset(outline, 0, sizeof(outline_t));
934 outline->shape = font->glyph[g].shape;
935 outline->bbox = font->layout->bounds[g];
939 swf_Shape11DrawerInit(&draw, 0);
940 swf_DrawText(&draw, font, (int)(size*100), _text);
942 outline->shape = swf_ShapeDrawerToShape(&draw);
943 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
947 if(dictionary_lookup(&outlines, name))
948 syntaxerror("outline %s defined twice", name);
949 dictionary_put2(&outlines, name, outline);
952 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
957 font = dictionary_lookup(&fonts, fontname);
959 syntaxerror("font \"%s\" not known!", fontname);
961 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
963 if(!font->numchars) {
964 s_box(name, 0, 0, black, 20, 0);
967 r = swf_SetDefineText(tag, font, &color, text, size);
969 s_addcharacter(name, id, tag, r);
973 void s_quicktime(char*name, char*url)
978 memset(&r, 0, sizeof(r));
980 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
982 swf_SetString(tag, url);
984 s_addcharacter(name, id, tag, r);
988 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)
991 EditTextLayout layout;
994 if(fontname && *fontname) {
995 flags |= ET_USEOUTLINES;
996 font = dictionary_lookup(&fonts, fontname);
998 syntaxerror("font \"%s\" not known!", fontname);
1000 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
1001 swf_SetU16(tag, id);
1002 layout.align = align;
1003 layout.leftmargin = 0;
1004 layout.rightmargin = 0;
1012 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1014 s_addcharacter(name, id, tag, r);
1018 /* type: either "jpeg" or "png"
1020 void s_image(char*name, char*type, char*filename, int quality)
1022 /* an image is actually two folded: 1st bitmap, 2nd character.
1023 Both of them can be used separately */
1025 /* step 1: the bitmap */
1029 if(!strcmp(type,"jpeg")) {
1030 #ifndef HAVE_LIBJPEG
1031 warning("no jpeg support compiled in");
1032 s_box(name, 0, 0, black, 20, 0);
1035 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1036 swf_SetU16(tag, imageID);
1038 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1039 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1042 swf_GetJPEGSize(filename, &width, &height);
1049 s_addimage(name, id, tag, r);
1052 } else if(!strcmp(type,"png")) {
1054 swf_SetU16(tag, imageID);
1056 getPNG(filename, &width, &height, (unsigned char**)&data);
1059 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1062 /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
1063 tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
1064 swf_SetU16(tag, imageID);
1065 swf_SetLosslessImage(tag, data, width, height);
1071 s_addimage(name, id, tag, r);
1074 warning("image type \"%s\" not supported yet!", type);
1075 s_box(name, 0, 0, black, 20, 0);
1079 /* step 2: the character */
1080 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1081 swf_SetU16(tag, id);
1082 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1084 s_addcharacter(name, id, tag, r);
1088 void s_getBitmapSize(char*name, int*width, int*height)
1090 character_t* image = dictionary_lookup(&images, name);
1091 gradient_t* gradient = dictionary_lookup(&gradients,name);
1093 *width = image->size.xmax;
1094 *height = image->size.ymax;
1098 /* internal SWF gradient size */
1099 if(gradient->radial) {
1108 syntaxerror("No such bitmap/gradient: %s", name);
1111 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1113 gradient_t* gradient = dictionary_lookup(&gradients, object);
1114 character_t* bitmap = dictionary_lookup(&images, object);
1115 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1117 FILLSTYLE*fs = &texture->fs;
1119 memset(&p, 0, sizeof(parameters_t));
1122 fs->type = FILL_TILED;
1123 fs->id_bitmap = bitmap->id;
1124 } else if(gradient) {
1125 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1126 fs->gradient = gradient->gradient;
1128 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1129 makeMatrix(&fs->m, &p);
1130 if(gradient && !gradient->radial) {
1137 p2 = swf_TurnPoint(p1, &m);
1142 if(dictionary_lookup(&textures, name))
1143 syntaxerror("texture %s defined twice", name);
1144 dictionary_put2(&textures, name, texture);
1147 void dumpSWF(SWF*swf)
1149 TAG* tag = swf->firstTag;
1150 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1152 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1155 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1158 void s_font(char*name, char*filename)
1161 font = swf_LoadFont(filename);
1164 warning("Couldn't open font file \"%s\"", filename);
1165 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1166 memset(font, 0, sizeof(SWFFONT));
1167 dictionary_put2(&fonts, name, font);
1173 /* fix the layout. Only needed for old fonts */
1175 for(t=0;t<font->numchars;t++) {
1176 font->glyph[t].advance = 0;
1179 swf_FontCreateLayout(font);
1181 /* just in case this thing is used in .edittext later on */
1182 swf_FontPrepareForEditText(font);
1185 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1186 swf_FontSetDefine2(tag, font);
1187 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1189 swf_SetU16(tag, id);
1190 swf_SetString(tag, name);
1193 if(dictionary_lookup(&fonts, name))
1194 syntaxerror("font %s defined twice", name);
1195 dictionary_put2(&fonts, name, font);
1200 typedef struct _sound_t
1206 void s_sound(char*name, char*filename)
1208 struct WAV wav, wav2;
1212 unsigned numsamples;
1213 unsigned blocksize = 1152;
1216 if(wav_read(&wav, filename)) {
1218 wav_convert2mono(&wav, &wav2, 44100);
1219 samples = (U16*)wav2.data;
1220 numsamples = wav2.size/2;
1222 #ifdef WORDS_BIGENDIAN
1224 for(t=0;t<numsamples;t++) {
1225 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1228 } else if(mp3_read(&mp3, filename)) {
1229 fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1235 warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1240 if(numsamples%blocksize != 0)
1242 // apply padding, so that block is a multiple of blocksize
1243 int numblocks = (numsamples+blocksize-1)/blocksize;
1246 numsamples2 = numblocks * blocksize;
1247 samples2 = malloc(sizeof(U16)*numsamples2);
1248 memcpy(samples2, samples, numsamples*sizeof(U16));
1249 memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1250 numsamples = numsamples2;
1254 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1255 swf_SetU16(tag, id); //id
1258 swf_SetSoundDefineMP3(
1259 tag, mp3.data, mp3.size,
1267 swf_SetSoundDefine(tag, samples, numsamples);
1270 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1274 if(dictionary_lookup(&sounds, name))
1275 syntaxerror("sound %s defined twice", name);
1276 dictionary_put2(&sounds, name, sound);
1284 static char* gradient_getToken(const char**p)
1288 while(**p && strchr(" \t\n\r", **p)) {
1292 while(**p && !strchr(" \t\n\r", **p)) {
1295 result = malloc((*p)-start+1);
1296 memcpy(result,start,(*p)-start+1);
1297 result[(*p)-start] = 0;
1301 float parsePercent(char*str);
1302 RGBA parseColor(char*str);
1304 GRADIENT parseGradient(const char*str)
1308 const char* p = str;
1309 memset(&gradient, 0, sizeof(GRADIENT));
1311 char*posstr,*colorstr;
1314 posstr = gradient_getToken(&p);
1317 pos = (int)(parsePercent(posstr)*255.0);
1320 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1321 colorstr = gradient_getToken(&p);
1322 color = parseColor(colorstr);
1323 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1324 warning("gradient record too big- max size is 8, rest ignored");
1327 gradient.ratios[gradient.num] = pos;
1328 gradient.rgba[gradient.num] = color;
1337 void s_gradient(char*name, const char*text, int radial, int rotate)
1339 gradient_t* gradient;
1340 gradient = malloc(sizeof(gradient_t));
1341 memset(gradient, 0, sizeof(gradient_t));
1342 gradient->gradient = parseGradient(text);
1343 gradient->radial = radial;
1344 gradient->rotate = rotate;
1346 if(dictionary_lookup(&gradients, name))
1347 syntaxerror("gradient %s defined twice", name);
1348 dictionary_put2(&gradients, name, gradient);
1351 void s_action(const char*text)
1354 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1356 syntaxerror("Couldn't compile ActionScript");
1359 tag = swf_InsertTag(tag, ST_DOACTION);
1361 swf_ActionSet(tag, a);
1366 void s_initaction(const char*character, const char*text)
1370 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1372 syntaxerror("Couldn't compile ActionScript");
1375 c = (character_t*)dictionary_lookup(&characters, character);
1377 tag = swf_InsertTag(tag, ST_DOINITACTION);
1378 swf_SetU16(tag, c->id);
1379 swf_ActionSet(tag, a);
1384 int s_swf3action(char*name, char*action)
1387 instance_t* object = 0;
1389 object = (instance_t*)dictionary_lookup(&instances, name);
1390 if(!object && name && *name) {
1391 /* we have a name, but couldn't find it. Abort. */
1394 a = action_SetTarget(0, name);
1395 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1396 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1397 else if(!strcmp(action, "stop")) a = action_Stop(a);
1398 else if(!strcmp(action, "play")) a = action_Play(a);
1399 a = action_SetTarget(a, "");
1402 tag = swf_InsertTag(tag, ST_DOACTION);
1403 swf_ActionSet(tag, a);
1408 void s_outline(char*name, char*format, char*source)
1417 //swf_Shape10DrawerInit(&draw, 0);
1418 swf_Shape11DrawerInit(&draw, 0);
1420 draw_string(&draw, source);
1422 shape = swf_ShapeDrawerToShape(&draw);
1423 bounds = swf_ShapeDrawerGetBBox(&draw);
1424 draw.dealloc(&draw);
1426 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1427 outline->shape = shape;
1428 outline->bbox = bounds;
1430 if(dictionary_lookup(&outlines, name))
1431 syntaxerror("outline %s defined twice", name);
1432 dictionary_put2(&outlines, name, outline);
1435 int s_playsound(char*name, int loops, int nomultiple, int stop)
1441 sound = dictionary_lookup(&sounds, name);
1445 tag = swf_InsertTag(tag, ST_STARTSOUND);
1446 swf_SetU16(tag, sound->id); //id
1447 memset(&info, 0, sizeof(info));
1450 info.nomultiple = nomultiple;
1451 swf_SetSoundInfo(tag, &info);
1455 void s_includeswf(char*name, char*filename)
1463 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1464 f = open(filename,O_RDONLY|O_BINARY);
1466 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1467 s_box(name, 0, 0, black, 20, 0);
1470 if (swf_ReadSWF(f,&swf)<0) {
1471 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1472 s_box(name, 0, 0, black, 20, 0);
1477 /* FIXME: The following sets the bounding Box for the character.
1478 It is wrong for two reasons:
1479 a) It may be too small (in case objects in the movie clip at the borders)
1480 b) it may be too big (because the poor movie never got autocropped)
1484 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1485 swf_SetU16(tag, id);
1486 swf_SetU16(tag, swf.frameCount);
1488 swf_Relocate(&swf, idmap);
1490 ftag = swf.firstTag;
1494 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1495 if(cutout[t] == ftag->id) {
1499 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1501 if(ftag->id == ST_END)
1505 /* We simply dump all tags right after the sprite
1506 header, relying on the fact that swf_OptimizeTagOrder() will
1507 sort things out for us later.
1508 We also rely on the fact that the imported SWF is well-formed.
1510 tag = swf_InsertTag(tag, ftag->id);
1511 swf_SetBlock(tag, ftag->data, ftag->len);
1515 syntaxerror("Included file %s contains errors", filename);
1516 tag = swf_InsertTag(tag, ST_END);
1520 s_addcharacter(name, id, tag, r);
1523 SRECT s_getCharBBox(char*name)
1525 character_t* c = dictionary_lookup(&characters, name);
1526 if(!c) syntaxerror("character '%s' unknown(2)", name);
1529 SRECT s_getInstanceBBox(char*name)
1531 instance_t * i = dictionary_lookup(&instances, name);
1533 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1535 if(!c) syntaxerror("internal error(5)");
1538 parameters_t s_getParameters(char*name)
1540 instance_t * i = dictionary_lookup(&instances, name);
1541 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1542 return i->parameters;
1544 void s_startclip(char*instance, char*character, parameters_t p)
1546 character_t* c = dictionary_lookup(&characters, character);
1550 syntaxerror("character %s not known", character);
1552 i = s_addinstance(instance, c, currentdepth);
1554 m = s_instancepos(i->character->size, &p);
1556 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1557 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1558 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1560 i->lastFrame= currentframe;
1562 stack[stackpos].tag = tag;
1563 stack[stackpos].type = 2;
1572 swf_SetTagPos(stack[stackpos].tag, 0);
1573 swf_GetPlaceObject(stack[stackpos].tag, &p);
1574 p.clipdepth = currentdepth;
1576 swf_ClearTag(stack[stackpos].tag);
1577 swf_SetPlaceObject(stack[stackpos].tag, &p);
1581 void s_put(char*instance, char*character, parameters_t p)
1583 character_t* c = dictionary_lookup(&characters, character);
1587 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1590 i = s_addinstance(instance, c, currentdepth);
1592 m = s_instancepos(i->character->size, &p);
1594 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1595 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1597 i->lastFrame = currentframe;
1601 void s_jump(char*instance, parameters_t p)
1603 instance_t* i = dictionary_lookup(&instances, instance);
1606 syntaxerror("instance %s not known", instance);
1610 m = s_instancepos(i->character->size, &p);
1612 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1613 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1615 i->lastFrame = currentframe;
1618 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1622 if(num==0 || num==1)
1624 ratio = (float)pos/(float)num;
1626 p.x = (p2->x-p1->x)*ratio + p1->x;
1627 p.y = (p2->y-p1->y)*ratio + p1->y;
1628 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1629 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1630 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1631 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1633 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1634 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1635 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1636 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1638 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1639 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1640 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1641 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1643 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1644 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1645 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1646 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1650 void s_change(char*instance, parameters_t p2)
1652 instance_t* i = dictionary_lookup(&instances, instance);
1656 int frame, allframes;
1658 syntaxerror("instance %s not known", instance);
1662 allframes = currentframe - i->lastFrame - 1;
1664 warning(".change ignored. can only .put/.change an object once per frame.");
1668 m = s_instancepos(i->character->size, &p2);
1669 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1670 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1673 /* o.k., we got the start and end point set. Now iterate though all the
1674 tags in between, inserting object changes after each new frame */
1677 if(!t) syntaxerror("internal error(6)");
1679 while(frame < allframes) {
1680 if(t->id == ST_SHOWFRAME) {
1685 p = s_interpolate(&p1, &p2, frame, allframes);
1686 m = s_instancepos(i->character->size, &p); //needed?
1687 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1688 i->lastFrame = currentframe;
1689 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1691 if(frame == allframes)
1696 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1700 void s_delinstance(char*instance)
1702 instance_t* i = dictionary_lookup(&instances, instance);
1704 syntaxerror("instance %s not known", instance);
1706 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1707 swf_SetU16(tag, i->depth);
1708 dictionary_del(&instances, instance);
1711 void s_qchange(char*instance, parameters_t p)
1718 syntaxerror(".end unexpected");
1719 if(stack[stackpos-1].type == 0)
1721 else if(stack[stackpos-1].type == 1)
1723 else if(stack[stackpos-1].type == 2)
1725 else if(stack[stackpos-1].type == 3)
1727 else syntaxerror("internal error 1");
1730 // ------------------------------------------------------------------------
1732 typedef int command_func_t(map_t*args);
1734 SRECT parseBox(char*str)
1737 float xmin, xmax, ymin, ymax;
1738 char*x = strchr(str, 'x');
1740 if(!strcmp(str, "autocrop")) {
1741 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1745 d1 = strchr(x+1, ':');
1747 d2 = strchr(d1+1, ':');
1749 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1753 else if(d1 && !d2) {
1754 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1760 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1765 r.xmin = (SCOORD)(xmin*20);
1766 r.ymin = (SCOORD)(ymin*20);
1767 r.xmax = (SCOORD)(xmax*20);
1768 r.ymax = (SCOORD)(ymax*20);
1771 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1774 float parseFloat(char*str)
1778 int parseInt(char*str)
1783 if(str[0]=='+' || str[0]=='-')
1787 if(str[t]<'0' || str[t]>'9')
1788 syntaxerror("Not an Integer: \"%s\"", str);
1791 int parseTwip(char*str)
1795 if(str[0]=='+' || str[0]=='-') {
1800 dot = strchr(str, '.');
1804 return sign*parseInt(str)*20;
1806 char* old = strdup(str);
1807 int l=strlen(dot+1);
1810 for(s=str;s<dot-1;s++)
1811 if(*s<'0' || *s>'9')
1812 syntaxerror("Not a coordinate: \"%s\"", str);
1814 if(*s<'0' || *s>'9')
1815 syntaxerror("Not a coordinate: \"%s\"", str);
1817 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1818 dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
1821 warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
1825 return sign*atoi(str)*20;
1827 return sign*atoi(str)*20+atoi(dot)*2;
1829 return sign*atoi(str)*20+atoi(dot)/5;
1834 int isPoint(char*str)
1836 if(strchr(str, '('))
1842 SPOINT parsePoint(char*str)
1846 int l = strlen(str);
1847 char*comma = strchr(str, ',');
1848 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1849 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1850 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1851 p.x = parseTwip(tmp);
1852 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1853 p.y = parseTwip(tmp);
1857 int parseColor2(char*str, RGBA*color)
1859 int l = strlen(str);
1863 struct {unsigned char r,g,b;char*name;} colors[] =
1864 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1865 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1866 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1867 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1868 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1869 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1870 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1871 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1872 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1873 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1874 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1875 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1879 if(str[0]=='#' && (l==7 || l==9)) {
1880 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1882 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1884 color->r = r; color->g = g; color->b = b; color->a = a;
1887 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1888 if(!strcmp(str, colors[t].name)) {
1893 color->r = r; color->g = g; color->b = b; color->a = a;
1899 RGBA parseColor(char*str)
1902 if(!parseColor2(str, &c))
1903 syntaxerror("Expression '%s' is not a color", str);
1907 typedef struct _muladd {
1912 MULADD parseMulAdd(char*str)
1915 char* str2 = (char*)malloc(strlen(str)+5);
1922 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1923 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1924 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1925 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1926 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1927 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1928 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1929 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1930 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1931 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1933 syntaxerror("'%s' is not a valid color transform expression", str);
1935 m.add = (int)(add*256);
1936 m.mul = (int)(mul*256);
1941 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1943 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1944 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1946 if(a<-32768) a=-32768;
1947 if(a>32767) a=32767;
1948 if(m<-32768) m=-32768;
1949 if(m>32767) m=32767;
1955 float parsePxOrPercent(char*fontname, char*str)
1957 int l = strlen(str);
1958 if(strchr(str, '%'))
1959 return parsePercent(str);
1960 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1961 float p = atof(str);
1962 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1964 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1968 float parsePercent(char*str)
1970 int l = strlen(str);
1974 return atoi(str)/100.0;
1976 syntaxerror("Expression '%s' is not a percentage", str);
1979 int isPercent(char*str)
1981 return str[strlen(str)-1]=='%';
1983 int parseNewSize(char*str, int size)
1986 return parsePercent(str)*size;
1988 return (int)(atof(str)*20);
1991 int isColor(char*str)
1994 return parseColor2(str, &c);
1997 static char* lu(map_t* args, char*name)
1999 char* value = map_lookup(args, name);
2001 map_dump(args, stdout, "");
2002 syntaxerror("internal error 2: value %s should be set", name);
2007 static int c_flash(map_t*args)
2009 char* filename = map_lookup(args, "filename");
2010 char* compressstr = lu(args, "compress");
2011 SRECT bbox = parseBox(lu(args, "bbox"));
2012 int version = parseInt(lu(args, "version"));
2013 int fps = (int)(parseFloat(lu(args, "fps"))*256);
2015 RGBA color = parseColor(lu(args, "background"));
2017 if(!filename || !*filename) {
2018 /* for compatibility */
2019 filename = map_lookup(args, "name");
2020 if(!filename || !*filename) {
2023 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2024 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2028 if(!filename || override_outputname)
2029 filename = outputname;
2031 if(!strcmp(compressstr, "default"))
2032 compress = version==6;
2033 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2035 else if(!strcmp(compressstr, "no"))
2037 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2039 s_swf(filename, bbox, version, fps, compress, color);
2042 int isRelative(char*str)
2044 return !strncmp(str, "<plus>", 6) ||
2045 !strncmp(str, "<minus>", 7);
2047 char* getOffset(char*str)
2049 if(!strncmp(str, "<plus>", 6))
2051 if(!strncmp(str, "<minus>", 7))
2053 syntaxerror("internal error (347)");
2056 int getSign(char*str)
2058 if(!strncmp(str, "<plus>", 6))
2060 if(!strncmp(str, "<minus>", 7))
2062 syntaxerror("internal error (348)");
2065 static dictionary_t points;
2066 static mem_t mpoints;
2067 int points_initialized = 0;
2069 SPOINT getPoint(SRECT r, char*name)
2072 if(!strcmp(name, "center")) {
2074 p.x = (r.xmin + r.xmax)/2;
2075 p.y = (r.ymin + r.ymax)/2;
2079 if(points_initialized)
2080 l = (int)dictionary_lookup(&points, name);
2082 syntaxerror("Invalid point: \"%s\".", name);
2085 return *(SPOINT*)&mpoints.buffer[l];
2088 static int texture2(char*name, char*object, map_t*args, int errors)
2091 char*xstr = map_lookup(args, "x");
2092 char*ystr = map_lookup(args, "y");
2093 char*widthstr = map_lookup(args, "width");
2094 char*heightstr = map_lookup(args, "height");
2095 char*scalestr = map_lookup(args, "scale");
2096 char*scalexstr = map_lookup(args, "scalex");
2097 char*scaleystr = map_lookup(args, "scaley");
2098 char*rotatestr = map_lookup(args, "rotate");
2099 char* shearstr = map_lookup(args, "shear");
2100 char* radiusstr = map_lookup(args, "r");
2102 float scalex = 1.0, scaley = 1.0;
2103 float rotate=0, shear=0;
2105 if(!*xstr && !*ystr) {
2107 syntaxerror("x and y must be set");
2110 if(*scalestr && (*scalexstr || *scaleystr)) {
2111 syntaxerror("scale and scalex/scaley can't both be set");
2114 if((*widthstr || *heightstr) && *radiusstr) {
2115 syntaxerror("width/height and radius can't both be set");
2118 widthstr = radiusstr;
2119 heightstr = radiusstr;
2121 if(!*xstr) xstr="0";
2122 if(!*ystr) ystr="0";
2123 if(!*rotatestr) rotatestr="0";
2124 if(!*shearstr) shearstr="0";
2127 scalex = scaley = parsePercent(scalestr);
2128 } else if(*scalexstr || *scaleystr) {
2129 if(scalexstr) scalex = parsePercent(scalexstr);
2130 if(scaleystr) scaley = parsePercent(scaleystr);
2131 } else if(*widthstr || *heightstr) {
2134 s_getBitmapSize(object, &width, &height);
2136 scalex = (float)parseTwip(widthstr)/(float)width;
2138 scaley = (float)parseTwip(heightstr)/(float)height;
2140 x = parseTwip(xstr);
2141 y = parseTwip(ystr);
2142 rotate = parseFloat(rotatestr);
2143 shear = parseFloat(shearstr);
2145 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2150 static int c_texture(map_t*args)
2152 char*name = lu(args, "instance");
2153 char*object = lu(args, "character");
2154 return texture2(name, object, args, 1);
2157 static int c_gradient(map_t*args)
2159 char*name = lu(args, "name");
2160 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2161 int rotate = parseInt(lu(args, "rotate"));
2165 syntaxerror("colon (:) expected");
2167 s_gradient(name, text, radial, rotate);
2169 /* check whether we also have placement information,
2170 which would make this a positioned gradient.
2171 If there is placement information, texture2() will
2172 add a texture, which has priority over the gradient.
2174 texture2(name, name, args, 0);
2177 static int c_point(map_t*args)
2179 char*name = lu(args, "name");
2183 if(!points_initialized) {
2184 dictionary_init(&points);
2186 points_initialized = 1;
2188 p.x = parseTwip(lu(args, "x"));
2189 p.y = parseTwip(lu(args, "y"));
2190 pos = mem_put(&mpoints, &p, sizeof(p));
2191 string_set(&s1, name);
2193 dictionary_put(&points, s1, (void*)pos);
2196 static int c_play(map_t*args)
2198 char*name = lu(args, "name");
2199 char*loop = lu(args, "loop");
2200 char*nomultiple = lu(args, "nomultiple");
2202 if(!strcmp(nomultiple, "nomultiple"))
2205 nm = parseInt(nomultiple);
2207 if(s_playsound(name, parseInt(loop), nm, 0)) {
2209 } else if(s_swf3action(name, "play")) {
2215 static int c_stop(map_t*args)
2217 char*name = map_lookup(args, "name");
2219 if(s_playsound(name, 0,0,1)) {
2221 } else if(s_swf3action(name, "stop")) {
2224 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2228 static int c_nextframe(map_t*args)
2230 char*name = lu(args, "name");
2232 if(s_swf3action(name, "nextframe")) {
2235 syntaxerror("I don't know anything about movie \"%s\"", name);
2239 static int c_previousframe(map_t*args)
2241 char*name = lu(args, "name");
2243 if(s_swf3action(name, "previousframe")) {
2246 syntaxerror("I don't know anything about movie \"%s\"", name);
2250 static int c_placement(map_t*args, int type)
2252 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2255 char* luminancestr = lu(args, "luminance");
2256 char* scalestr = lu(args, "scale");
2257 char* scalexstr = lu(args, "scalex");
2258 char* scaleystr = lu(args, "scaley");
2259 char* rotatestr = lu(args, "rotate");
2260 char* shearstr = lu(args, "shear");
2261 char* xstr="", *pivotstr="";
2262 char* ystr="", *anglestr="";
2263 char*above = lu(args, "above"); /*FIXME*/
2264 char*below = lu(args, "below");
2265 char* rstr = lu(args, "red");
2266 char* gstr = lu(args, "green");
2267 char* bstr = lu(args, "blue");
2268 char* astr = lu(args, "alpha");
2269 char* pinstr = lu(args, "pin");
2270 char* as = map_lookup(args, "as");
2278 if(type==9) { // (?) .rotate or .arcchange
2279 pivotstr = lu(args, "pivot");
2280 anglestr = lu(args, "angle");
2282 xstr = lu(args, "x");
2283 ystr = lu(args, "y");
2286 luminance = parseMulAdd(luminancestr);
2289 luminance.mul = 256;
2293 if(scalexstr[0]||scaleystr[0])
2294 syntaxerror("scalex/scaley and scale cannot both be set");
2295 scalexstr = scaleystr = scalestr;
2298 if(type == 0 || type == 4) {
2300 character = lu(args, "character");
2301 parameters_clear(&p);
2302 } else if (type == 5) {
2303 character = lu(args, "name");
2304 parameters_clear(&p);
2307 p = s_getParameters(instance);
2312 if(isRelative(xstr)) {
2313 if(type == 0 || type == 4)
2314 syntaxerror("relative x values not allowed for initial put or startclip");
2315 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2317 p.x = parseTwip(xstr);
2321 if(isRelative(ystr)) {
2322 if(type == 0 || type == 4)
2323 syntaxerror("relative y values not allowed for initial put or startclip");
2324 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2326 p.y = parseTwip(ystr);
2330 /* scale, scalex, scaley */
2332 oldbbox = s_getCharBBox(character);
2334 oldbbox = s_getInstanceBBox(instance);
2336 oldwidth = oldbbox.xmax - oldbbox.xmin;
2337 oldheight = oldbbox.ymax - oldbbox.ymin;
2339 if(oldwidth==0) p.scalex = 1.0;
2342 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2346 if(oldheight==0) p.scaley = 1.0;
2349 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2355 if(isRelative(rotatestr)) {
2356 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2358 p.rotate = parseFloat(rotatestr);
2364 if(isRelative(shearstr)) {
2365 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2367 p.shear = parseFloat(shearstr);
2372 if(isPoint(pivotstr))
2373 p.pivot = parsePoint(pivotstr);
2375 p.pivot = getPoint(oldbbox, pivotstr);
2379 p.pin = parsePoint(pinstr);
2381 p.pin = getPoint(oldbbox, pinstr);
2384 /* color transform */
2386 if(rstr[0] || luminancestr[0]) {
2389 r = parseMulAdd(rstr);
2391 r.add = p.cxform.r0;
2392 r.mul = p.cxform.r1;
2394 r = mergeMulAdd(r, luminance);
2395 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2397 if(gstr[0] || luminancestr[0]) {
2400 g = parseMulAdd(gstr);
2402 g.add = p.cxform.g0;
2403 g.mul = p.cxform.g1;
2405 g = mergeMulAdd(g, luminance);
2406 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2408 if(bstr[0] || luminancestr[0]) {
2411 b = parseMulAdd(bstr);
2413 b.add = p.cxform.b0;
2414 b.mul = p.cxform.b1;
2416 b = mergeMulAdd(b, luminance);
2417 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2420 MULADD a = parseMulAdd(astr);
2421 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2425 s_put(instance, character, p);
2427 s_change(instance, p);
2429 s_qchange(instance, p);
2431 s_jump(instance, p);
2433 s_startclip(instance, character, p);
2434 else if(type == 5) {
2436 s_buttonput(character, as, p);
2438 s_buttonput(character, "shape", p);
2443 static int c_put(map_t*args)
2445 c_placement(args, 0);
2448 static int c_change(map_t*args)
2450 c_placement(args, 1);
2453 static int c_qchange(map_t*args)
2455 c_placement(args, 2);
2458 static int c_arcchange(map_t*args)
2460 c_placement(args, 2);
2463 static int c_jump(map_t*args)
2465 c_placement(args, 3);
2468 static int c_startclip(map_t*args)
2470 c_placement(args, 4);
2473 static int c_show(map_t*args)
2475 c_placement(args, 5);
2478 static int c_del(map_t*args)
2480 char*instance = lu(args, "name");
2481 s_delinstance(instance);
2484 static int c_end(map_t*args)
2489 static int c_sprite(map_t*args)
2491 char* name = lu(args, "name");
2495 static int c_frame(map_t*args)
2497 char*framestr = lu(args, "n");
2498 char*cutstr = lu(args, "cut");
2500 char*name = lu(args, "name");
2501 char*anchor = lu(args, "anchor");
2504 if(!strcmp(anchor, "anchor") && !*name)
2509 if(strcmp(cutstr, "no"))
2511 if(isRelative(framestr)) {
2512 frame = s_getframe();
2513 if(getSign(framestr)<0)
2514 syntaxerror("relative frame expressions must be positive");
2515 frame += parseInt(getOffset(framestr));
2518 frame = parseInt(framestr);
2519 if(s_getframe() >= frame
2520 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2521 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2523 s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
2526 static int c_primitive(map_t*args)
2528 char*name = lu(args, "name");
2529 char*command = lu(args, "commandname");
2530 int width=0, height=0, r=0;
2531 int linewidth = parseTwip(lu(args, "line"));
2532 char*colorstr = lu(args, "color");
2533 RGBA color = parseColor(colorstr);
2534 char*fillstr = lu(args, "fill");
2541 if(!strcmp(command, "circle"))
2543 else if(!strcmp(command, "filled"))
2547 width = parseTwip(lu(args, "width"));
2548 height = parseTwip(lu(args, "height"));
2549 } else if (type==1) {
2550 r = parseTwip(lu(args, "r"));
2551 } else if (type==2) {
2552 outline = lu(args, "outline");
2555 if(!strcmp(fillstr, "fill"))
2557 if(!strcmp(fillstr, "none"))
2559 if(width<0 || height<0 || linewidth<0 || r<0)
2560 syntaxerror("values width, height, line, r must be positive");
2562 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2563 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2564 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2568 static int c_textshape(map_t*args)
2570 char*name = lu(args, "name");
2571 char*text = lu(args, "text");
2572 char*font = lu(args, "font");
2573 float size = parsePxOrPercent(font, lu(args, "size"));
2575 s_textshape(name, font, size, text);
2579 static int c_swf(map_t*args)
2581 char*name = lu(args, "name");
2582 char*filename = lu(args, "filename");
2583 char*command = lu(args, "commandname");
2584 if(!strcmp(command, "shape"))
2585 warning("Please use .swf instead of .shape");
2586 s_includeswf(name, filename);
2590 static int c_font(map_t*args)
2592 char*name = lu(args, "name");
2593 char*filename = lu(args, "filename");
2594 s_font(name, filename);
2598 static int c_sound(map_t*args)
2600 char*name = lu(args, "name");
2601 char*filename = lu(args, "filename");
2602 s_sound(name, filename);
2606 static int c_text(map_t*args)
2608 char*name = lu(args, "name");
2609 char*text = lu(args, "text");
2610 char*font = lu(args, "font");
2611 float size = parsePxOrPercent(font, lu(args, "size"));
2612 RGBA color = parseColor(lu(args, "color"));
2613 s_text(name, font, text, (int)(size*100), color);
2617 static int c_soundtrack(map_t*args)
2622 static int c_quicktime(map_t*args)
2624 char*name = lu(args, "name");
2625 char*url = lu(args, "url");
2626 s_quicktime(name, url);
2630 static int c_image(map_t*args)
2632 char*command = lu(args, "commandname");
2633 char*name = lu(args, "name");
2634 char*filename = lu(args, "filename");
2635 if(!strcmp(command,"jpeg")) {
2636 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2637 s_image(name, "jpeg", filename, quality);
2639 s_image(name, "png", filename, 0);
2644 static int c_outline(map_t*args)
2646 char*name = lu(args, "name");
2647 char*format = lu(args, "format");
2651 syntaxerror("colon (:) expected");
2653 s_outline(name, format, text);
2657 int fakechar(map_t*args)
2659 char*name = lu(args, "name");
2660 s_box(name, 0, 0, black, 20, 0);
2664 static int c_egon(map_t*args) {return fakechar(args);}
2665 static int c_button(map_t*args) {
2666 char*name = lu(args, "name");
2670 static int current_button_flags = 0;
2671 static int c_on_press(map_t*args)
2673 char*position = lu(args, "position");
2675 if(!strcmp(position, "inside")) {
2676 current_button_flags |= BC_OVERUP_OVERDOWN;
2677 } else if(!strcmp(position, "outside")) {
2678 //current_button_flags |= BC_IDLE_OUTDOWN;
2679 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2680 } else if(!strcmp(position, "anywhere")) {
2681 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2684 if(type == RAWDATA) {
2686 s_buttonaction(current_button_flags, action);
2687 current_button_flags = 0;
2693 static int c_on_release(map_t*args)
2695 char*position = lu(args, "position");
2697 if(!strcmp(position, "inside")) {
2698 current_button_flags |= BC_OVERDOWN_OVERUP;
2699 } else if(!strcmp(position, "outside")) {
2700 current_button_flags |= BC_OUTDOWN_IDLE;
2701 } else if(!strcmp(position, "anywhere")) {
2702 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2705 if(type == RAWDATA) {
2707 s_buttonaction(current_button_flags, action);
2708 current_button_flags = 0;
2714 static int c_on_move_in(map_t*args)
2716 char*position = lu(args, "state");
2718 if(!strcmp(position, "pressed")) {
2719 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2720 } else if(!strcmp(position, "not_pressed")) {
2721 current_button_flags |= BC_IDLE_OVERUP;
2722 } else if(!strcmp(position, "any")) {
2723 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2726 if(type == RAWDATA) {
2728 s_buttonaction(current_button_flags, action);
2729 current_button_flags = 0;
2735 static int c_on_move_out(map_t*args)
2737 char*position = lu(args, "state");
2739 if(!strcmp(position, "pressed")) {
2740 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2741 } else if(!strcmp(position, "not_pressed")) {
2742 current_button_flags |= BC_OVERUP_IDLE;
2743 } else if(!strcmp(position, "any")) {
2744 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2747 if(type == RAWDATA) {
2749 s_buttonaction(current_button_flags, action);
2750 current_button_flags = 0;
2756 static int c_on_key(map_t*args)
2758 char*key = lu(args, "key");
2760 if(strlen(key)==1) {
2763 current_button_flags |= 0x4000 + (key[0]*0x200);
2765 syntaxerror("invalid character: %c"+key[0]);
2770 <ctrl-x> = 0x200*(x-'a')
2774 syntaxerror("invalid key: %s",key);
2777 if(type == RAWDATA) {
2779 s_buttonaction(current_button_flags, action);
2780 current_button_flags = 0;
2787 static int c_edittext(map_t*args)
2789 //"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"},
2790 char*name = lu(args, "name");
2791 char*font = lu(args, "font");
2792 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2793 int width = parseTwip(lu(args, "width"));
2794 int height = parseTwip(lu(args, "height"));
2795 char*text = lu(args, "text");
2796 RGBA color = parseColor(lu(args, "color"));
2797 int maxlength = parseInt(lu(args, "maxlength"));
2798 char*variable = lu(args, "variable");
2799 char*passwordstr = lu(args, "password");
2800 char*wordwrapstr = lu(args, "wordwrap");
2801 char*multilinestr = lu(args, "multiline");
2802 char*htmlstr = lu(args, "html");
2803 char*noselectstr = lu(args, "noselect");
2804 char*readonlystr = lu(args, "readonly");
2805 char*borderstr = lu(args, "border");
2806 char*autosizestr = lu(args, "autosize");
2807 char*alignstr = lu(args, "align");
2811 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2812 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2813 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2814 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2815 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2816 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2817 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2818 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2819 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2820 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2821 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2822 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2823 else syntaxerror("Unknown alignment: %s", alignstr);
2825 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2829 static int c_morphshape(map_t*args) {return fakechar(args);}
2830 static int c_movie(map_t*args) {return fakechar(args);}
2832 static char* readfile(const char*filename)
2834 FILE*fi = fopen(filename, "rb");
2838 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2839 fseek(fi, 0, SEEK_END);
2841 fseek(fi, 0, SEEK_SET);
2842 text = rfx_alloc(l+1);
2843 fread(text, l, 1, fi);
2849 static int c_action(map_t*args)
2851 char* filename = map_lookup(args, "filename");
2852 if(!filename ||!*filename) {
2854 if(type != RAWDATA) {
2855 syntaxerror("colon (:) expected");
2859 s_action(readfile(filename));
2865 static int c_initaction(map_t*args)
2867 char* character = lu(args, "name");
2868 char* filename = map_lookup(args, "filename");
2869 if(!filename ||!*filename) {
2871 if(type != RAWDATA) {
2872 syntaxerror("colon (:) expected");
2874 s_initaction(character, text);
2876 s_initaction(character, readfile(filename));
2884 command_func_t* func;
2887 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2888 {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
2889 // "import" type stuff
2890 {"swf", c_swf, "name filename"},
2891 {"shape", c_swf, "name filename"},
2892 {"jpeg", c_image, "name filename quality=80%"},
2893 {"png", c_image, "name filename"},
2894 {"movie", c_movie, "name filename"},
2895 {"sound", c_sound, "name filename"},
2896 {"font", c_font, "name filename"},
2897 {"soundtrack", c_soundtrack, "filename"},
2898 {"quicktime", c_quicktime, "url"},
2900 // generators of primitives
2902 {"point", c_point, "name x=0 y=0"},
2903 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2904 {"outline", c_outline, "name format=simple"},
2905 {"textshape", c_textshape, "name font size=100% text"},
2907 // character generators
2908 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2909 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2910 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2912 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2913 {"text", c_text, "name text font size=100% color=white"},
2914 {"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="},
2915 {"morphshape", c_morphshape, "name start end"},
2916 {"button", c_button, "name"},
2917 {"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="},
2918 {"on_press", c_on_press, "position=inside"},
2919 {"on_release", c_on_release, "position=anywhere"},
2920 {"on_move_in", c_on_move_in, "state=not_pressed"},
2921 {"on_move_out", c_on_move_out, "state=not_pressed"},
2922 {"on_key", c_on_key, "key=any"},
2925 {"play", c_play, "name loop=0 @nomultiple=0"},
2926 {"stop", c_stop, "name= "},
2927 {"nextframe", c_nextframe, "name"},
2928 {"previousframe", c_previousframe, "name"},
2930 // object placement tags
2931 {"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="},
2932 {"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="},
2933 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2934 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2935 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2936 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2937 {"del", c_del, "name"},
2938 // virtual object placement
2939 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2941 // commands which start a block
2942 //startclip (see above)
2943 {"sprite", c_sprite, "name"},
2944 {"action", c_action, "filename="},
2945 {"initaction", c_initaction, "name filename="},
2951 static map_t parseArguments(char*command, char*pattern)
2967 string_set(&t1, "commandname");
2968 string_set(&t2, command);
2969 map_put(&result, t1, t2);
2971 if(!pattern || !*pattern)
2978 if(!strncmp("<i> ", x, 3)) {
2980 if(type == COMMAND || type == RAWDATA) {
2982 syntaxerror("character name expected");
2984 name[pos].str = "instance";
2986 value[pos].str = text;
2987 value[pos].len = strlen(text);
2991 if(type == ASSIGNMENT)
2994 name[pos].str = "character";
2996 value[pos].str = text;
2997 value[pos].len = strlen(text);
3005 isboolean[pos] = (x[0] =='@');
3018 name[pos].len = d-x;
3023 name[pos].len = e-x;
3024 value[pos].str = e+1;
3025 value[pos].len = d-e-1;
3033 /* for(t=0;t<len;t++) {
3034 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3035 isboolean[t]?"(boolean)":"");
3040 if(type == RAWDATA || type == COMMAND) {
3045 // first, search for boolean arguments
3046 for(pos=0;pos<len;pos++)
3048 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3050 if(type == ASSIGNMENT)
3052 value[pos].str = text;
3053 value[pos].len = strlen(text);
3054 /*printf("setting boolean parameter %s (to %s)\n",
3055 strdup_n(name[pos], namelen[pos]),
3056 strdup_n(value[pos], valuelen[pos]));*/
3061 // second, search for normal arguments
3063 for(pos=0;pos<len;pos++)
3065 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3066 (type != ASSIGNMENT && !set[pos])) {
3068 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3070 if(type == ASSIGNMENT)
3073 value[pos].str = text;
3074 value[pos].len = strlen(text);
3076 printf("setting parameter %s (to %s)\n",
3077 strdup_n(name[pos].str, name[pos].len),
3078 strdup_n(value[pos].str, value[pos].len));
3084 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3088 for(t=0;t<len;t++) {
3089 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3092 for(t=0;t<len;t++) {
3093 if(value[t].str && value[t].str[0] == '*') {
3094 //relative default- take value from some other parameter
3096 for(s=0;s<len;s++) {
3097 if(value[s].len == value[t].len-1 &&
3098 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3099 value[t].str = value[s].str;
3102 if(value[t].str == 0) {
3104 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3108 /* ok, now construct the dictionary from the parameters */
3112 map_put(&result, name[t], value[t]);
3116 static void parseArgumentsForCommand(char*command)
3121 msg("<verbose> parse Command: %s (line %d)", command, line);
3123 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3124 if(!strcmp(arguments[t].command, command)) {
3126 /* ugly hack- will be removed soon (once documentation and .sc generating
3127 utilities have been changed) */
3128 if(!strcmp(command, "swf") && !stackpos) {
3129 warning("Please use .flash instead of .swf- this will be mandatory soon");
3134 args = parseArguments(command, arguments[t].arguments);
3140 syntaxerror("command %s not known", command);
3142 // catch missing .flash directives at the beginning of a file
3143 if(strcmp(command, "flash") && !stackpos)
3145 syntaxerror("No movie defined- use .flash first");
3149 printf(".%s\n", command);fflush(stdout);
3150 map_dump(&args, stdout, "\t");fflush(stdout);
3153 (*arguments[nr].func)(&args);
3155 /*if(!strcmp(command, "button") ||
3156 !strcmp(command, "action")) {
3159 if(type == COMMAND) {
3160 if(!strcmp(text, "end"))
3174 int main (int argc,char ** argv)
3177 processargs(argc, argv);
3178 initLog(0,-1,0,0,-1,verbose);
3181 args_callback_usage(argv[0]);
3185 file = generateTokens(filename);
3187 printf("parser returned error.\n");
3193 while(!noMoreTokens()) {
3196 syntaxerror("command expected");
3197 parseArgumentsForCommand(text);