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;
1118 fs->type = FILL_TILED;
1119 fs->id_bitmap = bitmap->id;
1120 } else if(gradient) {
1121 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1122 fs->gradient = gradient->gradient;
1124 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1125 makeMatrix(&fs->m, &p);
1126 if(gradient && !gradient->radial) {
1133 p2 = swf_TurnPoint(p1, &m);
1138 if(dictionary_lookup(&textures, name))
1139 syntaxerror("texture %s defined twice", name);
1140 dictionary_put2(&textures, name, texture);
1143 void dumpSWF(SWF*swf)
1145 TAG* tag = swf->firstTag;
1146 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1148 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1151 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1154 void s_font(char*name, char*filename)
1157 font = swf_LoadFont(filename);
1160 warning("Couldn't open font file \"%s\"", filename);
1161 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1162 memset(font, 0, sizeof(SWFFONT));
1163 dictionary_put2(&fonts, name, font);
1169 /* fix the layout. Only needed for old fonts */
1171 for(t=0;t<font->numchars;t++) {
1172 font->glyph[t].advance = 0;
1175 swf_FontCreateLayout(font);
1177 /* just in case this thing is used in .edittext later on */
1178 swf_FontPrepareForEditText(font);
1181 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1182 swf_FontSetDefine2(tag, font);
1183 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1185 swf_SetU16(tag, id);
1186 swf_SetString(tag, name);
1189 if(dictionary_lookup(&fonts, name))
1190 syntaxerror("font %s defined twice", name);
1191 dictionary_put2(&fonts, name, font);
1196 typedef struct _sound_t
1202 void s_sound(char*name, char*filename)
1204 struct WAV wav, wav2;
1208 unsigned numsamples;
1209 unsigned blocksize = 1152;
1212 if(wav_read(&wav, filename)) {
1214 wav_convert2mono(&wav, &wav2, 44100);
1215 samples = (U16*)wav2.data;
1216 numsamples = wav2.size/2;
1218 #ifdef WORDS_BIGENDIAN
1220 for(t=0;t<numsamples;t++) {
1221 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1224 } else if(mp3_read(&mp3, filename)) {
1225 fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1231 warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1236 if(numsamples%blocksize != 0)
1238 // apply padding, so that block is a multiple of blocksize
1239 int numblocks = (numsamples+blocksize-1)/blocksize;
1242 numsamples2 = numblocks * blocksize;
1243 samples2 = malloc(sizeof(U16)*numsamples2);
1244 memcpy(samples2, samples, numsamples*sizeof(U16));
1245 memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1246 numsamples = numsamples2;
1250 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1251 swf_SetU16(tag, id); //id
1254 swf_SetSoundDefineMP3(
1255 tag, mp3.data, mp3.size,
1263 swf_SetSoundDefine(tag, samples, numsamples);
1266 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1270 if(dictionary_lookup(&sounds, name))
1271 syntaxerror("sound %s defined twice", name);
1272 dictionary_put2(&sounds, name, sound);
1280 static char* gradient_getToken(const char**p)
1284 while(**p && strchr(" \t\n\r", **p)) {
1288 while(**p && !strchr(" \t\n\r", **p)) {
1291 result = malloc((*p)-start+1);
1292 memcpy(result,start,(*p)-start+1);
1293 result[(*p)-start] = 0;
1297 float parsePercent(char*str);
1298 RGBA parseColor(char*str);
1300 GRADIENT parseGradient(const char*str)
1304 const char* p = str;
1305 memset(&gradient, 0, sizeof(GRADIENT));
1307 char*posstr,*colorstr;
1310 posstr = gradient_getToken(&p);
1313 pos = (int)(parsePercent(posstr)*255.0);
1316 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1317 colorstr = gradient_getToken(&p);
1318 color = parseColor(colorstr);
1319 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1320 warning("gradient record too big- max size is 8, rest ignored");
1323 gradient.ratios[gradient.num] = pos;
1324 gradient.rgba[gradient.num] = color;
1333 void s_gradient(char*name, const char*text, int radial, int rotate)
1335 gradient_t* gradient;
1336 gradient = malloc(sizeof(gradient_t));
1337 memset(gradient, 0, sizeof(gradient_t));
1338 gradient->gradient = parseGradient(text);
1339 gradient->radial = radial;
1340 gradient->rotate = rotate;
1342 if(dictionary_lookup(&gradients, name))
1343 syntaxerror("gradient %s defined twice", name);
1344 dictionary_put2(&gradients, name, gradient);
1347 void s_action(const char*text)
1350 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1352 syntaxerror("Couldn't compile ActionScript");
1355 tag = swf_InsertTag(tag, ST_DOACTION);
1357 swf_ActionSet(tag, a);
1362 void s_initaction(const char*character, const char*text)
1366 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1368 syntaxerror("Couldn't compile ActionScript");
1371 c = (character_t*)dictionary_lookup(&characters, character);
1373 tag = swf_InsertTag(tag, ST_DOINITACTION);
1374 swf_SetU16(tag, c->id);
1375 swf_ActionSet(tag, a);
1380 int s_swf3action(char*name, char*action)
1383 instance_t* object = 0;
1385 dictionary_lookup(&instances, name);
1386 if(!object && name && *name) {
1387 /* we have a name, but couldn't find it. Abort. */
1390 a = action_SetTarget(0, name);
1391 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1392 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1393 else if(!strcmp(action, "stop")) a = action_Stop(a);
1394 else if(!strcmp(action, "play")) a = action_Play(a);
1395 a = action_SetTarget(a, "");
1398 tag = swf_InsertTag(tag, ST_DOACTION);
1399 swf_ActionSet(tag, a);
1404 void s_outline(char*name, char*format, char*source)
1413 //swf_Shape10DrawerInit(&draw, 0);
1414 swf_Shape11DrawerInit(&draw, 0);
1416 draw_string(&draw, source);
1418 shape = swf_ShapeDrawerToShape(&draw);
1419 bounds = swf_ShapeDrawerGetBBox(&draw);
1420 draw.dealloc(&draw);
1422 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1423 outline->shape = shape;
1424 outline->bbox = bounds;
1426 if(dictionary_lookup(&outlines, name))
1427 syntaxerror("outline %s defined twice", name);
1428 dictionary_put2(&outlines, name, outline);
1431 int s_playsound(char*name, int loops, int nomultiple, int stop)
1437 sound = dictionary_lookup(&sounds, name);
1441 tag = swf_InsertTag(tag, ST_STARTSOUND);
1442 swf_SetU16(tag, sound->id); //id
1443 memset(&info, 0, sizeof(info));
1446 info.nomultiple = nomultiple;
1447 swf_SetSoundInfo(tag, &info);
1451 void s_includeswf(char*name, char*filename)
1459 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1460 f = open(filename,O_RDONLY|O_BINARY);
1462 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1463 s_box(name, 0, 0, black, 20, 0);
1466 if (swf_ReadSWF(f,&swf)<0) {
1467 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1468 s_box(name, 0, 0, black, 20, 0);
1473 /* FIXME: The following sets the bounding Box for the character.
1474 It is wrong for two reasons:
1475 a) It may be too small (in case objects in the movie clip at the borders)
1476 b) it may be too big (because the poor movie never got autocropped)
1480 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1481 swf_SetU16(tag, id);
1482 swf_SetU16(tag, swf.frameCount);
1484 swf_Relocate(&swf, idmap);
1486 ftag = swf.firstTag;
1490 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1491 if(cutout[t] == ftag->id) {
1495 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1497 if(ftag->id == ST_END)
1501 /* We simply dump all tags right after the sprite
1502 header, relying on the fact that swf_OptimizeTagOrder() will
1503 sort things out for us later.
1504 We also rely on the fact that the imported SWF is well-formed.
1506 tag = swf_InsertTag(tag, ftag->id);
1507 swf_SetBlock(tag, ftag->data, ftag->len);
1511 syntaxerror("Included file %s contains errors", filename);
1512 tag = swf_InsertTag(tag, ST_END);
1516 s_addcharacter(name, id, tag, r);
1519 SRECT s_getCharBBox(char*name)
1521 character_t* c = dictionary_lookup(&characters, name);
1522 if(!c) syntaxerror("character '%s' unknown(2)", name);
1525 SRECT s_getInstanceBBox(char*name)
1527 instance_t * i = dictionary_lookup(&instances, name);
1529 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1531 if(!c) syntaxerror("internal error(5)");
1534 parameters_t s_getParameters(char*name)
1536 instance_t * i = dictionary_lookup(&instances, name);
1537 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1538 return i->parameters;
1540 void s_startclip(char*instance, char*character, parameters_t p)
1542 character_t* c = dictionary_lookup(&characters, character);
1546 syntaxerror("character %s not known", character);
1548 i = s_addinstance(instance, c, currentdepth);
1550 m = s_instancepos(i->character->size, &p);
1552 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1553 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1554 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1556 i->lastFrame= currentframe;
1558 stack[stackpos].tag = tag;
1559 stack[stackpos].type = 2;
1568 swf_SetTagPos(stack[stackpos].tag, 0);
1569 swf_GetPlaceObject(stack[stackpos].tag, &p);
1570 p.clipdepth = currentdepth;
1572 swf_ClearTag(stack[stackpos].tag);
1573 swf_SetPlaceObject(stack[stackpos].tag, &p);
1577 void s_put(char*instance, char*character, parameters_t p)
1579 character_t* c = dictionary_lookup(&characters, character);
1583 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1586 i = s_addinstance(instance, c, currentdepth);
1588 m = s_instancepos(i->character->size, &p);
1590 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1591 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1593 i->lastFrame = currentframe;
1597 void s_jump(char*instance, parameters_t p)
1599 instance_t* i = dictionary_lookup(&instances, instance);
1602 syntaxerror("instance %s not known", instance);
1606 m = s_instancepos(i->character->size, &p);
1608 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1609 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1611 i->lastFrame = currentframe;
1614 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1618 if(num==0 || num==1)
1620 ratio = (float)pos/(float)num;
1622 p.x = (p2->x-p1->x)*ratio + p1->x;
1623 p.y = (p2->y-p1->y)*ratio + p1->y;
1624 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1625 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1626 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1627 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1629 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1630 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1631 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1632 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1634 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1635 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1636 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1637 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1639 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1640 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1641 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1642 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1646 void s_change(char*instance, parameters_t p2)
1648 instance_t* i = dictionary_lookup(&instances, instance);
1652 int frame, allframes;
1654 syntaxerror("instance %s not known", instance);
1658 allframes = currentframe - i->lastFrame - 1;
1660 warning(".change ignored. can only .put/.change an object once per frame.");
1664 m = s_instancepos(i->character->size, &p2);
1665 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1666 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1669 /* o.k., we got the start and end point set. Now iterate though all the
1670 tags in between, inserting object changes after each new frame */
1673 if(!t) syntaxerror("internal error(6)");
1675 while(frame < allframes) {
1676 if(t->id == ST_SHOWFRAME) {
1681 p = s_interpolate(&p1, &p2, frame, allframes);
1682 m = s_instancepos(i->character->size, &p); //needed?
1683 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1684 i->lastFrame = currentframe;
1685 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1687 if(frame == allframes)
1692 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1696 void s_delinstance(char*instance)
1698 instance_t* i = dictionary_lookup(&instances, instance);
1700 syntaxerror("instance %s not known", instance);
1702 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1703 swf_SetU16(tag, i->depth);
1704 dictionary_del(&instances, instance);
1707 void s_qchange(char*instance, parameters_t p)
1714 syntaxerror(".end unexpected");
1715 if(stack[stackpos-1].type == 0)
1717 else if(stack[stackpos-1].type == 1)
1719 else if(stack[stackpos-1].type == 2)
1721 else if(stack[stackpos-1].type == 3)
1723 else syntaxerror("internal error 1");
1726 // ------------------------------------------------------------------------
1728 typedef int command_func_t(map_t*args);
1730 SRECT parseBox(char*str)
1733 float xmin, xmax, ymin, ymax;
1734 char*x = strchr(str, 'x');
1736 if(!strcmp(str, "autocrop")) {
1737 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1741 d1 = strchr(x+1, ':');
1743 d2 = strchr(d1+1, ':');
1745 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1749 else if(d1 && !d2) {
1750 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1756 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1761 r.xmin = (SCOORD)(xmin*20);
1762 r.ymin = (SCOORD)(ymin*20);
1763 r.xmax = (SCOORD)(xmax*20);
1764 r.ymax = (SCOORD)(ymax*20);
1767 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1770 float parseFloat(char*str)
1774 int parseInt(char*str)
1779 if(str[0]=='+' || str[0]=='-')
1783 if(str[t]<'0' || str[t]>'9')
1784 syntaxerror("Not an Integer: \"%s\"", str);
1787 int parseTwip(char*str)
1791 if(str[0]=='+' || str[0]=='-') {
1796 dot = strchr(str, '.');
1800 return sign*parseInt(str)*20;
1802 int l=strlen(++dot);
1804 for(s=str;s<dot-1;s++)
1805 if(*s<'0' || *s>'9')
1806 syntaxerror("Not a coordinate: \"%s\"", str);
1808 if(*s<'0' || *s>'9')
1809 syntaxerror("Not a coordinate: \"%s\"", str);
1811 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1812 warning("precision loss: %s converted to twip: %s", str, dot);
1817 return sign*atoi(str)*20;
1819 return sign*atoi(str)*20+atoi(dot)*2;
1821 return sign*atoi(str)*20+atoi(dot)/5;
1826 int isPoint(char*str)
1828 if(strchr(str, '('))
1834 SPOINT parsePoint(char*str)
1838 int l = strlen(str);
1839 char*comma = strchr(str, ',');
1840 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1841 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1842 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1843 p.x = parseTwip(tmp);
1844 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1845 p.y = parseTwip(tmp);
1849 int parseColor2(char*str, RGBA*color)
1851 int l = strlen(str);
1855 struct {unsigned char r,g,b;char*name;} colors[] =
1856 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1857 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1858 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1859 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1860 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1861 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1862 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1863 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1864 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1865 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1866 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1867 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1871 if(str[0]=='#' && (l==7 || l==9)) {
1872 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1874 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1876 color->r = r; color->g = g; color->b = b; color->a = a;
1879 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1880 if(!strcmp(str, colors[t].name)) {
1885 color->r = r; color->g = g; color->b = b; color->a = a;
1891 RGBA parseColor(char*str)
1894 if(!parseColor2(str, &c))
1895 syntaxerror("Expression '%s' is not a color", str);
1899 typedef struct _muladd {
1904 MULADD parseMulAdd(char*str)
1907 char* str2 = (char*)malloc(strlen(str)+5);
1914 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1915 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1916 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1917 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1918 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1919 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1920 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1921 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1922 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1923 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1925 syntaxerror("'%s' is not a valid color transform expression", str);
1927 m.add = (int)(add*256);
1928 m.mul = (int)(mul*256);
1933 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1935 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1936 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1938 if(a<-32768) a=-32768;
1939 if(a>32767) a=32767;
1940 if(m<-32768) m=-32768;
1941 if(m>32767) m=32767;
1947 float parsePxOrPercent(char*fontname, char*str)
1949 int l = strlen(str);
1950 if(strchr(str, '%'))
1951 return parsePercent(str);
1952 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1953 float p = atof(str);
1954 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1956 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1960 float parsePercent(char*str)
1962 int l = strlen(str);
1966 return atoi(str)/100.0;
1968 syntaxerror("Expression '%s' is not a percentage", str);
1971 int isPercent(char*str)
1973 return str[strlen(str)-1]=='%';
1975 int parseNewSize(char*str, int size)
1978 return parsePercent(str)*size;
1980 return (int)(atof(str)*20);
1983 int isColor(char*str)
1986 return parseColor2(str, &c);
1989 static char* lu(map_t* args, char*name)
1991 char* value = map_lookup(args, name);
1993 map_dump(args, stdout, "");
1994 syntaxerror("internal error 2: value %s should be set", name);
1999 static int c_flash(map_t*args)
2001 char* filename = map_lookup(args, "filename");
2002 char* compressstr = lu(args, "compress");
2003 SRECT bbox = parseBox(lu(args, "bbox"));
2004 int version = parseInt(lu(args, "version"));
2005 int fps = (int)(parseFloat(lu(args, "fps"))*256);
2007 RGBA color = parseColor(lu(args, "background"));
2009 if(!filename || !*filename) {
2010 /* for compatibility */
2011 filename = map_lookup(args, "name");
2012 if(!filename || !*filename) {
2015 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2016 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2020 if(!filename || override_outputname)
2021 filename = outputname;
2023 if(!strcmp(compressstr, "default"))
2024 compress = version==6;
2025 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2027 else if(!strcmp(compressstr, "no"))
2029 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2031 s_swf(filename, bbox, version, fps, compress, color);
2034 int isRelative(char*str)
2036 return !strncmp(str, "<plus>", 6) ||
2037 !strncmp(str, "<minus>", 7);
2039 char* getOffset(char*str)
2041 if(!strncmp(str, "<plus>", 6))
2043 if(!strncmp(str, "<minus>", 7))
2045 syntaxerror("internal error (347)");
2048 int getSign(char*str)
2050 if(!strncmp(str, "<plus>", 6))
2052 if(!strncmp(str, "<minus>", 7))
2054 syntaxerror("internal error (348)");
2057 static dictionary_t points;
2058 static mem_t mpoints;
2059 int points_initialized = 0;
2061 SPOINT getPoint(SRECT r, char*name)
2064 if(!strcmp(name, "center")) {
2066 p.x = (r.xmin + r.xmax)/2;
2067 p.y = (r.ymin + r.ymax)/2;
2071 if(points_initialized)
2072 l = (int)dictionary_lookup(&points, name);
2074 syntaxerror("Invalid point: \"%s\".", name);
2077 return *(SPOINT*)&mpoints.buffer[l];
2080 static int texture2(char*name, char*object, map_t*args, int errors)
2083 char*xstr = map_lookup(args, "x");
2084 char*ystr = map_lookup(args, "y");
2085 char*widthstr = map_lookup(args, "width");
2086 char*heightstr = map_lookup(args, "height");
2087 char*scalestr = map_lookup(args, "scale");
2088 char*scalexstr = map_lookup(args, "scalex");
2089 char*scaleystr = map_lookup(args, "scaley");
2090 char*rotatestr = map_lookup(args, "rotate");
2091 char* shearstr = map_lookup(args, "shear");
2092 char* radiusstr = map_lookup(args, "r");
2094 float scalex = 1.0, scaley = 1.0;
2095 float rotate=0, shear=0;
2097 if(!*xstr && !*ystr) {
2099 syntaxerror("x and y must be set");
2102 if(*scalestr && (*scalexstr || *scaleystr)) {
2103 syntaxerror("scale and scalex/scaley can't both be set");
2106 if((*widthstr || *heightstr) && *radiusstr) {
2107 syntaxerror("width/height and radius can't both be set");
2110 widthstr = radiusstr;
2111 heightstr = radiusstr;
2113 if(!*xstr) xstr="0";
2114 if(!*ystr) ystr="0";
2115 if(!*rotatestr) rotatestr="0";
2116 if(!*shearstr) shearstr="0";
2119 scalex = scaley = parsePercent(scalestr);
2120 } else if(*scalexstr || *scaleystr) {
2121 if(scalexstr) scalex = parsePercent(scalexstr);
2122 if(scaleystr) scaley = parsePercent(scaleystr);
2123 } else if(*widthstr || *heightstr) {
2126 s_getBitmapSize(object, &width, &height);
2128 scalex = (float)parseTwip(widthstr)/(float)width;
2130 scaley = (float)parseTwip(heightstr)/(float)height;
2132 x = parseTwip(xstr);
2133 y = parseTwip(ystr);
2134 rotate = parseFloat(rotatestr);
2135 shear = parseFloat(shearstr);
2137 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2142 static int c_texture(map_t*args)
2144 char*name = lu(args, "instance");
2145 char*object = lu(args, "character");
2146 return texture2(name, object, args, 1);
2149 static int c_gradient(map_t*args)
2151 char*name = lu(args, "name");
2152 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2153 int rotate = parseInt(lu(args, "rotate"));
2157 syntaxerror("colon (:) expected");
2159 s_gradient(name, text, radial, rotate);
2161 /* check whether we also have placement information,
2162 which would make this a positioned gradient.
2163 If there is placement information, texture2() will
2164 add a texture, which has priority over the gradient.
2166 texture2(name, name, args, 0);
2169 static int c_point(map_t*args)
2171 char*name = lu(args, "name");
2175 if(!points_initialized) {
2176 dictionary_init(&points);
2178 points_initialized = 1;
2180 p.x = parseTwip(lu(args, "x"));
2181 p.y = parseTwip(lu(args, "y"));
2182 pos = mem_put(&mpoints, &p, sizeof(p));
2183 string_set(&s1, name);
2185 dictionary_put(&points, s1, (void*)pos);
2188 static int c_play(map_t*args)
2190 char*name = lu(args, "name");
2191 char*loop = lu(args, "loop");
2192 char*nomultiple = lu(args, "nomultiple");
2194 if(!strcmp(nomultiple, "nomultiple"))
2197 nm = parseInt(nomultiple);
2199 if(s_playsound(name, parseInt(loop), nm, 0)) {
2201 } else if(s_swf3action(name, "play")) {
2207 static int c_stop(map_t*args)
2209 char*name = map_lookup(args, "name");
2211 if(s_playsound(name, 0,0,1)) {
2213 } else if(s_swf3action(name, "stop")) {
2216 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2220 static int c_nextframe(map_t*args)
2222 char*name = lu(args, "name");
2224 if(s_swf3action(name, "nextframe")) {
2227 syntaxerror("I don't know anything about movie \"%s\"", name);
2231 static int c_previousframe(map_t*args)
2233 char*name = lu(args, "name");
2235 if(s_swf3action(name, "previousframe")) {
2238 syntaxerror("I don't know anything about movie \"%s\"", name);
2242 static int c_placement(map_t*args, int type)
2244 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2247 char* luminancestr = lu(args, "luminance");
2248 char* scalestr = lu(args, "scale");
2249 char* scalexstr = lu(args, "scalex");
2250 char* scaleystr = lu(args, "scaley");
2251 char* rotatestr = lu(args, "rotate");
2252 char* shearstr = lu(args, "shear");
2253 char* xstr="", *pivotstr="";
2254 char* ystr="", *anglestr="";
2255 char*above = lu(args, "above"); /*FIXME*/
2256 char*below = lu(args, "below");
2257 char* rstr = lu(args, "red");
2258 char* gstr = lu(args, "green");
2259 char* bstr = lu(args, "blue");
2260 char* astr = lu(args, "alpha");
2261 char* pinstr = lu(args, "pin");
2262 char* as = map_lookup(args, "as");
2270 if(type==9) { // (?) .rotate or .arcchange
2271 pivotstr = lu(args, "pivot");
2272 anglestr = lu(args, "angle");
2274 xstr = lu(args, "x");
2275 ystr = lu(args, "y");
2278 luminance = parseMulAdd(luminancestr);
2281 luminance.mul = 256;
2285 if(scalexstr[0]||scaleystr[0])
2286 syntaxerror("scalex/scaley and scale cannot both be set");
2287 scalexstr = scaleystr = scalestr;
2290 if(type == 0 || type == 4) {
2292 character = lu(args, "character");
2293 parameters_clear(&p);
2294 } else if (type == 5) {
2295 character = lu(args, "name");
2296 parameters_clear(&p);
2299 p = s_getParameters(instance);
2304 if(isRelative(xstr)) {
2305 if(type == 0 || type == 4)
2306 syntaxerror("relative x values not allowed for initial put or startclip");
2307 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2309 p.x = parseTwip(xstr);
2313 if(isRelative(ystr)) {
2314 if(type == 0 || type == 4)
2315 syntaxerror("relative y values not allowed for initial put or startclip");
2316 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2318 p.y = parseTwip(ystr);
2322 /* scale, scalex, scaley */
2324 oldbbox = s_getCharBBox(character);
2326 oldbbox = s_getInstanceBBox(instance);
2328 oldwidth = oldbbox.xmax - oldbbox.xmin;
2329 oldheight = oldbbox.ymax - oldbbox.ymin;
2331 if(oldwidth==0) p.scalex = 1.0;
2334 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2338 if(oldheight==0) p.scaley = 1.0;
2341 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2347 if(isRelative(rotatestr)) {
2348 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2350 p.rotate = parseFloat(rotatestr);
2356 if(isRelative(shearstr)) {
2357 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2359 p.shear = parseFloat(shearstr);
2364 if(isPoint(pivotstr))
2365 p.pivot = parsePoint(pivotstr);
2367 p.pivot = getPoint(oldbbox, pivotstr);
2371 p.pin = parsePoint(pinstr);
2373 p.pin = getPoint(oldbbox, pinstr);
2376 /* color transform */
2378 if(rstr[0] || luminancestr[0]) {
2381 r = parseMulAdd(rstr);
2383 r.add = p.cxform.r0;
2384 r.mul = p.cxform.r1;
2386 r = mergeMulAdd(r, luminance);
2387 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2389 if(gstr[0] || luminancestr[0]) {
2392 g = parseMulAdd(gstr);
2394 g.add = p.cxform.g0;
2395 g.mul = p.cxform.g1;
2397 g = mergeMulAdd(g, luminance);
2398 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2400 if(bstr[0] || luminancestr[0]) {
2403 b = parseMulAdd(bstr);
2405 b.add = p.cxform.b0;
2406 b.mul = p.cxform.b1;
2408 b = mergeMulAdd(b, luminance);
2409 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2412 MULADD a = parseMulAdd(astr);
2413 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2417 s_put(instance, character, p);
2419 s_change(instance, p);
2421 s_qchange(instance, p);
2423 s_jump(instance, p);
2425 s_startclip(instance, character, p);
2426 else if(type == 5) {
2428 s_buttonput(character, as, p);
2430 s_buttonput(character, "shape", p);
2435 static int c_put(map_t*args)
2437 c_placement(args, 0);
2440 static int c_change(map_t*args)
2442 c_placement(args, 1);
2445 static int c_qchange(map_t*args)
2447 c_placement(args, 2);
2450 static int c_arcchange(map_t*args)
2452 c_placement(args, 2);
2455 static int c_jump(map_t*args)
2457 c_placement(args, 3);
2460 static int c_startclip(map_t*args)
2462 c_placement(args, 4);
2465 static int c_show(map_t*args)
2467 c_placement(args, 5);
2470 static int c_del(map_t*args)
2472 char*instance = lu(args, "name");
2473 s_delinstance(instance);
2476 static int c_end(map_t*args)
2481 static int c_sprite(map_t*args)
2483 char* name = lu(args, "name");
2487 static int c_frame(map_t*args)
2489 char*framestr = lu(args, "n");
2490 char*cutstr = lu(args, "cut");
2491 char*name = lu(args, "name");
2494 if(strcmp(cutstr, "no"))
2496 if(isRelative(framestr)) {
2497 frame = s_getframe();
2498 if(getSign(framestr)<0)
2499 syntaxerror("relative frame expressions must be positive");
2500 frame += parseInt(getOffset(framestr));
2503 frame = parseInt(framestr);
2504 if(s_getframe() >= frame
2505 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2506 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2508 s_frame(frame, cut, name);
2511 static int c_primitive(map_t*args)
2513 char*name = lu(args, "name");
2514 char*command = lu(args, "commandname");
2515 int width=0, height=0, r=0;
2516 int linewidth = parseTwip(lu(args, "line"));
2517 char*colorstr = lu(args, "color");
2518 RGBA color = parseColor(colorstr);
2519 char*fillstr = lu(args, "fill");
2526 if(!strcmp(command, "circle"))
2528 else if(!strcmp(command, "filled"))
2532 width = parseTwip(lu(args, "width"));
2533 height = parseTwip(lu(args, "height"));
2534 } else if (type==1) {
2535 r = parseTwip(lu(args, "r"));
2536 } else if (type==2) {
2537 outline = lu(args, "outline");
2540 if(!strcmp(fillstr, "fill"))
2542 if(!strcmp(fillstr, "none"))
2544 if(width<0 || height<0 || linewidth<0 || r<0)
2545 syntaxerror("values width, height, line, r must be positive");
2547 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2548 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2549 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2553 static int c_textshape(map_t*args)
2555 char*name = lu(args, "name");
2556 char*text = lu(args, "text");
2557 char*font = lu(args, "font");
2558 float size = parsePxOrPercent(font, lu(args, "size"));
2560 s_textshape(name, font, size, text);
2564 static int c_swf(map_t*args)
2566 char*name = lu(args, "name");
2567 char*filename = lu(args, "filename");
2568 char*command = lu(args, "commandname");
2569 if(!strcmp(command, "shape"))
2570 warning("Please use .swf instead of .shape");
2571 s_includeswf(name, filename);
2575 static int c_font(map_t*args)
2577 char*name = lu(args, "name");
2578 char*filename = lu(args, "filename");
2579 s_font(name, filename);
2583 static int c_sound(map_t*args)
2585 char*name = lu(args, "name");
2586 char*filename = lu(args, "filename");
2587 s_sound(name, filename);
2591 static int c_text(map_t*args)
2593 char*name = lu(args, "name");
2594 char*text = lu(args, "text");
2595 char*font = lu(args, "font");
2596 float size = parsePxOrPercent(font, lu(args, "size"));
2597 RGBA color = parseColor(lu(args, "color"));
2598 s_text(name, font, text, (int)(size*100), color);
2602 static int c_soundtrack(map_t*args)
2607 static int c_quicktime(map_t*args)
2609 char*name = lu(args, "name");
2610 char*url = lu(args, "url");
2611 s_quicktime(name, url);
2615 static int c_image(map_t*args)
2617 char*command = lu(args, "commandname");
2618 char*name = lu(args, "name");
2619 char*filename = lu(args, "filename");
2620 if(!strcmp(command,"jpeg")) {
2621 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2622 s_image(name, "jpeg", filename, quality);
2624 s_image(name, "png", filename, 0);
2629 static int c_outline(map_t*args)
2631 char*name = lu(args, "name");
2632 char*format = lu(args, "format");
2636 syntaxerror("colon (:) expected");
2638 s_outline(name, format, text);
2642 int fakechar(map_t*args)
2644 char*name = lu(args, "name");
2645 s_box(name, 0, 0, black, 20, 0);
2649 static int c_egon(map_t*args) {return fakechar(args);}
2650 static int c_button(map_t*args) {
2651 char*name = lu(args, "name");
2655 static int current_button_flags = 0;
2656 static int c_on_press(map_t*args)
2658 char*position = lu(args, "position");
2660 if(!strcmp(position, "inside")) {
2661 current_button_flags |= BC_OVERUP_OVERDOWN;
2662 } else if(!strcmp(position, "outside")) {
2663 //current_button_flags |= BC_IDLE_OUTDOWN;
2664 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2665 } else if(!strcmp(position, "anywhere")) {
2666 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2669 if(type == RAWDATA) {
2671 s_buttonaction(current_button_flags, action);
2672 current_button_flags = 0;
2678 static int c_on_release(map_t*args)
2680 char*position = lu(args, "position");
2682 if(!strcmp(position, "inside")) {
2683 current_button_flags |= BC_OVERDOWN_OVERUP;
2684 } else if(!strcmp(position, "outside")) {
2685 current_button_flags |= BC_OUTDOWN_IDLE;
2686 } else if(!strcmp(position, "anywhere")) {
2687 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2690 if(type == RAWDATA) {
2692 s_buttonaction(current_button_flags, action);
2693 current_button_flags = 0;
2699 static int c_on_move_in(map_t*args)
2701 char*position = lu(args, "state");
2703 if(!strcmp(position, "pressed")) {
2704 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2705 } else if(!strcmp(position, "not_pressed")) {
2706 current_button_flags |= BC_IDLE_OVERUP;
2707 } else if(!strcmp(position, "any")) {
2708 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2711 if(type == RAWDATA) {
2713 s_buttonaction(current_button_flags, action);
2714 current_button_flags = 0;
2720 static int c_on_move_out(map_t*args)
2722 char*position = lu(args, "state");
2724 if(!strcmp(position, "pressed")) {
2725 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2726 } else if(!strcmp(position, "not_pressed")) {
2727 current_button_flags |= BC_OVERUP_IDLE;
2728 } else if(!strcmp(position, "any")) {
2729 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2732 if(type == RAWDATA) {
2734 s_buttonaction(current_button_flags, action);
2735 current_button_flags = 0;
2741 static int c_on_key(map_t*args)
2743 char*key = lu(args, "key");
2745 if(strlen(key)==1) {
2748 current_button_flags |= 0x4000 + (key[0]*0x200);
2750 syntaxerror("invalid character: %c"+key[0]);
2755 <ctrl-x> = 0x200*(x-'a')
2759 syntaxerror("invalid key: %s",key);
2762 if(type == RAWDATA) {
2764 s_buttonaction(current_button_flags, action);
2765 current_button_flags = 0;
2772 static int c_edittext(map_t*args)
2774 //"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"},
2775 char*name = lu(args, "name");
2776 char*font = lu(args, "font");
2777 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2778 int width = parseTwip(lu(args, "width"));
2779 int height = parseTwip(lu(args, "height"));
2780 char*text = lu(args, "text");
2781 RGBA color = parseColor(lu(args, "color"));
2782 int maxlength = parseInt(lu(args, "maxlength"));
2783 char*variable = lu(args, "variable");
2784 char*passwordstr = lu(args, "password");
2785 char*wordwrapstr = lu(args, "wordwrap");
2786 char*multilinestr = lu(args, "multiline");
2787 char*htmlstr = lu(args, "html");
2788 char*noselectstr = lu(args, "noselect");
2789 char*readonlystr = lu(args, "readonly");
2790 char*borderstr = lu(args, "border");
2791 char*autosizestr = lu(args, "autosize");
2792 char*alignstr = lu(args, "align");
2796 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2797 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2798 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2799 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2800 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2801 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2802 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2803 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2804 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2805 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2806 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2807 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2808 else syntaxerror("Unknown alignment: %s", alignstr);
2810 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2814 static int c_morphshape(map_t*args) {return fakechar(args);}
2815 static int c_movie(map_t*args) {return fakechar(args);}
2817 static char* readfile(const char*filename)
2819 FILE*fi = fopen(filename, "rb");
2823 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2824 fseek(fi, 0, SEEK_END);
2826 fseek(fi, 0, SEEK_SET);
2827 text = rfx_alloc(l+1);
2828 fread(text, l, 1, fi);
2834 static int c_action(map_t*args)
2836 char* filename = map_lookup(args, "filename");
2837 if(!filename ||!*filename) {
2839 if(type != RAWDATA) {
2840 syntaxerror("colon (:) expected");
2844 s_action(readfile(filename));
2850 static int c_initaction(map_t*args)
2852 char* character = lu(args, "name");
2853 char* filename = map_lookup(args, "filename");
2854 if(!filename ||!*filename) {
2856 if(type != RAWDATA) {
2857 syntaxerror("colon (:) expected");
2859 s_initaction(character, text);
2861 s_initaction(character, readfile(filename));
2869 command_func_t* func;
2872 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2873 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2874 // "import" type stuff
2875 {"swf", c_swf, "name filename"},
2876 {"shape", c_swf, "name filename"},
2877 {"jpeg", c_image, "name filename quality=80%"},
2878 {"png", c_image, "name filename"},
2879 {"movie", c_movie, "name filename"},
2880 {"sound", c_sound, "name filename"},
2881 {"font", c_font, "name filename"},
2882 {"soundtrack", c_soundtrack, "filename"},
2883 {"quicktime", c_quicktime, "url"},
2885 // generators of primitives
2887 {"point", c_point, "name x=0 y=0"},
2888 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2889 {"outline", c_outline, "name format=simple"},
2890 {"textshape", c_textshape, "name font size=100% text"},
2892 // character generators
2893 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2894 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2895 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2897 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2898 {"text", c_text, "name text font size=100% color=white"},
2899 {"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="},
2900 {"morphshape", c_morphshape, "name start end"},
2901 {"button", c_button, "name"},
2902 {"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="},
2903 {"on_press", c_on_press, "position=inside"},
2904 {"on_release", c_on_release, "position=anywhere"},
2905 {"on_move_in", c_on_move_in, "state=not_pressed"},
2906 {"on_move_out", c_on_move_out, "state=not_pressed"},
2907 {"on_key", c_on_key, "key=any"},
2910 {"play", c_play, "name loop=0 @nomultiple=0"},
2911 {"stop", c_stop, "name= "},
2912 {"nextframe", c_nextframe, "name"},
2913 {"previousframe", c_previousframe, "name"},
2915 // object placement tags
2916 {"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="},
2917 {"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="},
2918 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2919 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2920 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2921 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2922 {"del", c_del, "name"},
2923 // virtual object placement
2924 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2926 // commands which start a block
2927 //startclip (see above)
2928 {"sprite", c_sprite, "name"},
2929 {"action", c_action, "filename="},
2930 {"initaction", c_initaction, "name filename="},
2936 static map_t parseArguments(char*command, char*pattern)
2952 string_set(&t1, "commandname");
2953 string_set(&t2, command);
2954 map_put(&result, t1, t2);
2956 if(!pattern || !*pattern)
2963 if(!strncmp("<i> ", x, 3)) {
2965 if(type == COMMAND || type == RAWDATA) {
2967 syntaxerror("character name expected");
2969 name[pos].str = "instance";
2971 value[pos].str = text;
2972 value[pos].len = strlen(text);
2976 if(type == ASSIGNMENT)
2979 name[pos].str = "character";
2981 value[pos].str = text;
2982 value[pos].len = strlen(text);
2990 isboolean[pos] = (x[0] =='@');
3003 name[pos].len = d-x;
3008 name[pos].len = e-x;
3009 value[pos].str = e+1;
3010 value[pos].len = d-e-1;
3018 /* for(t=0;t<len;t++) {
3019 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3020 isboolean[t]?"(boolean)":"");
3025 if(type == RAWDATA || type == COMMAND) {
3030 // first, search for boolean arguments
3031 for(pos=0;pos<len;pos++)
3033 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3035 if(type == ASSIGNMENT)
3037 value[pos].str = text;
3038 value[pos].len = strlen(text);
3039 /*printf("setting boolean parameter %s (to %s)\n",
3040 strdup_n(name[pos], namelen[pos]),
3041 strdup_n(value[pos], valuelen[pos]));*/
3046 // second, search for normal arguments
3048 for(pos=0;pos<len;pos++)
3050 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3051 (type != ASSIGNMENT && !set[pos])) {
3053 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3055 if(type == ASSIGNMENT)
3058 value[pos].str = text;
3059 value[pos].len = strlen(text);
3061 printf("setting parameter %s (to %s)\n",
3062 strdup_n(name[pos].str, name[pos].len),
3063 strdup_n(value[pos].str, value[pos].len));
3069 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3073 for(t=0;t<len;t++) {
3074 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3077 for(t=0;t<len;t++) {
3078 if(value[t].str && value[t].str[0] == '*') {
3079 //relative default- take value from some other parameter
3081 for(s=0;s<len;s++) {
3082 if(value[s].len == value[t].len-1 &&
3083 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3084 value[t].str = value[s].str;
3087 if(value[t].str == 0) {
3089 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3093 /* ok, now construct the dictionary from the parameters */
3097 map_put(&result, name[t], value[t]);
3101 static void parseArgumentsForCommand(char*command)
3106 msg("<verbose> parse Command: %s (line %d)", command, line);
3108 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3109 if(!strcmp(arguments[t].command, command)) {
3111 /* ugly hack- will be removed soon (once documentation and .sc generating
3112 utilities have been changed) */
3113 if(!strcmp(command, "swf") && !stackpos) {
3114 warning("Please use .flash instead of .swf- this will be mandatory soon");
3119 args = parseArguments(command, arguments[t].arguments);
3125 syntaxerror("command %s not known", command);
3127 // catch missing .flash directives at the beginning of a file
3128 if(strcmp(command, "flash") && !stackpos)
3130 syntaxerror("No movie defined- use .flash first");
3134 printf(".%s\n", command);fflush(stdout);
3135 map_dump(&args, stdout, "\t");fflush(stdout);
3138 (*arguments[nr].func)(&args);
3140 /*if(!strcmp(command, "button") ||
3141 !strcmp(command, "action")) {
3144 if(type == COMMAND) {
3145 if(!strcmp(text, "end"))
3159 int main (int argc,char ** argv)
3162 processargs(argc, argv);
3163 initLog(0,-1,0,0,-1,verbose);
3166 args_callback_usage(argv[0]);
3170 file = generateTokens(filename);
3172 printf("parser returned error.\n");
3178 while(!noMoreTokens()) {
3181 syntaxerror("command expected");
3182 parseArgumentsForCommand(text);