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 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1271 swf_SetU16(tag, id);
1272 swf_SetString(tag, name);
1273 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1275 swf_SetU16(tag, id);
1276 swf_SetString(tag, name);
1278 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1282 if(dictionary_lookup(&sounds, name))
1283 syntaxerror("sound %s defined twice", name);
1284 dictionary_put2(&sounds, name, sound);
1292 static char* gradient_getToken(const char**p)
1296 while(**p && strchr(" \t\n\r", **p)) {
1300 while(**p && !strchr(" \t\n\r", **p)) {
1303 result = malloc((*p)-start+1);
1304 memcpy(result,start,(*p)-start+1);
1305 result[(*p)-start] = 0;
1309 float parsePercent(char*str);
1310 RGBA parseColor(char*str);
1312 GRADIENT parseGradient(const char*str)
1316 const char* p = str;
1317 memset(&gradient, 0, sizeof(GRADIENT));
1319 char*posstr,*colorstr;
1322 posstr = gradient_getToken(&p);
1325 pos = (int)(parsePercent(posstr)*255.0);
1328 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1329 colorstr = gradient_getToken(&p);
1330 color = parseColor(colorstr);
1331 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1332 warning("gradient record too big- max size is 8, rest ignored");
1335 gradient.ratios[gradient.num] = pos;
1336 gradient.rgba[gradient.num] = color;
1345 void s_gradient(char*name, const char*text, int radial, int rotate)
1347 gradient_t* gradient;
1348 gradient = malloc(sizeof(gradient_t));
1349 memset(gradient, 0, sizeof(gradient_t));
1350 gradient->gradient = parseGradient(text);
1351 gradient->radial = radial;
1352 gradient->rotate = rotate;
1354 if(dictionary_lookup(&gradients, name))
1355 syntaxerror("gradient %s defined twice", name);
1356 dictionary_put2(&gradients, name, gradient);
1359 void s_action(const char*text)
1362 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1364 syntaxerror("Couldn't compile ActionScript");
1367 tag = swf_InsertTag(tag, ST_DOACTION);
1369 swf_ActionSet(tag, a);
1374 void s_initaction(const char*character, const char*text)
1378 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1380 syntaxerror("Couldn't compile ActionScript");
1383 c = (character_t*)dictionary_lookup(&characters, character);
1385 tag = swf_InsertTag(tag, ST_DOINITACTION);
1386 swf_SetU16(tag, c->id);
1387 swf_ActionSet(tag, a);
1392 int s_swf3action(char*name, char*action)
1395 instance_t* object = 0;
1397 object = (instance_t*)dictionary_lookup(&instances, name);
1398 if(!object && name && *name) {
1399 /* we have a name, but couldn't find it. Abort. */
1402 a = action_SetTarget(0, name);
1403 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1404 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1405 else if(!strcmp(action, "stop")) a = action_Stop(a);
1406 else if(!strcmp(action, "play")) a = action_Play(a);
1407 a = action_SetTarget(a, "");
1410 tag = swf_InsertTag(tag, ST_DOACTION);
1411 swf_ActionSet(tag, a);
1416 void s_outline(char*name, char*format, char*source)
1425 //swf_Shape10DrawerInit(&draw, 0);
1426 swf_Shape11DrawerInit(&draw, 0);
1428 draw_string(&draw, source);
1430 shape = swf_ShapeDrawerToShape(&draw);
1431 bounds = swf_ShapeDrawerGetBBox(&draw);
1432 draw.dealloc(&draw);
1434 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1435 outline->shape = shape;
1436 outline->bbox = bounds;
1438 if(dictionary_lookup(&outlines, name))
1439 syntaxerror("outline %s defined twice", name);
1440 dictionary_put2(&outlines, name, outline);
1443 int s_playsound(char*name, int loops, int nomultiple, int stop)
1449 sound = dictionary_lookup(&sounds, name);
1453 tag = swf_InsertTag(tag, ST_STARTSOUND);
1454 swf_SetU16(tag, sound->id); //id
1455 memset(&info, 0, sizeof(info));
1458 info.nomultiple = nomultiple;
1459 swf_SetSoundInfo(tag, &info);
1463 void s_includeswf(char*name, char*filename)
1471 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1472 f = open(filename,O_RDONLY|O_BINARY);
1474 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1475 s_box(name, 0, 0, black, 20, 0);
1478 if (swf_ReadSWF(f,&swf)<0) {
1479 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1480 s_box(name, 0, 0, black, 20, 0);
1485 /* FIXME: The following sets the bounding Box for the character.
1486 It is wrong for two reasons:
1487 a) It may be too small (in case objects in the movie clip at the borders)
1488 b) it may be too big (because the poor movie never got autocropped)
1492 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1493 swf_SetU16(tag, id);
1494 swf_SetU16(tag, swf.frameCount);
1496 swf_Relocate(&swf, idmap);
1498 ftag = swf.firstTag;
1502 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1503 if(cutout[t] == ftag->id) {
1507 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1509 if(ftag->id == ST_END)
1513 /* We simply dump all tags right after the sprite
1514 header, relying on the fact that swf_OptimizeTagOrder() will
1515 sort things out for us later.
1516 We also rely on the fact that the imported SWF is well-formed.
1518 tag = swf_InsertTag(tag, ftag->id);
1519 swf_SetBlock(tag, ftag->data, ftag->len);
1523 syntaxerror("Included file %s contains errors", filename);
1524 tag = swf_InsertTag(tag, ST_END);
1528 s_addcharacter(name, id, tag, r);
1531 SRECT s_getCharBBox(char*name)
1533 character_t* c = dictionary_lookup(&characters, name);
1534 if(!c) syntaxerror("character '%s' unknown(2)", name);
1537 SRECT s_getInstanceBBox(char*name)
1539 instance_t * i = dictionary_lookup(&instances, name);
1541 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1543 if(!c) syntaxerror("internal error(5)");
1546 parameters_t s_getParameters(char*name)
1548 instance_t * i = dictionary_lookup(&instances, name);
1549 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1550 return i->parameters;
1552 void s_startclip(char*instance, char*character, parameters_t p)
1554 character_t* c = dictionary_lookup(&characters, character);
1558 syntaxerror("character %s not known", character);
1560 i = s_addinstance(instance, c, currentdepth);
1562 m = s_instancepos(i->character->size, &p);
1564 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1565 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1566 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1568 i->lastFrame= currentframe;
1570 stack[stackpos].tag = tag;
1571 stack[stackpos].type = 2;
1580 swf_SetTagPos(stack[stackpos].tag, 0);
1581 swf_GetPlaceObject(stack[stackpos].tag, &p);
1582 p.clipdepth = currentdepth;
1584 swf_ClearTag(stack[stackpos].tag);
1585 swf_SetPlaceObject(stack[stackpos].tag, &p);
1589 void s_put(char*instance, char*character, parameters_t p)
1591 character_t* c = dictionary_lookup(&characters, character);
1595 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1598 i = s_addinstance(instance, c, currentdepth);
1600 m = s_instancepos(i->character->size, &p);
1602 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1603 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1605 i->lastFrame = currentframe;
1609 void s_jump(char*instance, parameters_t p)
1611 instance_t* i = dictionary_lookup(&instances, instance);
1614 syntaxerror("instance %s not known", instance);
1618 m = s_instancepos(i->character->size, &p);
1620 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1621 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1623 i->lastFrame = currentframe;
1626 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1630 if(num==0 || num==1)
1632 ratio = (float)pos/(float)num;
1634 p.x = (p2->x-p1->x)*ratio + p1->x;
1635 p.y = (p2->y-p1->y)*ratio + p1->y;
1636 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1637 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1638 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1639 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1641 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1642 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1643 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1644 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1646 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1647 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1648 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1649 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1651 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1652 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1653 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1654 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1658 void s_change(char*instance, parameters_t p2)
1660 instance_t* i = dictionary_lookup(&instances, instance);
1664 int frame, allframes;
1666 syntaxerror("instance %s not known", instance);
1670 allframes = currentframe - i->lastFrame - 1;
1672 warning(".change ignored. can only .put/.change an object once per frame.");
1676 m = s_instancepos(i->character->size, &p2);
1677 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1678 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1681 /* o.k., we got the start and end point set. Now iterate though all the
1682 tags in between, inserting object changes after each new frame */
1685 if(!t) syntaxerror("internal error(6)");
1687 while(frame < allframes) {
1688 if(t->id == ST_SHOWFRAME) {
1693 p = s_interpolate(&p1, &p2, frame, allframes);
1694 m = s_instancepos(i->character->size, &p); //needed?
1695 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1696 i->lastFrame = currentframe;
1697 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1699 if(frame == allframes)
1704 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1708 void s_delinstance(char*instance)
1710 instance_t* i = dictionary_lookup(&instances, instance);
1712 syntaxerror("instance %s not known", instance);
1714 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1715 swf_SetU16(tag, i->depth);
1716 dictionary_del(&instances, instance);
1719 void s_qchange(char*instance, parameters_t p)
1726 syntaxerror(".end unexpected");
1727 if(stack[stackpos-1].type == 0)
1729 else if(stack[stackpos-1].type == 1)
1731 else if(stack[stackpos-1].type == 2)
1733 else if(stack[stackpos-1].type == 3)
1735 else syntaxerror("internal error 1");
1738 // ------------------------------------------------------------------------
1740 typedef int command_func_t(map_t*args);
1742 SRECT parseBox(char*str)
1745 float xmin, xmax, ymin, ymax;
1746 char*x = strchr(str, 'x');
1748 if(!strcmp(str, "autocrop")) {
1749 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1753 d1 = strchr(x+1, ':');
1755 d2 = strchr(d1+1, ':');
1757 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1761 else if(d1 && !d2) {
1762 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1768 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1773 r.xmin = (SCOORD)(xmin*20);
1774 r.ymin = (SCOORD)(ymin*20);
1775 r.xmax = (SCOORD)(xmax*20);
1776 r.ymax = (SCOORD)(ymax*20);
1779 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1782 float parseFloat(char*str)
1786 int parseInt(char*str)
1791 if(str[0]=='+' || str[0]=='-')
1795 if(str[t]<'0' || str[t]>'9')
1796 syntaxerror("Not an Integer: \"%s\"", str);
1799 int parseTwip(char*str)
1803 if(str[0]=='+' || str[0]=='-') {
1808 dot = strchr(str, '.');
1812 return sign*parseInt(str)*20;
1814 char* old = strdup(str);
1815 int l=strlen(dot+1);
1818 for(s=str;s<dot-1;s++)
1819 if(*s<'0' || *s>'9')
1820 syntaxerror("Not a coordinate: \"%s\"", str);
1822 if(*s<'0' || *s>'9')
1823 syntaxerror("Not a coordinate: \"%s\"", str);
1825 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1826 dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
1829 warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
1833 return sign*atoi(str)*20;
1835 return sign*atoi(str)*20+atoi(dot)*2;
1837 return sign*atoi(str)*20+atoi(dot)/5;
1842 int isPoint(char*str)
1844 if(strchr(str, '('))
1850 SPOINT parsePoint(char*str)
1854 int l = strlen(str);
1855 char*comma = strchr(str, ',');
1856 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1857 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1858 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1859 p.x = parseTwip(tmp);
1860 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1861 p.y = parseTwip(tmp);
1865 int parseColor2(char*str, RGBA*color)
1867 int l = strlen(str);
1871 struct {unsigned char r,g,b;char*name;} colors[] =
1872 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1873 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1874 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1875 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1876 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1877 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1878 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1879 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1880 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1881 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1882 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1883 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1887 if(str[0]=='#' && (l==7 || l==9)) {
1888 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1890 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1892 color->r = r; color->g = g; color->b = b; color->a = a;
1895 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1896 if(!strcmp(str, colors[t].name)) {
1901 color->r = r; color->g = g; color->b = b; color->a = a;
1907 RGBA parseColor(char*str)
1910 if(!parseColor2(str, &c))
1911 syntaxerror("Expression '%s' is not a color", str);
1915 typedef struct _muladd {
1920 MULADD parseMulAdd(char*str)
1923 char* str2 = (char*)malloc(strlen(str)+5);
1930 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1931 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1932 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1933 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1934 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1935 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1936 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1937 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1938 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1939 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1941 syntaxerror("'%s' is not a valid color transform expression", str);
1943 m.add = (int)(add*256);
1944 m.mul = (int)(mul*256);
1949 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1951 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1952 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1954 if(a<-32768) a=-32768;
1955 if(a>32767) a=32767;
1956 if(m<-32768) m=-32768;
1957 if(m>32767) m=32767;
1963 float parsePxOrPercent(char*fontname, char*str)
1965 int l = strlen(str);
1966 if(strchr(str, '%'))
1967 return parsePercent(str);
1968 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1969 float p = atof(str);
1970 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1972 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1976 float parsePercent(char*str)
1978 int l = strlen(str);
1982 return atoi(str)/100.0;
1984 syntaxerror("Expression '%s' is not a percentage", str);
1987 int isPercent(char*str)
1989 return str[strlen(str)-1]=='%';
1991 int parseNewSize(char*str, int size)
1994 return parsePercent(str)*size;
1996 return (int)(atof(str)*20);
1999 int isColor(char*str)
2002 return parseColor2(str, &c);
2005 static char* lu(map_t* args, char*name)
2007 char* value = map_lookup(args, name);
2009 map_dump(args, stdout, "");
2010 syntaxerror("internal error 2: value %s should be set", name);
2015 static int c_flash(map_t*args)
2017 char* filename = map_lookup(args, "filename");
2018 char* compressstr = lu(args, "compress");
2019 SRECT bbox = parseBox(lu(args, "bbox"));
2020 int version = parseInt(lu(args, "version"));
2021 int fps = (int)(parseFloat(lu(args, "fps"))*256);
2023 RGBA color = parseColor(lu(args, "background"));
2025 if(!filename || !*filename) {
2026 /* for compatibility */
2027 filename = map_lookup(args, "name");
2028 if(!filename || !*filename) {
2031 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2032 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2036 if(!filename || override_outputname)
2037 filename = outputname;
2039 if(!strcmp(compressstr, "default"))
2040 compress = version==6;
2041 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2043 else if(!strcmp(compressstr, "no"))
2045 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2047 s_swf(filename, bbox, version, fps, compress, color);
2050 int isRelative(char*str)
2052 return !strncmp(str, "<plus>", 6) ||
2053 !strncmp(str, "<minus>", 7);
2055 char* getOffset(char*str)
2057 if(!strncmp(str, "<plus>", 6))
2059 if(!strncmp(str, "<minus>", 7))
2061 syntaxerror("internal error (347)");
2064 int getSign(char*str)
2066 if(!strncmp(str, "<plus>", 6))
2068 if(!strncmp(str, "<minus>", 7))
2070 syntaxerror("internal error (348)");
2073 static dictionary_t points;
2074 static mem_t mpoints;
2075 int points_initialized = 0;
2077 SPOINT getPoint(SRECT r, char*name)
2080 if(!strcmp(name, "center")) {
2082 p.x = (r.xmin + r.xmax)/2;
2083 p.y = (r.ymin + r.ymax)/2;
2087 if(points_initialized)
2088 l = (int)dictionary_lookup(&points, name);
2090 syntaxerror("Invalid point: \"%s\".", name);
2093 return *(SPOINT*)&mpoints.buffer[l];
2096 static int texture2(char*name, char*object, map_t*args, int errors)
2099 char*xstr = map_lookup(args, "x");
2100 char*ystr = map_lookup(args, "y");
2101 char*widthstr = map_lookup(args, "width");
2102 char*heightstr = map_lookup(args, "height");
2103 char*scalestr = map_lookup(args, "scale");
2104 char*scalexstr = map_lookup(args, "scalex");
2105 char*scaleystr = map_lookup(args, "scaley");
2106 char*rotatestr = map_lookup(args, "rotate");
2107 char* shearstr = map_lookup(args, "shear");
2108 char* radiusstr = map_lookup(args, "r");
2110 float scalex = 1.0, scaley = 1.0;
2111 float rotate=0, shear=0;
2113 if(!*xstr && !*ystr) {
2115 syntaxerror("x and y must be set");
2118 if(*scalestr && (*scalexstr || *scaleystr)) {
2119 syntaxerror("scale and scalex/scaley can't both be set");
2122 if((*widthstr || *heightstr) && *radiusstr) {
2123 syntaxerror("width/height and radius can't both be set");
2126 widthstr = radiusstr;
2127 heightstr = radiusstr;
2129 if(!*xstr) xstr="0";
2130 if(!*ystr) ystr="0";
2131 if(!*rotatestr) rotatestr="0";
2132 if(!*shearstr) shearstr="0";
2135 scalex = scaley = parsePercent(scalestr);
2136 } else if(*scalexstr || *scaleystr) {
2137 if(scalexstr) scalex = parsePercent(scalexstr);
2138 if(scaleystr) scaley = parsePercent(scaleystr);
2139 } else if(*widthstr || *heightstr) {
2142 s_getBitmapSize(object, &width, &height);
2144 scalex = (float)parseTwip(widthstr)/(float)width;
2146 scaley = (float)parseTwip(heightstr)/(float)height;
2148 x = parseTwip(xstr);
2149 y = parseTwip(ystr);
2150 rotate = parseFloat(rotatestr);
2151 shear = parseFloat(shearstr);
2153 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2158 static int c_texture(map_t*args)
2160 char*name = lu(args, "instance");
2161 char*object = lu(args, "character");
2162 return texture2(name, object, args, 1);
2165 static int c_gradient(map_t*args)
2167 char*name = lu(args, "name");
2168 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2169 int rotate = parseInt(lu(args, "rotate"));
2173 syntaxerror("colon (:) expected");
2175 s_gradient(name, text, radial, rotate);
2177 /* check whether we also have placement information,
2178 which would make this a positioned gradient.
2179 If there is placement information, texture2() will
2180 add a texture, which has priority over the gradient.
2182 texture2(name, name, args, 0);
2185 static int c_point(map_t*args)
2187 char*name = lu(args, "name");
2191 if(!points_initialized) {
2192 dictionary_init(&points);
2194 points_initialized = 1;
2196 p.x = parseTwip(lu(args, "x"));
2197 p.y = parseTwip(lu(args, "y"));
2198 pos = mem_put(&mpoints, &p, sizeof(p));
2199 string_set(&s1, name);
2201 dictionary_put(&points, s1, (void*)pos);
2204 static int c_play(map_t*args)
2206 char*name = lu(args, "name");
2207 char*loop = lu(args, "loop");
2208 char*nomultiple = lu(args, "nomultiple");
2210 if(!strcmp(nomultiple, "nomultiple"))
2213 nm = parseInt(nomultiple);
2215 if(s_playsound(name, parseInt(loop), nm, 0)) {
2217 } else if(s_swf3action(name, "play")) {
2223 static int c_stop(map_t*args)
2225 char*name = map_lookup(args, "name");
2227 if(s_playsound(name, 0,0,1)) {
2229 } else if(s_swf3action(name, "stop")) {
2232 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2236 static int c_nextframe(map_t*args)
2238 char*name = lu(args, "name");
2240 if(s_swf3action(name, "nextframe")) {
2243 syntaxerror("I don't know anything about movie \"%s\"", name);
2247 static int c_previousframe(map_t*args)
2249 char*name = lu(args, "name");
2251 if(s_swf3action(name, "previousframe")) {
2254 syntaxerror("I don't know anything about movie \"%s\"", name);
2258 static int c_placement(map_t*args, int type)
2260 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2263 char* luminancestr = lu(args, "luminance");
2264 char* scalestr = lu(args, "scale");
2265 char* scalexstr = lu(args, "scalex");
2266 char* scaleystr = lu(args, "scaley");
2267 char* rotatestr = lu(args, "rotate");
2268 char* shearstr = lu(args, "shear");
2269 char* xstr="", *pivotstr="";
2270 char* ystr="", *anglestr="";
2271 char*above = lu(args, "above"); /*FIXME*/
2272 char*below = lu(args, "below");
2273 char* rstr = lu(args, "red");
2274 char* gstr = lu(args, "green");
2275 char* bstr = lu(args, "blue");
2276 char* astr = lu(args, "alpha");
2277 char* pinstr = lu(args, "pin");
2278 char* as = map_lookup(args, "as");
2286 if(type==9) { // (?) .rotate or .arcchange
2287 pivotstr = lu(args, "pivot");
2288 anglestr = lu(args, "angle");
2290 xstr = lu(args, "x");
2291 ystr = lu(args, "y");
2294 luminance = parseMulAdd(luminancestr);
2297 luminance.mul = 256;
2301 if(scalexstr[0]||scaleystr[0])
2302 syntaxerror("scalex/scaley and scale cannot both be set");
2303 scalexstr = scaleystr = scalestr;
2306 if(type == 0 || type == 4) {
2308 character = lu(args, "character");
2309 parameters_clear(&p);
2310 } else if (type == 5) {
2311 character = lu(args, "name");
2312 parameters_clear(&p);
2315 p = s_getParameters(instance);
2320 if(isRelative(xstr)) {
2321 if(type == 0 || type == 4)
2322 syntaxerror("relative x values not allowed for initial put or startclip");
2323 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2325 p.x = parseTwip(xstr);
2329 if(isRelative(ystr)) {
2330 if(type == 0 || type == 4)
2331 syntaxerror("relative y values not allowed for initial put or startclip");
2332 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2334 p.y = parseTwip(ystr);
2338 /* scale, scalex, scaley */
2340 oldbbox = s_getCharBBox(character);
2342 oldbbox = s_getInstanceBBox(instance);
2344 oldwidth = oldbbox.xmax - oldbbox.xmin;
2345 oldheight = oldbbox.ymax - oldbbox.ymin;
2347 if(oldwidth==0) p.scalex = 1.0;
2350 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2354 if(oldheight==0) p.scaley = 1.0;
2357 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2363 if(isRelative(rotatestr)) {
2364 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2366 p.rotate = parseFloat(rotatestr);
2372 if(isRelative(shearstr)) {
2373 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2375 p.shear = parseFloat(shearstr);
2380 if(isPoint(pivotstr))
2381 p.pivot = parsePoint(pivotstr);
2383 p.pivot = getPoint(oldbbox, pivotstr);
2387 p.pin = parsePoint(pinstr);
2389 p.pin = getPoint(oldbbox, pinstr);
2392 /* color transform */
2394 if(rstr[0] || luminancestr[0]) {
2397 r = parseMulAdd(rstr);
2399 r.add = p.cxform.r0;
2400 r.mul = p.cxform.r1;
2402 r = mergeMulAdd(r, luminance);
2403 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2405 if(gstr[0] || luminancestr[0]) {
2408 g = parseMulAdd(gstr);
2410 g.add = p.cxform.g0;
2411 g.mul = p.cxform.g1;
2413 g = mergeMulAdd(g, luminance);
2414 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2416 if(bstr[0] || luminancestr[0]) {
2419 b = parseMulAdd(bstr);
2421 b.add = p.cxform.b0;
2422 b.mul = p.cxform.b1;
2424 b = mergeMulAdd(b, luminance);
2425 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2428 MULADD a = parseMulAdd(astr);
2429 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2433 s_put(instance, character, p);
2435 s_change(instance, p);
2437 s_qchange(instance, p);
2439 s_jump(instance, p);
2441 s_startclip(instance, character, p);
2442 else if(type == 5) {
2444 s_buttonput(character, as, p);
2446 s_buttonput(character, "shape", p);
2451 static int c_put(map_t*args)
2453 c_placement(args, 0);
2456 static int c_change(map_t*args)
2458 c_placement(args, 1);
2461 static int c_qchange(map_t*args)
2463 c_placement(args, 2);
2466 static int c_arcchange(map_t*args)
2468 c_placement(args, 2);
2471 static int c_jump(map_t*args)
2473 c_placement(args, 3);
2476 static int c_startclip(map_t*args)
2478 c_placement(args, 4);
2481 static int c_show(map_t*args)
2483 c_placement(args, 5);
2486 static int c_del(map_t*args)
2488 char*instance = lu(args, "name");
2489 s_delinstance(instance);
2492 static int c_end(map_t*args)
2497 static int c_sprite(map_t*args)
2499 char* name = lu(args, "name");
2503 static int c_frame(map_t*args)
2505 char*framestr = lu(args, "n");
2506 char*cutstr = lu(args, "cut");
2508 char*name = lu(args, "name");
2509 char*anchor = lu(args, "anchor");
2512 if(!strcmp(anchor, "anchor") && !*name)
2517 if(strcmp(cutstr, "no"))
2519 if(isRelative(framestr)) {
2520 frame = s_getframe();
2521 if(getSign(framestr)<0)
2522 syntaxerror("relative frame expressions must be positive");
2523 frame += parseInt(getOffset(framestr));
2526 frame = parseInt(framestr);
2527 if(s_getframe() >= frame
2528 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2529 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2531 s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
2534 static int c_primitive(map_t*args)
2536 char*name = lu(args, "name");
2537 char*command = lu(args, "commandname");
2538 int width=0, height=0, r=0;
2539 int linewidth = parseTwip(lu(args, "line"));
2540 char*colorstr = lu(args, "color");
2541 RGBA color = parseColor(colorstr);
2542 char*fillstr = lu(args, "fill");
2549 if(!strcmp(command, "circle"))
2551 else if(!strcmp(command, "filled"))
2555 width = parseTwip(lu(args, "width"));
2556 height = parseTwip(lu(args, "height"));
2557 } else if (type==1) {
2558 r = parseTwip(lu(args, "r"));
2559 } else if (type==2) {
2560 outline = lu(args, "outline");
2563 if(!strcmp(fillstr, "fill"))
2565 if(!strcmp(fillstr, "none"))
2567 if(width<0 || height<0 || linewidth<0 || r<0)
2568 syntaxerror("values width, height, line, r must be positive");
2570 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2571 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2572 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2576 static int c_textshape(map_t*args)
2578 char*name = lu(args, "name");
2579 char*text = lu(args, "text");
2580 char*font = lu(args, "font");
2581 float size = parsePxOrPercent(font, lu(args, "size"));
2583 s_textshape(name, font, size, text);
2587 static int c_swf(map_t*args)
2589 char*name = lu(args, "name");
2590 char*filename = lu(args, "filename");
2591 char*command = lu(args, "commandname");
2592 if(!strcmp(command, "shape"))
2593 warning("Please use .swf instead of .shape");
2594 s_includeswf(name, filename);
2598 static int c_font(map_t*args)
2600 char*name = lu(args, "name");
2601 char*filename = lu(args, "filename");
2602 s_font(name, filename);
2606 static int c_sound(map_t*args)
2608 char*name = lu(args, "name");
2609 char*filename = lu(args, "filename");
2610 s_sound(name, filename);
2614 static int c_text(map_t*args)
2616 char*name = lu(args, "name");
2617 char*text = lu(args, "text");
2618 char*font = lu(args, "font");
2619 float size = parsePxOrPercent(font, lu(args, "size"));
2620 RGBA color = parseColor(lu(args, "color"));
2621 s_text(name, font, text, (int)(size*100), color);
2625 static int c_soundtrack(map_t*args)
2630 static int c_quicktime(map_t*args)
2632 char*name = lu(args, "name");
2633 char*url = lu(args, "url");
2634 s_quicktime(name, url);
2638 static int c_image(map_t*args)
2640 char*command = lu(args, "commandname");
2641 char*name = lu(args, "name");
2642 char*filename = lu(args, "filename");
2643 if(!strcmp(command,"jpeg")) {
2644 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2645 s_image(name, "jpeg", filename, quality);
2647 s_image(name, "png", filename, 0);
2652 static int c_outline(map_t*args)
2654 char*name = lu(args, "name");
2655 char*format = lu(args, "format");
2659 syntaxerror("colon (:) expected");
2661 s_outline(name, format, text);
2665 int fakechar(map_t*args)
2667 char*name = lu(args, "name");
2668 s_box(name, 0, 0, black, 20, 0);
2672 static int c_egon(map_t*args) {return fakechar(args);}
2673 static int c_button(map_t*args) {
2674 char*name = lu(args, "name");
2678 static int current_button_flags = 0;
2679 static int c_on_press(map_t*args)
2681 char*position = lu(args, "position");
2683 if(!strcmp(position, "inside")) {
2684 current_button_flags |= BC_OVERUP_OVERDOWN;
2685 } else if(!strcmp(position, "outside")) {
2686 //current_button_flags |= BC_IDLE_OUTDOWN;
2687 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2688 } else if(!strcmp(position, "anywhere")) {
2689 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2692 if(type == RAWDATA) {
2694 s_buttonaction(current_button_flags, action);
2695 current_button_flags = 0;
2701 static int c_on_release(map_t*args)
2703 char*position = lu(args, "position");
2705 if(!strcmp(position, "inside")) {
2706 current_button_flags |= BC_OVERDOWN_OVERUP;
2707 } else if(!strcmp(position, "outside")) {
2708 current_button_flags |= BC_OUTDOWN_IDLE;
2709 } else if(!strcmp(position, "anywhere")) {
2710 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2713 if(type == RAWDATA) {
2715 s_buttonaction(current_button_flags, action);
2716 current_button_flags = 0;
2722 static int c_on_move_in(map_t*args)
2724 char*position = lu(args, "state");
2726 if(!strcmp(position, "pressed")) {
2727 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2728 } else if(!strcmp(position, "not_pressed")) {
2729 current_button_flags |= BC_IDLE_OVERUP;
2730 } else if(!strcmp(position, "any")) {
2731 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2734 if(type == RAWDATA) {
2736 s_buttonaction(current_button_flags, action);
2737 current_button_flags = 0;
2743 static int c_on_move_out(map_t*args)
2745 char*position = lu(args, "state");
2747 if(!strcmp(position, "pressed")) {
2748 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2749 } else if(!strcmp(position, "not_pressed")) {
2750 current_button_flags |= BC_OVERUP_IDLE;
2751 } else if(!strcmp(position, "any")) {
2752 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2755 if(type == RAWDATA) {
2757 s_buttonaction(current_button_flags, action);
2758 current_button_flags = 0;
2764 static int c_on_key(map_t*args)
2766 char*key = lu(args, "key");
2768 if(strlen(key)==1) {
2771 current_button_flags |= 0x4000 + (key[0]*0x200);
2773 syntaxerror("invalid character: %c"+key[0]);
2778 <ctrl-x> = 0x200*(x-'a')
2782 syntaxerror("invalid key: %s",key);
2785 if(type == RAWDATA) {
2787 s_buttonaction(current_button_flags, action);
2788 current_button_flags = 0;
2795 static int c_edittext(map_t*args)
2797 //"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"},
2798 char*name = lu(args, "name");
2799 char*font = lu(args, "font");
2800 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2801 int width = parseTwip(lu(args, "width"));
2802 int height = parseTwip(lu(args, "height"));
2803 char*text = lu(args, "text");
2804 RGBA color = parseColor(lu(args, "color"));
2805 int maxlength = parseInt(lu(args, "maxlength"));
2806 char*variable = lu(args, "variable");
2807 char*passwordstr = lu(args, "password");
2808 char*wordwrapstr = lu(args, "wordwrap");
2809 char*multilinestr = lu(args, "multiline");
2810 char*htmlstr = lu(args, "html");
2811 char*noselectstr = lu(args, "noselect");
2812 char*readonlystr = lu(args, "readonly");
2813 char*borderstr = lu(args, "border");
2814 char*autosizestr = lu(args, "autosize");
2815 char*alignstr = lu(args, "align");
2819 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2820 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2821 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2822 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2823 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2824 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2825 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2826 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2827 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2828 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2829 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2830 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2831 else syntaxerror("Unknown alignment: %s", alignstr);
2833 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2837 static int c_morphshape(map_t*args) {return fakechar(args);}
2838 static int c_movie(map_t*args) {return fakechar(args);}
2840 static char* readfile(const char*filename)
2842 FILE*fi = fopen(filename, "rb");
2846 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2847 fseek(fi, 0, SEEK_END);
2849 fseek(fi, 0, SEEK_SET);
2850 text = rfx_alloc(l+1);
2851 fread(text, l, 1, fi);
2857 static int c_action(map_t*args)
2859 char* filename = map_lookup(args, "filename");
2860 if(!filename ||!*filename) {
2862 if(type != RAWDATA) {
2863 syntaxerror("colon (:) expected");
2867 s_action(readfile(filename));
2873 static int c_initaction(map_t*args)
2875 char* character = lu(args, "name");
2876 char* filename = map_lookup(args, "filename");
2877 if(!filename ||!*filename) {
2879 if(type != RAWDATA) {
2880 syntaxerror("colon (:) expected");
2882 s_initaction(character, text);
2884 s_initaction(character, readfile(filename));
2892 command_func_t* func;
2895 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2896 {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
2897 // "import" type stuff
2898 {"swf", c_swf, "name filename"},
2899 {"shape", c_swf, "name filename"},
2900 {"jpeg", c_image, "name filename quality=80%"},
2901 {"png", c_image, "name filename"},
2902 {"movie", c_movie, "name filename"},
2903 {"sound", c_sound, "name filename"},
2904 {"font", c_font, "name filename"},
2905 {"soundtrack", c_soundtrack, "filename"},
2906 {"quicktime", c_quicktime, "url"},
2908 // generators of primitives
2910 {"point", c_point, "name x=0 y=0"},
2911 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2912 {"outline", c_outline, "name format=simple"},
2913 {"textshape", c_textshape, "name font size=100% text"},
2915 // character generators
2916 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2917 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2918 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2920 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2921 {"text", c_text, "name text font size=100% color=white"},
2922 {"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="},
2923 {"morphshape", c_morphshape, "name start end"},
2924 {"button", c_button, "name"},
2925 {"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="},
2926 {"on_press", c_on_press, "position=inside"},
2927 {"on_release", c_on_release, "position=anywhere"},
2928 {"on_move_in", c_on_move_in, "state=not_pressed"},
2929 {"on_move_out", c_on_move_out, "state=not_pressed"},
2930 {"on_key", c_on_key, "key=any"},
2933 {"play", c_play, "name loop=0 @nomultiple=0"},
2934 {"stop", c_stop, "name= "},
2935 {"nextframe", c_nextframe, "name"},
2936 {"previousframe", c_previousframe, "name"},
2938 // object placement tags
2939 {"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="},
2940 {"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="},
2941 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2942 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2943 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2944 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2945 {"del", c_del, "name"},
2946 // virtual object placement
2947 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2949 // commands which start a block
2950 //startclip (see above)
2951 {"sprite", c_sprite, "name"},
2952 {"action", c_action, "filename="},
2953 {"initaction", c_initaction, "name filename="},
2959 static map_t parseArguments(char*command, char*pattern)
2975 string_set(&t1, "commandname");
2976 string_set(&t2, command);
2977 map_put(&result, t1, t2);
2979 if(!pattern || !*pattern)
2986 if(!strncmp("<i> ", x, 3)) {
2988 if(type == COMMAND || type == RAWDATA) {
2990 syntaxerror("character name expected");
2992 name[pos].str = "instance";
2994 value[pos].str = text;
2995 value[pos].len = strlen(text);
2999 if(type == ASSIGNMENT)
3002 name[pos].str = "character";
3004 value[pos].str = text;
3005 value[pos].len = strlen(text);
3013 isboolean[pos] = (x[0] =='@');
3026 name[pos].len = d-x;
3031 name[pos].len = e-x;
3032 value[pos].str = e+1;
3033 value[pos].len = d-e-1;
3041 /* for(t=0;t<len;t++) {
3042 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3043 isboolean[t]?"(boolean)":"");
3048 if(type == RAWDATA || type == COMMAND) {
3053 // first, search for boolean arguments
3054 for(pos=0;pos<len;pos++)
3056 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3058 if(type == ASSIGNMENT)
3060 value[pos].str = text;
3061 value[pos].len = strlen(text);
3062 /*printf("setting boolean parameter %s (to %s)\n",
3063 strdup_n(name[pos], namelen[pos]),
3064 strdup_n(value[pos], valuelen[pos]));*/
3069 // second, search for normal arguments
3071 for(pos=0;pos<len;pos++)
3073 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3074 (type != ASSIGNMENT && !set[pos])) {
3076 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3078 if(type == ASSIGNMENT)
3081 value[pos].str = text;
3082 value[pos].len = strlen(text);
3084 printf("setting parameter %s (to %s)\n",
3085 strdup_n(name[pos].str, name[pos].len),
3086 strdup_n(value[pos].str, value[pos].len));
3092 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3096 for(t=0;t<len;t++) {
3097 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3100 for(t=0;t<len;t++) {
3101 if(value[t].str && value[t].str[0] == '*') {
3102 //relative default- take value from some other parameter
3104 for(s=0;s<len;s++) {
3105 if(value[s].len == value[t].len-1 &&
3106 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3107 value[t].str = value[s].str;
3110 if(value[t].str == 0) {
3112 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3116 /* ok, now construct the dictionary from the parameters */
3120 map_put(&result, name[t], value[t]);
3124 static void parseArgumentsForCommand(char*command)
3129 msg("<verbose> parse Command: %s (line %d)", command, line);
3131 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3132 if(!strcmp(arguments[t].command, command)) {
3134 /* ugly hack- will be removed soon (once documentation and .sc generating
3135 utilities have been changed) */
3136 if(!strcmp(command, "swf") && !stackpos) {
3137 warning("Please use .flash instead of .swf- this will be mandatory soon");
3142 args = parseArguments(command, arguments[t].arguments);
3148 syntaxerror("command %s not known", command);
3150 // catch missing .flash directives at the beginning of a file
3151 if(strcmp(command, "flash") && !stackpos)
3153 syntaxerror("No movie defined- use .flash first");
3157 printf(".%s\n", command);fflush(stdout);
3158 map_dump(&args, stdout, "\t");fflush(stdout);
3161 (*arguments[nr].func)(&args);
3163 /*if(!strcmp(command, "button") ||
3164 !strcmp(command, "action")) {
3167 if(type == COMMAND) {
3168 if(!strcmp(text, "end"))
3182 int main (int argc,char ** argv)
3185 processargs(argc, argv);
3186 initLog(0,-1,0,0,-1,verbose);
3189 args_callback_usage(argv[0]);
3193 file = generateTokens(filename);
3195 printf("parser returned error.\n");
3201 while(!noMoreTokens()) {
3204 syntaxerror("command expected");
3205 parseArgumentsForCommand(text);