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)
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);
732 swf_SetU8(tag, 1); //make this an anchor
735 if(nr == 0 && currentframe == 0 && name) {
736 tag = swf_InsertTag(tag, ST_FRAMELABEL);
737 swf_SetString(tag, name);
738 swf_SetU8(tag, 1); //make this an anchor
743 syntaxerror("Can't cut, frame empty");
745 stack[stackpos].cut = tag;
751 int parseColor2(char*str, RGBA*color);
753 int addFillStyle(SHAPE*s, SRECT*r, char*name)
760 parseColor2(name, &color);
761 return swf_ShapeAddSolidFillStyle(s, &color);
762 } else if ((texture = dictionary_lookup(&textures, name))) {
763 return swf_ShapeAddFillStyle2(s, &texture->fs);
764 } else if((image = dictionary_lookup(&images, name))) {
766 swf_GetMatrix(0, &m);
767 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
768 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
771 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
772 } else if ((gradient = dictionary_lookup(&gradients, name))) {
776 swf_GetMatrix(0, &rot);
777 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
778 csin = sin(-gradient->rotate*2*3.14159265358979/360);
780 rot.r1 = -csin*65536;
783 r2 = swf_TurnRect(*r, &rot);
784 swf_GetMatrix(0, &m);
785 m.sx = (r2.xmax - r2.xmin)*2*ccos;
786 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
787 m.r0 = (r2.ymax - r2.ymin)*2*csin;
788 m.sy = (r2.ymax - r2.ymin)*2*ccos;
789 m.tx = r->xmin + (r->xmax - r->xmin)/2;
790 m.ty = r->ymin + (r->ymax - r->ymin)/2;
791 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
792 } else if (parseColor2(name, &color)) {
793 return swf_ShapeAddSolidFillStyle(s, &color);
795 syntaxerror("not a color/fillstyle: %s", name);
800 RGBA black={r:0,g:0,b:0,a:0};
801 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
810 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
813 linewidth = linewidth>=20?linewidth-20:0;
814 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
817 fs1 = addFillStyle(s, &r2, texture);
820 r.xmin = r2.xmin-linewidth/2;
821 r.ymin = r2.ymin-linewidth/2;
822 r.xmax = r2.xmax+linewidth/2;
823 r.ymax = r2.ymax+linewidth/2;
825 swf_SetShapeHeader(tag,s);
826 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
827 swf_ShapeSetLine(tag,s,width,0);
828 swf_ShapeSetLine(tag,s,0,height);
829 swf_ShapeSetLine(tag,s,-width,0);
830 swf_ShapeSetLine(tag,s,0,-height);
831 swf_ShapeSetEnd(tag);
834 s_addcharacter(name, id, tag, r);
838 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
844 outline = dictionary_lookup(&outlines, outlinename);
846 syntaxerror("outline %s not defined", outlinename);
850 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
853 linewidth = linewidth>=20?linewidth-20:0;
854 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
857 fs1 = addFillStyle(s, &r2, texture);
860 rect.xmin = r2.xmin-linewidth/2;
861 rect.ymin = r2.ymin-linewidth/2;
862 rect.xmax = r2.xmax+linewidth/2;
863 rect.ymax = r2.ymax+linewidth/2;
865 swf_SetRect(tag,&rect);
866 swf_SetShapeStyles(tag, s);
867 swf_ShapeCountBits(s,0,0);
868 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
869 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
870 swf_SetShapeBits(tag, s);
871 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
874 s_addcharacter(name, id, tag, rect);
878 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
883 r2.xmin = r2.ymin = 0;
887 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
890 linewidth = linewidth>=20?linewidth-20:0;
891 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
894 fs1 = addFillStyle(s, &r2, texture);
896 rect.xmin = r2.xmin-linewidth/2;
897 rect.ymin = r2.ymin-linewidth/2;
898 rect.xmax = r2.xmax+linewidth/2;
899 rect.ymax = r2.ymax+linewidth/2;
901 swf_SetRect(tag,&rect);
902 swf_SetShapeHeader(tag,s);
903 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
904 swf_ShapeSetCircle(tag, s, r,r,r,r);
905 swf_ShapeSetEnd(tag);
908 s_addcharacter(name, id, tag, rect);
912 void s_textshape(char*name, char*fontname, float size, char*_text)
915 U8*text = (U8*)_text;
919 font = dictionary_lookup(&fonts, fontname);
921 syntaxerror("font \"%s\" not known!", fontname);
923 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
924 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
925 s_box(name, 0, 0, black, 20, 0);
928 g = font->ascii2glyph[text[0]];
930 outline = malloc(sizeof(outline_t));
931 memset(outline, 0, sizeof(outline_t));
932 outline->shape = font->glyph[g].shape;
933 outline->bbox = font->layout->bounds[g];
937 swf_Shape11DrawerInit(&draw, 0);
938 swf_DrawText(&draw, font, (int)(size*100), _text);
940 outline->shape = swf_ShapeDrawerToShape(&draw);
941 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
945 if(dictionary_lookup(&outlines, name))
946 syntaxerror("outline %s defined twice", name);
947 dictionary_put2(&outlines, name, outline);
950 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
955 font = dictionary_lookup(&fonts, fontname);
957 syntaxerror("font \"%s\" not known!", fontname);
959 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
961 if(!font->numchars) {
962 s_box(name, 0, 0, black, 20, 0);
965 r = swf_SetDefineText(tag, font, &color, text, size);
967 s_addcharacter(name, id, tag, r);
971 void s_quicktime(char*name, char*url)
976 memset(&r, 0, sizeof(r));
978 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
980 swf_SetString(tag, url);
982 s_addcharacter(name, id, tag, r);
986 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)
989 EditTextLayout layout;
992 if(fontname && *fontname) {
993 flags |= ET_USEOUTLINES;
994 font = dictionary_lookup(&fonts, fontname);
996 syntaxerror("font \"%s\" not known!", fontname);
998 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
1000 layout.align = align;
1001 layout.leftmargin = 0;
1002 layout.rightmargin = 0;
1010 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1012 s_addcharacter(name, id, tag, r);
1016 /* type: either "jpeg" or "png"
1018 void s_image(char*name, char*type, char*filename, int quality)
1020 /* an image is actually two folded: 1st bitmap, 2nd character.
1021 Both of them can be used separately */
1023 /* step 1: the bitmap */
1027 if(!strcmp(type,"jpeg")) {
1028 #ifndef HAVE_LIBJPEG
1029 warning("no jpeg support compiled in");
1030 s_box(name, 0, 0, black, 20, 0);
1033 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1034 swf_SetU16(tag, imageID);
1036 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1037 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1040 swf_GetJPEGSize(filename, &width, &height);
1047 s_addimage(name, id, tag, r);
1050 } else if(!strcmp(type,"png")) {
1052 swf_SetU16(tag, imageID);
1054 getPNG(filename, &width, &height, (unsigned char**)&data);
1057 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1060 /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
1061 tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
1062 swf_SetU16(tag, imageID);
1063 swf_SetLosslessImage(tag, data, width, height);
1069 s_addimage(name, id, tag, r);
1072 warning("image type \"%s\" not supported yet!", type);
1073 s_box(name, 0, 0, black, 20, 0);
1077 /* step 2: the character */
1078 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1079 swf_SetU16(tag, id);
1080 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1082 s_addcharacter(name, id, tag, r);
1086 void s_getBitmapSize(char*name, int*width, int*height)
1088 character_t* image = dictionary_lookup(&images, name);
1089 gradient_t* gradient = dictionary_lookup(&gradients,name);
1091 *width = image->size.xmax;
1092 *height = image->size.ymax;
1096 /* internal SWF gradient size */
1097 if(gradient->radial) {
1106 syntaxerror("No such bitmap/gradient: %s", name);
1109 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1111 gradient_t* gradient = dictionary_lookup(&gradients, object);
1112 character_t* bitmap = dictionary_lookup(&images, object);
1113 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1115 FILLSTYLE*fs = &texture->fs;
1117 memset(&p, 0, sizeof(parameters_t));
1120 fs->type = FILL_TILED;
1121 fs->id_bitmap = bitmap->id;
1122 } else if(gradient) {
1123 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1124 fs->gradient = gradient->gradient;
1126 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1127 makeMatrix(&fs->m, &p);
1128 if(gradient && !gradient->radial) {
1135 p2 = swf_TurnPoint(p1, &m);
1140 if(dictionary_lookup(&textures, name))
1141 syntaxerror("texture %s defined twice", name);
1142 dictionary_put2(&textures, name, texture);
1145 void dumpSWF(SWF*swf)
1147 TAG* tag = swf->firstTag;
1148 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1150 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1153 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1156 void s_font(char*name, char*filename)
1159 font = swf_LoadFont(filename);
1162 warning("Couldn't open font file \"%s\"", filename);
1163 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1164 memset(font, 0, sizeof(SWFFONT));
1165 dictionary_put2(&fonts, name, font);
1171 /* fix the layout. Only needed for old fonts */
1173 for(t=0;t<font->numchars;t++) {
1174 font->glyph[t].advance = 0;
1177 swf_FontCreateLayout(font);
1179 /* just in case this thing is used in .edittext later on */
1180 swf_FontPrepareForEditText(font);
1183 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1184 swf_FontSetDefine2(tag, font);
1185 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1187 swf_SetU16(tag, id);
1188 swf_SetString(tag, name);
1191 if(dictionary_lookup(&fonts, name))
1192 syntaxerror("font %s defined twice", name);
1193 dictionary_put2(&fonts, name, font);
1198 typedef struct _sound_t
1204 void s_sound(char*name, char*filename)
1206 struct WAV wav, wav2;
1210 unsigned numsamples;
1211 unsigned blocksize = 1152;
1214 if(wav_read(&wav, filename)) {
1216 wav_convert2mono(&wav, &wav2, 44100);
1217 samples = (U16*)wav2.data;
1218 numsamples = wav2.size/2;
1220 #ifdef WORDS_BIGENDIAN
1222 for(t=0;t<numsamples;t++) {
1223 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1226 } else if(mp3_read(&mp3, filename)) {
1227 fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1233 warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1238 if(numsamples%blocksize != 0)
1240 // apply padding, so that block is a multiple of blocksize
1241 int numblocks = (numsamples+blocksize-1)/blocksize;
1244 numsamples2 = numblocks * blocksize;
1245 samples2 = malloc(sizeof(U16)*numsamples2);
1246 memcpy(samples2, samples, numsamples*sizeof(U16));
1247 memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1248 numsamples = numsamples2;
1252 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1253 swf_SetU16(tag, id); //id
1256 swf_SetSoundDefineMP3(
1257 tag, mp3.data, mp3.size,
1265 swf_SetSoundDefine(tag, samples, numsamples);
1268 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1272 if(dictionary_lookup(&sounds, name))
1273 syntaxerror("sound %s defined twice", name);
1274 dictionary_put2(&sounds, name, sound);
1282 static char* gradient_getToken(const char**p)
1286 while(**p && strchr(" \t\n\r", **p)) {
1290 while(**p && !strchr(" \t\n\r", **p)) {
1293 result = malloc((*p)-start+1);
1294 memcpy(result,start,(*p)-start+1);
1295 result[(*p)-start] = 0;
1299 float parsePercent(char*str);
1300 RGBA parseColor(char*str);
1302 GRADIENT parseGradient(const char*str)
1306 const char* p = str;
1307 memset(&gradient, 0, sizeof(GRADIENT));
1309 char*posstr,*colorstr;
1312 posstr = gradient_getToken(&p);
1315 pos = (int)(parsePercent(posstr)*255.0);
1318 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1319 colorstr = gradient_getToken(&p);
1320 color = parseColor(colorstr);
1321 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1322 warning("gradient record too big- max size is 8, rest ignored");
1325 gradient.ratios[gradient.num] = pos;
1326 gradient.rgba[gradient.num] = color;
1335 void s_gradient(char*name, const char*text, int radial, int rotate)
1337 gradient_t* gradient;
1338 gradient = malloc(sizeof(gradient_t));
1339 memset(gradient, 0, sizeof(gradient_t));
1340 gradient->gradient = parseGradient(text);
1341 gradient->radial = radial;
1342 gradient->rotate = rotate;
1344 if(dictionary_lookup(&gradients, name))
1345 syntaxerror("gradient %s defined twice", name);
1346 dictionary_put2(&gradients, name, gradient);
1349 void s_action(const char*text)
1352 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1354 syntaxerror("Couldn't compile ActionScript");
1357 tag = swf_InsertTag(tag, ST_DOACTION);
1359 swf_ActionSet(tag, a);
1364 void s_initaction(const char*character, const char*text)
1368 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1370 syntaxerror("Couldn't compile ActionScript");
1373 c = (character_t*)dictionary_lookup(&characters, character);
1375 tag = swf_InsertTag(tag, ST_DOINITACTION);
1376 swf_SetU16(tag, c->id);
1377 swf_ActionSet(tag, a);
1382 int s_swf3action(char*name, char*action)
1385 instance_t* object = 0;
1387 object = (instance_t*)dictionary_lookup(&instances, name);
1388 if(!object && name && *name) {
1389 /* we have a name, but couldn't find it. Abort. */
1392 a = action_SetTarget(0, name);
1393 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1394 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1395 else if(!strcmp(action, "stop")) a = action_Stop(a);
1396 else if(!strcmp(action, "play")) a = action_Play(a);
1397 a = action_SetTarget(a, "");
1400 tag = swf_InsertTag(tag, ST_DOACTION);
1401 swf_ActionSet(tag, a);
1406 void s_outline(char*name, char*format, char*source)
1415 //swf_Shape10DrawerInit(&draw, 0);
1416 swf_Shape11DrawerInit(&draw, 0);
1418 draw_string(&draw, source);
1420 shape = swf_ShapeDrawerToShape(&draw);
1421 bounds = swf_ShapeDrawerGetBBox(&draw);
1422 draw.dealloc(&draw);
1424 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1425 outline->shape = shape;
1426 outline->bbox = bounds;
1428 if(dictionary_lookup(&outlines, name))
1429 syntaxerror("outline %s defined twice", name);
1430 dictionary_put2(&outlines, name, outline);
1433 int s_playsound(char*name, int loops, int nomultiple, int stop)
1439 sound = dictionary_lookup(&sounds, name);
1443 tag = swf_InsertTag(tag, ST_STARTSOUND);
1444 swf_SetU16(tag, sound->id); //id
1445 memset(&info, 0, sizeof(info));
1448 info.nomultiple = nomultiple;
1449 swf_SetSoundInfo(tag, &info);
1453 void s_includeswf(char*name, char*filename)
1461 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1462 f = open(filename,O_RDONLY|O_BINARY);
1464 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1465 s_box(name, 0, 0, black, 20, 0);
1468 if (swf_ReadSWF(f,&swf)<0) {
1469 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1470 s_box(name, 0, 0, black, 20, 0);
1475 /* FIXME: The following sets the bounding Box for the character.
1476 It is wrong for two reasons:
1477 a) It may be too small (in case objects in the movie clip at the borders)
1478 b) it may be too big (because the poor movie never got autocropped)
1482 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1483 swf_SetU16(tag, id);
1484 swf_SetU16(tag, swf.frameCount);
1486 swf_Relocate(&swf, idmap);
1488 ftag = swf.firstTag;
1492 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1493 if(cutout[t] == ftag->id) {
1497 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1499 if(ftag->id == ST_END)
1503 /* We simply dump all tags right after the sprite
1504 header, relying on the fact that swf_OptimizeTagOrder() will
1505 sort things out for us later.
1506 We also rely on the fact that the imported SWF is well-formed.
1508 tag = swf_InsertTag(tag, ftag->id);
1509 swf_SetBlock(tag, ftag->data, ftag->len);
1513 syntaxerror("Included file %s contains errors", filename);
1514 tag = swf_InsertTag(tag, ST_END);
1518 s_addcharacter(name, id, tag, r);
1521 SRECT s_getCharBBox(char*name)
1523 character_t* c = dictionary_lookup(&characters, name);
1524 if(!c) syntaxerror("character '%s' unknown(2)", name);
1527 SRECT s_getInstanceBBox(char*name)
1529 instance_t * i = dictionary_lookup(&instances, name);
1531 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1533 if(!c) syntaxerror("internal error(5)");
1536 parameters_t s_getParameters(char*name)
1538 instance_t * i = dictionary_lookup(&instances, name);
1539 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1540 return i->parameters;
1542 void s_startclip(char*instance, char*character, parameters_t p)
1544 character_t* c = dictionary_lookup(&characters, character);
1548 syntaxerror("character %s not known", character);
1550 i = s_addinstance(instance, c, currentdepth);
1552 m = s_instancepos(i->character->size, &p);
1554 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1555 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1556 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1558 i->lastFrame= currentframe;
1560 stack[stackpos].tag = tag;
1561 stack[stackpos].type = 2;
1570 swf_SetTagPos(stack[stackpos].tag, 0);
1571 swf_GetPlaceObject(stack[stackpos].tag, &p);
1572 p.clipdepth = currentdepth;
1574 swf_ClearTag(stack[stackpos].tag);
1575 swf_SetPlaceObject(stack[stackpos].tag, &p);
1579 void s_put(char*instance, char*character, parameters_t p)
1581 character_t* c = dictionary_lookup(&characters, character);
1585 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1588 i = s_addinstance(instance, c, currentdepth);
1590 m = s_instancepos(i->character->size, &p);
1592 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1593 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1595 i->lastFrame = currentframe;
1599 void s_jump(char*instance, parameters_t p)
1601 instance_t* i = dictionary_lookup(&instances, instance);
1604 syntaxerror("instance %s not known", instance);
1608 m = s_instancepos(i->character->size, &p);
1610 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1611 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1613 i->lastFrame = currentframe;
1616 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1620 if(num==0 || num==1)
1622 ratio = (float)pos/(float)num;
1624 p.x = (p2->x-p1->x)*ratio + p1->x;
1625 p.y = (p2->y-p1->y)*ratio + p1->y;
1626 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1627 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1628 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1629 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1631 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1632 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1633 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1634 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1636 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1637 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1638 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1639 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1641 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1642 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1643 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1644 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1648 void s_change(char*instance, parameters_t p2)
1650 instance_t* i = dictionary_lookup(&instances, instance);
1654 int frame, allframes;
1656 syntaxerror("instance %s not known", instance);
1660 allframes = currentframe - i->lastFrame - 1;
1662 warning(".change ignored. can only .put/.change an object once per frame.");
1666 m = s_instancepos(i->character->size, &p2);
1667 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1668 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1671 /* o.k., we got the start and end point set. Now iterate though all the
1672 tags in between, inserting object changes after each new frame */
1675 if(!t) syntaxerror("internal error(6)");
1677 while(frame < allframes) {
1678 if(t->id == ST_SHOWFRAME) {
1683 p = s_interpolate(&p1, &p2, frame, allframes);
1684 m = s_instancepos(i->character->size, &p); //needed?
1685 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1686 i->lastFrame = currentframe;
1687 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1689 if(frame == allframes)
1694 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1698 void s_delinstance(char*instance)
1700 instance_t* i = dictionary_lookup(&instances, instance);
1702 syntaxerror("instance %s not known", instance);
1704 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1705 swf_SetU16(tag, i->depth);
1706 dictionary_del(&instances, instance);
1709 void s_qchange(char*instance, parameters_t p)
1716 syntaxerror(".end unexpected");
1717 if(stack[stackpos-1].type == 0)
1719 else if(stack[stackpos-1].type == 1)
1721 else if(stack[stackpos-1].type == 2)
1723 else if(stack[stackpos-1].type == 3)
1725 else syntaxerror("internal error 1");
1728 // ------------------------------------------------------------------------
1730 typedef int command_func_t(map_t*args);
1732 SRECT parseBox(char*str)
1735 float xmin, xmax, ymin, ymax;
1736 char*x = strchr(str, 'x');
1738 if(!strcmp(str, "autocrop")) {
1739 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1743 d1 = strchr(x+1, ':');
1745 d2 = strchr(d1+1, ':');
1747 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1751 else if(d1 && !d2) {
1752 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1758 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1763 r.xmin = (SCOORD)(xmin*20);
1764 r.ymin = (SCOORD)(ymin*20);
1765 r.xmax = (SCOORD)(xmax*20);
1766 r.ymax = (SCOORD)(ymax*20);
1769 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1772 float parseFloat(char*str)
1776 int parseInt(char*str)
1781 if(str[0]=='+' || str[0]=='-')
1785 if(str[t]<'0' || str[t]>'9')
1786 syntaxerror("Not an Integer: \"%s\"", str);
1789 int parseTwip(char*str)
1793 if(str[0]=='+' || str[0]=='-') {
1798 dot = strchr(str, '.');
1802 return sign*parseInt(str)*20;
1804 int l=strlen(++dot);
1806 for(s=str;s<dot-1;s++)
1807 if(*s<'0' || *s>'9')
1808 syntaxerror("Not a coordinate: \"%s\"", str);
1810 if(*s<'0' || *s>'9')
1811 syntaxerror("Not a coordinate: \"%s\"", str);
1813 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1814 warning("precision loss: %s converted to twip: %s", str, dot);
1819 return sign*atoi(str)*20;
1821 return sign*atoi(str)*20+atoi(dot)*2;
1823 return sign*atoi(str)*20+atoi(dot)/5;
1828 int isPoint(char*str)
1830 if(strchr(str, '('))
1836 SPOINT parsePoint(char*str)
1840 int l = strlen(str);
1841 char*comma = strchr(str, ',');
1842 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1843 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1844 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1845 p.x = parseTwip(tmp);
1846 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1847 p.y = parseTwip(tmp);
1851 int parseColor2(char*str, RGBA*color)
1853 int l = strlen(str);
1857 struct {unsigned char r,g,b;char*name;} colors[] =
1858 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1859 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1860 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1861 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1862 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1863 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1864 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1865 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1866 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1867 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1868 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1869 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1873 if(str[0]=='#' && (l==7 || l==9)) {
1874 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1876 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1878 color->r = r; color->g = g; color->b = b; color->a = a;
1881 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1882 if(!strcmp(str, colors[t].name)) {
1887 color->r = r; color->g = g; color->b = b; color->a = a;
1893 RGBA parseColor(char*str)
1896 if(!parseColor2(str, &c))
1897 syntaxerror("Expression '%s' is not a color", str);
1901 typedef struct _muladd {
1906 MULADD parseMulAdd(char*str)
1909 char* str2 = (char*)malloc(strlen(str)+5);
1916 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1917 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1918 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1919 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1920 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1921 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1922 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1923 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1924 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1925 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1927 syntaxerror("'%s' is not a valid color transform expression", str);
1929 m.add = (int)(add*256);
1930 m.mul = (int)(mul*256);
1935 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1937 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1938 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1940 if(a<-32768) a=-32768;
1941 if(a>32767) a=32767;
1942 if(m<-32768) m=-32768;
1943 if(m>32767) m=32767;
1949 float parsePxOrPercent(char*fontname, char*str)
1951 int l = strlen(str);
1952 if(strchr(str, '%'))
1953 return parsePercent(str);
1954 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1955 float p = atof(str);
1956 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1958 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1962 float parsePercent(char*str)
1964 int l = strlen(str);
1968 return atoi(str)/100.0;
1970 syntaxerror("Expression '%s' is not a percentage", str);
1973 int isPercent(char*str)
1975 return str[strlen(str)-1]=='%';
1977 int parseNewSize(char*str, int size)
1980 return parsePercent(str)*size;
1982 return (int)(atof(str)*20);
1985 int isColor(char*str)
1988 return parseColor2(str, &c);
1991 static char* lu(map_t* args, char*name)
1993 char* value = map_lookup(args, name);
1995 map_dump(args, stdout, "");
1996 syntaxerror("internal error 2: value %s should be set", name);
2001 static int c_flash(map_t*args)
2003 char* filename = map_lookup(args, "filename");
2004 char* compressstr = lu(args, "compress");
2005 SRECT bbox = parseBox(lu(args, "bbox"));
2006 int version = parseInt(lu(args, "version"));
2007 int fps = (int)(parseFloat(lu(args, "fps"))*256);
2009 RGBA color = parseColor(lu(args, "background"));
2011 if(!filename || !*filename) {
2012 /* for compatibility */
2013 filename = map_lookup(args, "name");
2014 if(!filename || !*filename) {
2017 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2018 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2022 if(!filename || override_outputname)
2023 filename = outputname;
2025 if(!strcmp(compressstr, "default"))
2026 compress = version==6;
2027 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2029 else if(!strcmp(compressstr, "no"))
2031 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2033 s_swf(filename, bbox, version, fps, compress, color);
2036 int isRelative(char*str)
2038 return !strncmp(str, "<plus>", 6) ||
2039 !strncmp(str, "<minus>", 7);
2041 char* getOffset(char*str)
2043 if(!strncmp(str, "<plus>", 6))
2045 if(!strncmp(str, "<minus>", 7))
2047 syntaxerror("internal error (347)");
2050 int getSign(char*str)
2052 if(!strncmp(str, "<plus>", 6))
2054 if(!strncmp(str, "<minus>", 7))
2056 syntaxerror("internal error (348)");
2059 static dictionary_t points;
2060 static mem_t mpoints;
2061 int points_initialized = 0;
2063 SPOINT getPoint(SRECT r, char*name)
2066 if(!strcmp(name, "center")) {
2068 p.x = (r.xmin + r.xmax)/2;
2069 p.y = (r.ymin + r.ymax)/2;
2073 if(points_initialized)
2074 l = (int)dictionary_lookup(&points, name);
2076 syntaxerror("Invalid point: \"%s\".", name);
2079 return *(SPOINT*)&mpoints.buffer[l];
2082 static int texture2(char*name, char*object, map_t*args, int errors)
2085 char*xstr = map_lookup(args, "x");
2086 char*ystr = map_lookup(args, "y");
2087 char*widthstr = map_lookup(args, "width");
2088 char*heightstr = map_lookup(args, "height");
2089 char*scalestr = map_lookup(args, "scale");
2090 char*scalexstr = map_lookup(args, "scalex");
2091 char*scaleystr = map_lookup(args, "scaley");
2092 char*rotatestr = map_lookup(args, "rotate");
2093 char* shearstr = map_lookup(args, "shear");
2094 char* radiusstr = map_lookup(args, "r");
2096 float scalex = 1.0, scaley = 1.0;
2097 float rotate=0, shear=0;
2099 if(!*xstr && !*ystr) {
2101 syntaxerror("x and y must be set");
2104 if(*scalestr && (*scalexstr || *scaleystr)) {
2105 syntaxerror("scale and scalex/scaley can't both be set");
2108 if((*widthstr || *heightstr) && *radiusstr) {
2109 syntaxerror("width/height and radius can't both be set");
2112 widthstr = radiusstr;
2113 heightstr = radiusstr;
2115 if(!*xstr) xstr="0";
2116 if(!*ystr) ystr="0";
2117 if(!*rotatestr) rotatestr="0";
2118 if(!*shearstr) shearstr="0";
2121 scalex = scaley = parsePercent(scalestr);
2122 } else if(*scalexstr || *scaleystr) {
2123 if(scalexstr) scalex = parsePercent(scalexstr);
2124 if(scaleystr) scaley = parsePercent(scaleystr);
2125 } else if(*widthstr || *heightstr) {
2128 s_getBitmapSize(object, &width, &height);
2130 scalex = (float)parseTwip(widthstr)/(float)width;
2132 scaley = (float)parseTwip(heightstr)/(float)height;
2134 x = parseTwip(xstr);
2135 y = parseTwip(ystr);
2136 rotate = parseFloat(rotatestr);
2137 shear = parseFloat(shearstr);
2139 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2144 static int c_texture(map_t*args)
2146 char*name = lu(args, "instance");
2147 char*object = lu(args, "character");
2148 return texture2(name, object, args, 1);
2151 static int c_gradient(map_t*args)
2153 char*name = lu(args, "name");
2154 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2155 int rotate = parseInt(lu(args, "rotate"));
2159 syntaxerror("colon (:) expected");
2161 s_gradient(name, text, radial, rotate);
2163 /* check whether we also have placement information,
2164 which would make this a positioned gradient.
2165 If there is placement information, texture2() will
2166 add a texture, which has priority over the gradient.
2168 texture2(name, name, args, 0);
2171 static int c_point(map_t*args)
2173 char*name = lu(args, "name");
2177 if(!points_initialized) {
2178 dictionary_init(&points);
2180 points_initialized = 1;
2182 p.x = parseTwip(lu(args, "x"));
2183 p.y = parseTwip(lu(args, "y"));
2184 pos = mem_put(&mpoints, &p, sizeof(p));
2185 string_set(&s1, name);
2187 dictionary_put(&points, s1, (void*)pos);
2190 static int c_play(map_t*args)
2192 char*name = lu(args, "name");
2193 char*loop = lu(args, "loop");
2194 char*nomultiple = lu(args, "nomultiple");
2196 if(!strcmp(nomultiple, "nomultiple"))
2199 nm = parseInt(nomultiple);
2201 if(s_playsound(name, parseInt(loop), nm, 0)) {
2203 } else if(s_swf3action(name, "play")) {
2209 static int c_stop(map_t*args)
2211 char*name = map_lookup(args, "name");
2213 if(s_playsound(name, 0,0,1)) {
2215 } else if(s_swf3action(name, "stop")) {
2218 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2222 static int c_nextframe(map_t*args)
2224 char*name = lu(args, "name");
2226 if(s_swf3action(name, "nextframe")) {
2229 syntaxerror("I don't know anything about movie \"%s\"", name);
2233 static int c_previousframe(map_t*args)
2235 char*name = lu(args, "name");
2237 if(s_swf3action(name, "previousframe")) {
2240 syntaxerror("I don't know anything about movie \"%s\"", name);
2244 static int c_placement(map_t*args, int type)
2246 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2249 char* luminancestr = lu(args, "luminance");
2250 char* scalestr = lu(args, "scale");
2251 char* scalexstr = lu(args, "scalex");
2252 char* scaleystr = lu(args, "scaley");
2253 char* rotatestr = lu(args, "rotate");
2254 char* shearstr = lu(args, "shear");
2255 char* xstr="", *pivotstr="";
2256 char* ystr="", *anglestr="";
2257 char*above = lu(args, "above"); /*FIXME*/
2258 char*below = lu(args, "below");
2259 char* rstr = lu(args, "red");
2260 char* gstr = lu(args, "green");
2261 char* bstr = lu(args, "blue");
2262 char* astr = lu(args, "alpha");
2263 char* pinstr = lu(args, "pin");
2264 char* as = map_lookup(args, "as");
2272 if(type==9) { // (?) .rotate or .arcchange
2273 pivotstr = lu(args, "pivot");
2274 anglestr = lu(args, "angle");
2276 xstr = lu(args, "x");
2277 ystr = lu(args, "y");
2280 luminance = parseMulAdd(luminancestr);
2283 luminance.mul = 256;
2287 if(scalexstr[0]||scaleystr[0])
2288 syntaxerror("scalex/scaley and scale cannot both be set");
2289 scalexstr = scaleystr = scalestr;
2292 if(type == 0 || type == 4) {
2294 character = lu(args, "character");
2295 parameters_clear(&p);
2296 } else if (type == 5) {
2297 character = lu(args, "name");
2298 parameters_clear(&p);
2301 p = s_getParameters(instance);
2306 if(isRelative(xstr)) {
2307 if(type == 0 || type == 4)
2308 syntaxerror("relative x values not allowed for initial put or startclip");
2309 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2311 p.x = parseTwip(xstr);
2315 if(isRelative(ystr)) {
2316 if(type == 0 || type == 4)
2317 syntaxerror("relative y values not allowed for initial put or startclip");
2318 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2320 p.y = parseTwip(ystr);
2324 /* scale, scalex, scaley */
2326 oldbbox = s_getCharBBox(character);
2328 oldbbox = s_getInstanceBBox(instance);
2330 oldwidth = oldbbox.xmax - oldbbox.xmin;
2331 oldheight = oldbbox.ymax - oldbbox.ymin;
2333 if(oldwidth==0) p.scalex = 1.0;
2336 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2340 if(oldheight==0) p.scaley = 1.0;
2343 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2349 if(isRelative(rotatestr)) {
2350 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2352 p.rotate = parseFloat(rotatestr);
2358 if(isRelative(shearstr)) {
2359 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2361 p.shear = parseFloat(shearstr);
2366 if(isPoint(pivotstr))
2367 p.pivot = parsePoint(pivotstr);
2369 p.pivot = getPoint(oldbbox, pivotstr);
2373 p.pin = parsePoint(pinstr);
2375 p.pin = getPoint(oldbbox, pinstr);
2378 /* color transform */
2380 if(rstr[0] || luminancestr[0]) {
2383 r = parseMulAdd(rstr);
2385 r.add = p.cxform.r0;
2386 r.mul = p.cxform.r1;
2388 r = mergeMulAdd(r, luminance);
2389 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2391 if(gstr[0] || luminancestr[0]) {
2394 g = parseMulAdd(gstr);
2396 g.add = p.cxform.g0;
2397 g.mul = p.cxform.g1;
2399 g = mergeMulAdd(g, luminance);
2400 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2402 if(bstr[0] || luminancestr[0]) {
2405 b = parseMulAdd(bstr);
2407 b.add = p.cxform.b0;
2408 b.mul = p.cxform.b1;
2410 b = mergeMulAdd(b, luminance);
2411 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2414 MULADD a = parseMulAdd(astr);
2415 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2419 s_put(instance, character, p);
2421 s_change(instance, p);
2423 s_qchange(instance, p);
2425 s_jump(instance, p);
2427 s_startclip(instance, character, p);
2428 else if(type == 5) {
2430 s_buttonput(character, as, p);
2432 s_buttonput(character, "shape", p);
2437 static int c_put(map_t*args)
2439 c_placement(args, 0);
2442 static int c_change(map_t*args)
2444 c_placement(args, 1);
2447 static int c_qchange(map_t*args)
2449 c_placement(args, 2);
2452 static int c_arcchange(map_t*args)
2454 c_placement(args, 2);
2457 static int c_jump(map_t*args)
2459 c_placement(args, 3);
2462 static int c_startclip(map_t*args)
2464 c_placement(args, 4);
2467 static int c_show(map_t*args)
2469 c_placement(args, 5);
2472 static int c_del(map_t*args)
2474 char*instance = lu(args, "name");
2475 s_delinstance(instance);
2478 static int c_end(map_t*args)
2483 static int c_sprite(map_t*args)
2485 char* name = lu(args, "name");
2489 static int c_frame(map_t*args)
2491 char*framestr = lu(args, "n");
2492 char*cutstr = lu(args, "cut");
2493 char*name = lu(args, "name");
2496 if(strcmp(cutstr, "no"))
2498 if(isRelative(framestr)) {
2499 frame = s_getframe();
2500 if(getSign(framestr)<0)
2501 syntaxerror("relative frame expressions must be positive");
2502 frame += parseInt(getOffset(framestr));
2505 frame = parseInt(framestr);
2506 if(s_getframe() >= frame
2507 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2508 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2510 s_frame(frame, cut, name);
2513 static int c_primitive(map_t*args)
2515 char*name = lu(args, "name");
2516 char*command = lu(args, "commandname");
2517 int width=0, height=0, r=0;
2518 int linewidth = parseTwip(lu(args, "line"));
2519 char*colorstr = lu(args, "color");
2520 RGBA color = parseColor(colorstr);
2521 char*fillstr = lu(args, "fill");
2528 if(!strcmp(command, "circle"))
2530 else if(!strcmp(command, "filled"))
2534 width = parseTwip(lu(args, "width"));
2535 height = parseTwip(lu(args, "height"));
2536 } else if (type==1) {
2537 r = parseTwip(lu(args, "r"));
2538 } else if (type==2) {
2539 outline = lu(args, "outline");
2542 if(!strcmp(fillstr, "fill"))
2544 if(!strcmp(fillstr, "none"))
2546 if(width<0 || height<0 || linewidth<0 || r<0)
2547 syntaxerror("values width, height, line, r must be positive");
2549 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2550 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2551 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2555 static int c_textshape(map_t*args)
2557 char*name = lu(args, "name");
2558 char*text = lu(args, "text");
2559 char*font = lu(args, "font");
2560 float size = parsePxOrPercent(font, lu(args, "size"));
2562 s_textshape(name, font, size, text);
2566 static int c_swf(map_t*args)
2568 char*name = lu(args, "name");
2569 char*filename = lu(args, "filename");
2570 char*command = lu(args, "commandname");
2571 if(!strcmp(command, "shape"))
2572 warning("Please use .swf instead of .shape");
2573 s_includeswf(name, filename);
2577 static int c_font(map_t*args)
2579 char*name = lu(args, "name");
2580 char*filename = lu(args, "filename");
2581 s_font(name, filename);
2585 static int c_sound(map_t*args)
2587 char*name = lu(args, "name");
2588 char*filename = lu(args, "filename");
2589 s_sound(name, filename);
2593 static int c_text(map_t*args)
2595 char*name = lu(args, "name");
2596 char*text = lu(args, "text");
2597 char*font = lu(args, "font");
2598 float size = parsePxOrPercent(font, lu(args, "size"));
2599 RGBA color = parseColor(lu(args, "color"));
2600 s_text(name, font, text, (int)(size*100), color);
2604 static int c_soundtrack(map_t*args)
2609 static int c_quicktime(map_t*args)
2611 char*name = lu(args, "name");
2612 char*url = lu(args, "url");
2613 s_quicktime(name, url);
2617 static int c_image(map_t*args)
2619 char*command = lu(args, "commandname");
2620 char*name = lu(args, "name");
2621 char*filename = lu(args, "filename");
2622 if(!strcmp(command,"jpeg")) {
2623 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2624 s_image(name, "jpeg", filename, quality);
2626 s_image(name, "png", filename, 0);
2631 static int c_outline(map_t*args)
2633 char*name = lu(args, "name");
2634 char*format = lu(args, "format");
2638 syntaxerror("colon (:) expected");
2640 s_outline(name, format, text);
2644 int fakechar(map_t*args)
2646 char*name = lu(args, "name");
2647 s_box(name, 0, 0, black, 20, 0);
2651 static int c_egon(map_t*args) {return fakechar(args);}
2652 static int c_button(map_t*args) {
2653 char*name = lu(args, "name");
2657 static int current_button_flags = 0;
2658 static int c_on_press(map_t*args)
2660 char*position = lu(args, "position");
2662 if(!strcmp(position, "inside")) {
2663 current_button_flags |= BC_OVERUP_OVERDOWN;
2664 } else if(!strcmp(position, "outside")) {
2665 //current_button_flags |= BC_IDLE_OUTDOWN;
2666 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2667 } else if(!strcmp(position, "anywhere")) {
2668 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2671 if(type == RAWDATA) {
2673 s_buttonaction(current_button_flags, action);
2674 current_button_flags = 0;
2680 static int c_on_release(map_t*args)
2682 char*position = lu(args, "position");
2684 if(!strcmp(position, "inside")) {
2685 current_button_flags |= BC_OVERDOWN_OVERUP;
2686 } else if(!strcmp(position, "outside")) {
2687 current_button_flags |= BC_OUTDOWN_IDLE;
2688 } else if(!strcmp(position, "anywhere")) {
2689 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2692 if(type == RAWDATA) {
2694 s_buttonaction(current_button_flags, action);
2695 current_button_flags = 0;
2701 static int c_on_move_in(map_t*args)
2703 char*position = lu(args, "state");
2705 if(!strcmp(position, "pressed")) {
2706 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2707 } else if(!strcmp(position, "not_pressed")) {
2708 current_button_flags |= BC_IDLE_OVERUP;
2709 } else if(!strcmp(position, "any")) {
2710 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2713 if(type == RAWDATA) {
2715 s_buttonaction(current_button_flags, action);
2716 current_button_flags = 0;
2722 static int c_on_move_out(map_t*args)
2724 char*position = lu(args, "state");
2726 if(!strcmp(position, "pressed")) {
2727 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2728 } else if(!strcmp(position, "not_pressed")) {
2729 current_button_flags |= BC_OVERUP_IDLE;
2730 } else if(!strcmp(position, "any")) {
2731 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2734 if(type == RAWDATA) {
2736 s_buttonaction(current_button_flags, action);
2737 current_button_flags = 0;
2743 static int c_on_key(map_t*args)
2745 char*key = lu(args, "key");
2747 if(strlen(key)==1) {
2750 current_button_flags |= 0x4000 + (key[0]*0x200);
2752 syntaxerror("invalid character: %c"+key[0]);
2757 <ctrl-x> = 0x200*(x-'a')
2761 syntaxerror("invalid key: %s",key);
2764 if(type == RAWDATA) {
2766 s_buttonaction(current_button_flags, action);
2767 current_button_flags = 0;
2774 static int c_edittext(map_t*args)
2776 //"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"},
2777 char*name = lu(args, "name");
2778 char*font = lu(args, "font");
2779 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2780 int width = parseTwip(lu(args, "width"));
2781 int height = parseTwip(lu(args, "height"));
2782 char*text = lu(args, "text");
2783 RGBA color = parseColor(lu(args, "color"));
2784 int maxlength = parseInt(lu(args, "maxlength"));
2785 char*variable = lu(args, "variable");
2786 char*passwordstr = lu(args, "password");
2787 char*wordwrapstr = lu(args, "wordwrap");
2788 char*multilinestr = lu(args, "multiline");
2789 char*htmlstr = lu(args, "html");
2790 char*noselectstr = lu(args, "noselect");
2791 char*readonlystr = lu(args, "readonly");
2792 char*borderstr = lu(args, "border");
2793 char*autosizestr = lu(args, "autosize");
2794 char*alignstr = lu(args, "align");
2798 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2799 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2800 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2801 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2802 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2803 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2804 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2805 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2806 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2807 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2808 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2809 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2810 else syntaxerror("Unknown alignment: %s", alignstr);
2812 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2816 static int c_morphshape(map_t*args) {return fakechar(args);}
2817 static int c_movie(map_t*args) {return fakechar(args);}
2819 static char* readfile(const char*filename)
2821 FILE*fi = fopen(filename, "rb");
2825 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2826 fseek(fi, 0, SEEK_END);
2828 fseek(fi, 0, SEEK_SET);
2829 text = rfx_alloc(l+1);
2830 fread(text, l, 1, fi);
2836 static int c_action(map_t*args)
2838 char* filename = map_lookup(args, "filename");
2839 if(!filename ||!*filename) {
2841 if(type != RAWDATA) {
2842 syntaxerror("colon (:) expected");
2846 s_action(readfile(filename));
2852 static int c_initaction(map_t*args)
2854 char* character = lu(args, "name");
2855 char* filename = map_lookup(args, "filename");
2856 if(!filename ||!*filename) {
2858 if(type != RAWDATA) {
2859 syntaxerror("colon (:) expected");
2861 s_initaction(character, text);
2863 s_initaction(character, readfile(filename));
2871 command_func_t* func;
2874 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2875 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2876 // "import" type stuff
2877 {"swf", c_swf, "name filename"},
2878 {"shape", c_swf, "name filename"},
2879 {"jpeg", c_image, "name filename quality=80%"},
2880 {"png", c_image, "name filename"},
2881 {"movie", c_movie, "name filename"},
2882 {"sound", c_sound, "name filename"},
2883 {"font", c_font, "name filename"},
2884 {"soundtrack", c_soundtrack, "filename"},
2885 {"quicktime", c_quicktime, "url"},
2887 // generators of primitives
2889 {"point", c_point, "name x=0 y=0"},
2890 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2891 {"outline", c_outline, "name format=simple"},
2892 {"textshape", c_textshape, "name font size=100% text"},
2894 // character generators
2895 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2896 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2897 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2899 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2900 {"text", c_text, "name text font size=100% color=white"},
2901 {"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="},
2902 {"morphshape", c_morphshape, "name start end"},
2903 {"button", c_button, "name"},
2904 {"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="},
2905 {"on_press", c_on_press, "position=inside"},
2906 {"on_release", c_on_release, "position=anywhere"},
2907 {"on_move_in", c_on_move_in, "state=not_pressed"},
2908 {"on_move_out", c_on_move_out, "state=not_pressed"},
2909 {"on_key", c_on_key, "key=any"},
2912 {"play", c_play, "name loop=0 @nomultiple=0"},
2913 {"stop", c_stop, "name= "},
2914 {"nextframe", c_nextframe, "name"},
2915 {"previousframe", c_previousframe, "name"},
2917 // object placement tags
2918 {"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="},
2919 {"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="},
2920 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2921 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2922 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2923 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2924 {"del", c_del, "name"},
2925 // virtual object placement
2926 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2928 // commands which start a block
2929 //startclip (see above)
2930 {"sprite", c_sprite, "name"},
2931 {"action", c_action, "filename="},
2932 {"initaction", c_initaction, "name filename="},
2938 static map_t parseArguments(char*command, char*pattern)
2954 string_set(&t1, "commandname");
2955 string_set(&t2, command);
2956 map_put(&result, t1, t2);
2958 if(!pattern || !*pattern)
2965 if(!strncmp("<i> ", x, 3)) {
2967 if(type == COMMAND || type == RAWDATA) {
2969 syntaxerror("character name expected");
2971 name[pos].str = "instance";
2973 value[pos].str = text;
2974 value[pos].len = strlen(text);
2978 if(type == ASSIGNMENT)
2981 name[pos].str = "character";
2983 value[pos].str = text;
2984 value[pos].len = strlen(text);
2992 isboolean[pos] = (x[0] =='@');
3005 name[pos].len = d-x;
3010 name[pos].len = e-x;
3011 value[pos].str = e+1;
3012 value[pos].len = d-e-1;
3020 /* for(t=0;t<len;t++) {
3021 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3022 isboolean[t]?"(boolean)":"");
3027 if(type == RAWDATA || type == COMMAND) {
3032 // first, search for boolean arguments
3033 for(pos=0;pos<len;pos++)
3035 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3037 if(type == ASSIGNMENT)
3039 value[pos].str = text;
3040 value[pos].len = strlen(text);
3041 /*printf("setting boolean parameter %s (to %s)\n",
3042 strdup_n(name[pos], namelen[pos]),
3043 strdup_n(value[pos], valuelen[pos]));*/
3048 // second, search for normal arguments
3050 for(pos=0;pos<len;pos++)
3052 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3053 (type != ASSIGNMENT && !set[pos])) {
3055 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3057 if(type == ASSIGNMENT)
3060 value[pos].str = text;
3061 value[pos].len = strlen(text);
3063 printf("setting parameter %s (to %s)\n",
3064 strdup_n(name[pos].str, name[pos].len),
3065 strdup_n(value[pos].str, value[pos].len));
3071 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3075 for(t=0;t<len;t++) {
3076 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3079 for(t=0;t<len;t++) {
3080 if(value[t].str && value[t].str[0] == '*') {
3081 //relative default- take value from some other parameter
3083 for(s=0;s<len;s++) {
3084 if(value[s].len == value[t].len-1 &&
3085 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3086 value[t].str = value[s].str;
3089 if(value[t].str == 0) {
3091 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3095 /* ok, now construct the dictionary from the parameters */
3099 map_put(&result, name[t], value[t]);
3103 static void parseArgumentsForCommand(char*command)
3108 msg("<verbose> parse Command: %s (line %d)", command, line);
3110 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3111 if(!strcmp(arguments[t].command, command)) {
3113 /* ugly hack- will be removed soon (once documentation and .sc generating
3114 utilities have been changed) */
3115 if(!strcmp(command, "swf") && !stackpos) {
3116 warning("Please use .flash instead of .swf- this will be mandatory soon");
3121 args = parseArguments(command, arguments[t].arguments);
3127 syntaxerror("command %s not known", command);
3129 // catch missing .flash directives at the beginning of a file
3130 if(strcmp(command, "flash") && !stackpos)
3132 syntaxerror("No movie defined- use .flash first");
3136 printf(".%s\n", command);fflush(stdout);
3137 map_dump(&args, stdout, "\t");fflush(stdout);
3140 (*arguments[nr].func)(&args);
3142 /*if(!strcmp(command, "button") ||
3143 !strcmp(command, "action")) {
3146 if(type == COMMAND) {
3147 if(!strcmp(text, "end"))
3161 int main (int argc,char ** argv)
3164 processargs(argc, argv);
3165 initLog(0,-1,0,0,-1,verbose);
3168 args_callback_usage(argv[0]);
3172 file = generateTokens(filename);
3174 printf("parser returned error.\n");
3180 while(!noMoreTokens()) {
3183 syntaxerror("command expected");
3184 parseArgumentsForCommand(text);