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"
40 static char * filename = 0;
41 static char * outputname = "output.swf";
42 static int verbose = 2;
43 static int optimize = 0;
44 static int override_outputname = 0;
46 static struct options_t options[] = {
55 int args_callback_option(char*name,char*val)
57 if(!strcmp(name, "V")) {
58 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
61 else if(!strcmp(name, "o")) {
63 override_outputname = 1;
66 else if(!strcmp(name, "O")) {
70 else if(!strcmp(name, "v")) {
75 printf("Unknown option: -%s\n", name);
80 int args_callback_longoption(char*name,char*val)
82 return args_long2shortoption(options, name, val);
84 void args_callback_usage(char *name)
87 printf("Usage: %s [-o file.swf] file.sc\n", name);
89 printf("-h , --help Print short help message and exit\n");
90 printf("-V , --version Print version info and exit\n");
91 printf("-v , --verbose Increase verbosity. \n");
92 printf("-o , --output <filename> Set output file to <filename>.\n");
95 int args_callback_command(char*name,char*val)
98 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
105 static struct token_t* file;
114 static void syntaxerror(char*format, ...)
118 va_start(arglist, format);
119 vsprintf(buf, format, arglist);
121 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
125 static void warning(char*format, ...)
129 va_start(arglist, format);
130 vsprintf(buf, format, arglist);
132 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
135 static void readToken()
137 type = file[pos].type;
139 syntaxerror("unexpected end of file");
141 text = file[pos].text;
142 textlen = strlen(text);
143 line = file[pos].line;
144 column = file[pos].column;
146 //printf("---> %d(%s) %s\n", type, type_names[type], text);
149 static void pushBack()
152 if(!pos) syntaxerror("internal error 3");
157 textlen = strlen(text);
160 column = file[p].column;
163 static int noMoreTokens()
165 if(file[pos].type == END)
170 // ------------------------------ swf routines ----------------------------
174 int type; //0=swf, 1=sprite, 2=clip, 3=button
180 /* for sprites (1): */
186 dictionary_t oldinstances;
191 static int stackpos = 0;
193 static dictionary_t characters;
194 static dictionary_t images;
195 static dictionary_t textures;
196 static dictionary_t outlines;
197 static dictionary_t gradients;
198 static char idmap[65536];
199 static TAG*tag = 0; //current tag
201 static int id; //current character id
202 static int currentframe; //current frame in current level
203 static SRECT currentrect; //current bounding box in current level
204 static U16 currentdepth;
205 static dictionary_t instances;
206 static dictionary_t fonts;
207 static dictionary_t sounds;
209 typedef struct _parameters {
211 float scalex, scaley;
219 typedef struct _character {
225 typedef struct _instance {
226 character_t*character;
228 parameters_t parameters;
229 TAG* lastTag; //last tag which set the object
230 U16 lastFrame; //frame lastTag is in
233 typedef struct _outline {
238 typedef struct _gradient {
244 typedef struct _texture {
248 static void character_init(character_t*c)
250 memset(c, 0, sizeof(character_t));
252 static character_t* character_new()
255 c = (character_t*)malloc(sizeof(character_t));
259 static void instance_init(instance_t*i)
261 memset(i, 0, sizeof(instance_t));
263 static instance_t* instance_new()
266 c = (instance_t*)malloc(sizeof(instance_t));
271 static void incrementid()
275 syntaxerror("Out of character ids.");
280 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
282 character_t* c = character_new();
284 c->definingTag = ctag;
287 if(dictionary_lookup(&characters, name))
288 syntaxerror("character %s defined twice", name);
289 dictionary_put2(&characters, name, c);
291 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
293 swf_SetString(tag, name);
294 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
297 swf_SetString(tag, name);
299 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
301 character_t* c = character_new();
302 c->definingTag = ctag;
306 if(dictionary_lookup(&images, name))
307 syntaxerror("image %s defined twice", name);
308 dictionary_put2(&images, name, c);
310 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
312 instance_t* i = instance_new();
315 //swf_GetMatrix(0, &i->matrix);
316 if(dictionary_lookup(&instances, name))
317 syntaxerror("object %s defined twice", name);
318 dictionary_put2(&instances, name, i);
322 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)
325 p->scalex = scalex; p->scaley = scaley;
326 p->pin = pin; p->pivot = pivot;
327 p->rotate = rotate; p->cxform = cxform;
331 static void parameters_clear(parameters_t*p)
334 p->scalex = 1.0; p->scaley = 1.0;
337 p->pivot.x = 0; p->pivot.y = 0;
340 swf_GetCXForm(0, &p->cxform, 1);
343 static void makeMatrix(MATRIX*m, parameters_t*p)
352 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
353 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
354 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
355 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
357 m->sx = (int)(sx*65536+0.5);
358 m->r1 = (int)(r1*65536+0.5);
359 m->r0 = (int)(r0*65536+0.5);
360 m->sy = (int)(sy*65536+0.5);
364 h = swf_TurnPoint(p->pin, m);
369 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
374 r = swf_TurnRect(rect, &m);
375 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
376 currentrect.xmax == 0 && currentrect.ymax == 0)
379 swf_ExpandRect2(¤trect, &r);
383 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
385 SWF*swf = (SWF*)malloc(sizeof(SWF));
388 syntaxerror(".swf blocks can't be nested");
390 memset(swf, 0, sizeof(swf));
391 swf->fileVersion = version;
393 swf->frameRate = fps;
394 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
395 swf->compressed = compress;
396 swf_SetRGB(tag,&background);
398 if(stackpos==sizeof(stack)/sizeof(stack[0]))
399 syntaxerror("too many levels of recursion");
401 dictionary_init(&characters);
402 dictionary_init(&images);
403 dictionary_init(&textures);
404 dictionary_init(&outlines);
405 dictionary_init(&gradients);
406 dictionary_init(&instances);
407 dictionary_init(&fonts);
408 dictionary_init(&sounds);
410 memset(&stack[stackpos], 0, sizeof(stack[0]));
411 stack[stackpos].type = 0;
412 stack[stackpos].filename = strdup(name);
413 stack[stackpos].swf = swf;
414 stack[stackpos].oldframe = -1;
419 memset(¤trect, 0, sizeof(currentrect));
422 memset(idmap, 0, sizeof(idmap));
426 void s_sprite(char*name)
428 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
429 swf_SetU16(tag, id); //id
430 swf_SetU16(tag, 0); //frames
432 memset(&stack[stackpos], 0, sizeof(stack[0]));
433 stack[stackpos].type = 1;
434 stack[stackpos].oldframe = currentframe;
435 stack[stackpos].olddepth = currentdepth;
436 stack[stackpos].oldrect = currentrect;
437 stack[stackpos].oldinstances = instances;
438 stack[stackpos].tag = tag;
439 stack[stackpos].id = id;
440 stack[stackpos].name = strdup(name);
442 /* FIXME: those four fields should be bundled together */
443 dictionary_init(&instances);
446 memset(¤trect, 0, sizeof(currentrect));
452 typedef struct _buttonrecord
460 typedef struct _button
464 buttonrecord_t records[4];
467 static button_t mybutton;
469 void s_button(char*name)
471 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
472 swf_SetU16(tag, id); //id
473 swf_ButtonSetFlags(tag, 0); //menu=no
475 memset(&mybutton, 0, sizeof(mybutton));
477 memset(&stack[stackpos], 0, sizeof(stack[0]));
478 stack[stackpos].type = 3;
479 stack[stackpos].tag = tag;
480 stack[stackpos].id = id;
481 stack[stackpos].name = strdup(name);
482 stack[stackpos].oldrect = currentrect;
483 memset(¤trect, 0, sizeof(currentrect));
488 void s_buttonput(char*character, char*as, parameters_t p)
490 character_t* c = dictionary_lookup(&characters, character);
495 if(!stackpos || (stack[stackpos-1].type != 3)) {
496 syntaxerror(".show may only appear in .button");
499 syntaxerror("character %s not known (in .shape %s)", character, character);
501 if(mybutton.endofshapes) {
502 syntaxerror("a .do may not precede a .show", character, character);
505 m = s_instancepos(c->size, &p);
513 if(*s==',' || *s==0) {
514 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
515 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
516 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
517 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
518 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
519 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
526 static void setbuttonrecords(TAG*tag)
528 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
529 if(!mybutton.endofshapes) {
532 if(!mybutton.records[3].set) {
533 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
537 if(mybutton.records[t].set) {
538 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
541 swf_SetU8(tag,0); // end of button records
542 mybutton.endofshapes = 1;
546 void s_buttonaction(int flags, char*action)
552 setbuttonrecords(stack[stackpos-1].tag);
554 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
556 syntaxerror("Couldn't compile ActionScript");
559 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
560 swf_ActionSet(stack[stackpos-1].tag, a);
561 mybutton.nr_actions++;
566 static void setactionend(TAG*tag)
568 if(!mybutton.nr_actions) {
569 /* no actions means we didn't have an actionoffset,
570 which means we can't signal the end of the
571 buttonaction records, so, *sigh*, we have
572 to insert a dummy record */
573 swf_SetU16(tag, 0); //offset
574 swf_SetU16(tag, 0); //condition
575 swf_SetU8(tag, 0); //action
579 static void s_endButton()
582 setbuttonrecords(stack[stackpos-1].tag);
583 setactionend(stack[stackpos-1].tag);
586 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
590 tag = stack[stackpos].tag;
591 currentrect = stack[stackpos].oldrect;
593 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
594 free(stack[stackpos].name);
597 TAG* removeFromTo(TAG*from, TAG*to)
599 TAG*save = from->prev;
601 TAG*next = from->next;
609 static void s_endSprite()
611 SRECT r = currentrect;
613 if(stack[stackpos].cut)
614 tag = removeFromTo(stack[stackpos].cut, tag);
618 /* TODO: before clearing, prepend "<spritename>." to names and
619 copy into old instances dict */
620 dictionary_clear(&instances);
622 currentframe = stack[stackpos].oldframe;
623 currentrect = stack[stackpos].oldrect;
624 currentdepth = stack[stackpos].olddepth;
625 instances = stack[stackpos].oldinstances;
627 tag = swf_InsertTag(tag, ST_SHOWFRAME);
628 tag = swf_InsertTag(tag, ST_END);
630 tag = stack[stackpos].tag;
633 syntaxerror("internal error(7)");
635 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
636 free(stack[stackpos].name);
639 static void s_endSWF()
645 if(stack[stackpos].cut)
646 tag = removeFromTo(stack[stackpos].cut, tag);
650 swf = stack[stackpos].swf;
651 filename = stack[stackpos].filename;
653 //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
654 // tag = swf_InsertTag(tag, ST_SHOWFRAME);
655 tag = swf_InsertTag(tag, ST_SHOWFRAME);
657 tag = swf_InsertTag(tag, ST_END);
659 swf_OptimizeTagOrder(swf);
665 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
666 swf->movieSize = currentrect; /* "autocrop" */
669 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
670 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
671 swf->movieSize.ymax += 20;
672 warning("Empty bounding box for movie");
675 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
677 syntaxerror("couldn't create output file %s", filename);
680 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
682 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
686 dictionary_clear(&instances);
687 dictionary_clear(&characters);
688 dictionary_clear(&images);
689 dictionary_clear(&textures);
690 dictionary_clear(&outlines);
691 dictionary_clear(&gradients);
692 dictionary_clear(&fonts);
693 dictionary_clear(&sounds);
703 if(stack[stackpos-1].type == 0)
704 syntaxerror("End of file encountered in .flash block");
705 if(stack[stackpos-1].type == 1)
706 syntaxerror("End of file encountered in .sprite block");
707 if(stack[stackpos-1].type == 2)
708 syntaxerror("End of file encountered in .clip block");
714 return currentframe+1;
717 void s_frame(int nr, int cut, char*name)
723 syntaxerror("Illegal frame number");
724 nr--; // internally, frame 1 is frame 0
726 for(t=currentframe;t<nr;t++) {
727 tag = swf_InsertTag(tag, ST_SHOWFRAME);
728 if(t==nr-1 && name && *name) {
729 tag = swf_InsertTag(tag, ST_FRAMELABEL);
730 swf_SetString(tag, name);
733 if(nr == 0 && currentframe == 0 && name) {
734 tag = swf_InsertTag(tag, ST_FRAMELABEL);
735 swf_SetString(tag, name);
740 syntaxerror("Can't cut, frame empty");
742 stack[stackpos].cut = tag;
748 int parseColor2(char*str, RGBA*color);
750 int addFillStyle(SHAPE*s, SRECT*r, char*name)
757 parseColor2(name, &color);
758 return swf_ShapeAddSolidFillStyle(s, &color);
759 } else if ((texture = dictionary_lookup(&textures, name))) {
760 return swf_ShapeAddFillStyle2(s, &texture->fs);
761 } else if((image = dictionary_lookup(&images, name))) {
763 swf_GetMatrix(0, &m);
764 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
765 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
768 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
769 } else if ((gradient = dictionary_lookup(&gradients, name))) {
773 swf_GetMatrix(0, &rot);
774 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
775 csin = sin(-gradient->rotate*2*3.14159265358979/360);
777 rot.r1 = -csin*65536;
780 r2 = swf_TurnRect(*r, &rot);
781 swf_GetMatrix(0, &m);
782 m.sx = (r2.xmax - r2.xmin)*2*ccos;
783 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
784 m.r0 = (r2.ymax - r2.ymin)*2*csin;
785 m.sy = (r2.ymax - r2.ymin)*2*ccos;
786 m.tx = r->xmin + (r->xmax - r->xmin)/2;
787 m.ty = r->ymin + (r->ymax - r->ymin)/2;
788 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
789 } else if (parseColor2(name, &color)) {
790 return swf_ShapeAddSolidFillStyle(s, &color);
792 syntaxerror("not a color/fillstyle: %s", name);
797 RGBA black={r:0,g:0,b:0,a:0};
798 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
807 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
810 ls1 = swf_ShapeAddLineStyle(s,linewidth>=20?linewidth-20:0,&color);
812 fs1 = addFillStyle(s, &r2, texture);
815 r.xmin = r2.xmin-linewidth-linewidth/2;
816 r.ymin = r2.ymin-linewidth-linewidth/2;
817 r.xmax = r2.xmax+linewidth+linewidth/2;
818 r.ymax = r2.ymax+linewidth+linewidth/2;
820 swf_SetShapeHeader(tag,s);
821 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
822 swf_ShapeSetLine(tag,s,width,0);
823 swf_ShapeSetLine(tag,s,0,height);
824 swf_ShapeSetLine(tag,s,-width,0);
825 swf_ShapeSetLine(tag,s,0,-height);
826 swf_ShapeSetEnd(tag);
829 s_addcharacter(name, id, tag, r);
833 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
839 outline = dictionary_lookup(&outlines, outlinename);
841 syntaxerror("outline %s not defined", outlinename);
845 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
848 ls1 = swf_ShapeAddLineStyle(s,linewidth>=20?linewidth-20:0,&color);
850 fs1 = addFillStyle(s, &r2, texture);
853 rect.xmin = r2.xmin-linewidth-linewidth/2;
854 rect.ymin = r2.ymin-linewidth-linewidth/2;
855 rect.xmax = r2.xmax+linewidth+linewidth/2;
856 rect.ymax = r2.ymax+linewidth+linewidth/2;
858 swf_SetRect(tag,&rect);
859 swf_SetShapeStyles(tag, s);
860 swf_ShapeCountBits(s,0,0);
861 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, 1, 1,
862 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
863 swf_SetShapeBits(tag, s);
864 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
867 s_addcharacter(name, id, tag, rect);
871 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
876 r2.xmin = r2.ymin = 0;
880 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
883 ls1 = swf_ShapeAddLineStyle(s,linewidth>=20?linewidth-20:0,&color);
885 fs1 = addFillStyle(s, &r2, texture);
887 rect.xmin = r2.xmin-linewidth-linewidth/2;
888 rect.ymin = r2.ymin-linewidth-linewidth/2;
889 rect.xmax = r2.xmax+linewidth+linewidth/2;
890 rect.ymax = r2.ymax+linewidth+linewidth/2;
892 swf_SetRect(tag,&rect);
893 swf_SetShapeHeader(tag,s);
894 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
895 swf_ShapeSetCircle(tag, s, r,r,r,r);
896 swf_ShapeSetEnd(tag);
899 s_addcharacter(name, id, tag, rect);
903 void s_textshape(char*name, char*fontname, float size, char*_text)
906 U8*text = (U8*)_text;
910 font = dictionary_lookup(&fonts, fontname);
912 syntaxerror("font \"%s\" not known!", fontname);
914 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
915 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
916 s_box(name, 0, 0, black, 20, 0);
919 g = font->ascii2glyph[text[0]];
921 outline = malloc(sizeof(outline_t));
922 memset(outline, 0, sizeof(outline_t));
923 outline->shape = font->glyph[g].shape;
924 outline->bbox = font->layout->bounds[g];
928 swf_Shape11DrawerInit(&draw, 0);
929 swf_DrawText(&draw, font, (int)(size*100), _text);
931 outline->shape = swf_ShapeDrawerToShape(&draw);
932 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
936 if(dictionary_lookup(&outlines, name))
937 syntaxerror("outline %s defined twice", name);
938 dictionary_put2(&outlines, name, outline);
941 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
946 font = dictionary_lookup(&fonts, fontname);
948 syntaxerror("font \"%s\" not known!", fontname);
950 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
952 if(!font->numchars) {
953 s_box(name, 0, 0, black, 20, 0);
956 r = swf_SetDefineText(tag, font, &color, text, size);
958 s_addcharacter(name, id, tag, r);
962 void s_quicktime(char*name, char*url)
967 memset(&r, 0, sizeof(r));
969 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
971 swf_SetString(tag, url);
973 s_addcharacter(name, id, tag, r);
977 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
980 EditTextLayout layout;
983 if(fontname && *fontname) {
984 flags |= ET_USEOUTLINES;
985 font = dictionary_lookup(&fonts, fontname);
987 syntaxerror("font \"%s\" not known!", fontname);
989 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
992 layout.leftmargin = 0;
993 layout.rightmargin = 0;
1001 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1003 s_addcharacter(name, id, tag, r);
1007 /* type: either "jpeg" or "png"
1009 void s_image(char*name, char*type, char*filename, int quality)
1011 /* an image is actually two folded: 1st bitmap, 2nd character.
1012 Both of them can be used separately */
1014 /* step 1: the bitmap */
1019 warning("image type \"png\" not supported yet!");
1020 s_box(name, 0, 0, black, 20, 0);
1024 #ifndef HAVE_LIBJPEG
1025 warning("no jpeg support compiled in");
1026 s_box(name, 0, 0, black, 20, 0);
1029 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1030 swf_SetU16(tag, imageID);
1032 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1033 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1036 swf_GetJPEGSize(filename, &width, &height);
1043 s_addimage(name, id, tag, r);
1048 /* step 2: the character */
1049 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1050 swf_SetU16(tag, id);
1051 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1053 s_addcharacter(name, id, tag, r);
1057 void s_getBitmapSize(char*name, int*width, int*height)
1059 character_t* image = dictionary_lookup(&images, name);
1060 gradient_t* gradient = dictionary_lookup(&gradients,name);
1062 *width = image->size.xmax;
1063 *height = image->size.ymax;
1067 /* internal SWF gradient size */
1068 if(gradient->radial) {
1077 syntaxerror("No such bitmap/gradient: %s", name);
1080 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1082 gradient_t* gradient = dictionary_lookup(&gradients, object);
1083 character_t* bitmap = dictionary_lookup(&images, object);
1084 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1086 FILLSTYLE*fs = &texture->fs;
1089 fs->type = FILL_TILED;
1090 fs->id_bitmap = bitmap->id;
1091 } else if(gradient) {
1092 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1093 fs->gradient = gradient->gradient;
1095 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1096 makeMatrix(&fs->m, &p);
1097 if(gradient && !gradient->radial) {
1104 p2 = swf_TurnPoint(p1, &m);
1109 if(dictionary_lookup(&textures, name))
1110 syntaxerror("texture %s defined twice", name);
1111 dictionary_put2(&textures, name, texture);
1114 void dumpSWF(SWF*swf)
1116 TAG* tag = swf->firstTag;
1117 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1119 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1122 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1125 void s_font(char*name, char*filename)
1128 font = swf_LoadFont(filename);
1131 warning("Couldn't open font file \"%s\"", filename);
1132 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1133 memset(font, 0, sizeof(SWFFONT));
1134 dictionary_put2(&fonts, name, font);
1140 /* fix the layout. Only needed for old fonts */
1142 for(t=0;t<font->numchars;t++) {
1143 font->glyph[t].advance = 0;
1146 swf_FontCreateLayout(font);
1148 /* just in case this thing is used in .edittext later on */
1149 swf_FontPrepareForEditText(font);
1152 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1153 swf_FontSetDefine2(tag, font);
1156 if(dictionary_lookup(&fonts, name))
1157 syntaxerror("font %s defined twice", name);
1158 dictionary_put2(&fonts, name, font);
1163 typedef struct _sound_t
1169 void s_sound(char*name, char*filename)
1171 struct WAV wav, wav2;
1177 if(!readWAV(filename, &wav)) {
1178 warning("Couldn't read wav file \"%s\"", filename);
1182 convertWAV2mono(&wav, &wav2, 44100);
1183 samples = (U16*)wav2.data;
1184 numsamples = wav2.size/2;
1186 #ifdef WORDS_BIGENDIAN
1188 for(t=0;t<numsamples;t++) {
1189 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1194 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1195 swf_SetU16(tag, id); //id
1196 swf_SetSoundDefine(tag, samples, numsamples);
1198 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1202 if(dictionary_lookup(&sounds, name))
1203 syntaxerror("sound %s defined twice", name);
1204 dictionary_put2(&sounds, name, sound);
1212 static char* gradient_getToken(const char**p)
1216 while(**p && strchr(" \t\n\r", **p)) {
1220 while(**p && !strchr(" \t\n\r", **p)) {
1223 result = malloc((*p)-start+1);
1224 memcpy(result,start,(*p)-start+1);
1225 result[(*p)-start] = 0;
1229 float parsePercent(char*str);
1230 RGBA parseColor(char*str);
1232 GRADIENT parseGradient(const char*str)
1236 const char* p = str;
1237 memset(&gradient, 0, sizeof(GRADIENT));
1239 char*posstr,*colorstr;
1242 posstr = gradient_getToken(&p);
1245 pos = (int)(parsePercent(posstr)*255.0);
1248 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1249 colorstr = gradient_getToken(&p);
1250 color = parseColor(colorstr);
1251 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1252 warning("gradient record too big- max size is 8, rest ignored");
1255 gradient.ratios[gradient.num] = pos;
1256 gradient.rgba[gradient.num] = color;
1265 void s_gradient(char*name, const char*text, int radial, int rotate)
1267 gradient_t* gradient;
1268 gradient = malloc(sizeof(gradient_t));
1269 memset(gradient, 0, sizeof(gradient_t));
1270 gradient->gradient = parseGradient(text);
1271 gradient->radial = radial;
1272 gradient->rotate = rotate;
1274 if(dictionary_lookup(&gradients, name))
1275 syntaxerror("gradient %s defined twice", name);
1276 dictionary_put2(&gradients, name, gradient);
1279 void s_action(const char*text)
1282 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1284 syntaxerror("Couldn't compile ActionScript");
1287 tag = swf_InsertTag(tag, ST_DOACTION);
1289 swf_ActionSet(tag, a);
1294 int s_swf3action(char*name, char*action)
1297 instance_t* object = 0;
1299 dictionary_lookup(&instances, name);
1300 if(!object && name && *name) {
1301 /* we have a name, but couldn't find it. Abort. */
1304 a = action_SetTarget(0, name);
1305 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1306 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1307 else if(!strcmp(action, "stop")) a = action_Stop(a);
1308 else if(!strcmp(action, "play")) a = action_Play(a);
1309 a = action_SetTarget(a, "");
1312 tag = swf_InsertTag(tag, ST_DOACTION);
1313 swf_ActionSet(tag, a);
1318 void s_outline(char*name, char*format, char*source)
1327 swf_Shape11DrawerInit(&draw, 0);
1328 draw_string(&draw, source);
1330 shape = swf_ShapeDrawerToShape(&draw);
1331 bounds = swf_ShapeDrawerGetBBox(&draw);
1332 draw.dealloc(&draw);
1334 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1335 outline->shape = shape;
1336 outline->bbox = bounds;
1338 if(dictionary_lookup(&outlines, name))
1339 syntaxerror("outline %s defined twice", name);
1340 dictionary_put2(&outlines, name, outline);
1343 int s_playsound(char*name, int loops, int nomultiple, int stop)
1349 sound = dictionary_lookup(&sounds, name);
1353 tag = swf_InsertTag(tag, ST_STARTSOUND);
1354 swf_SetU16(tag, sound->id); //id
1355 memset(&info, 0, sizeof(info));
1358 info.nomultiple = nomultiple;
1359 swf_SetSoundInfo(tag, &info);
1363 void s_includeswf(char*name, char*filename)
1371 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1372 f = open(filename,O_RDONLY|O_BINARY);
1374 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1375 s_box(name, 0, 0, black, 20, 0);
1378 if (swf_ReadSWF(f,&swf)<0) {
1379 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1380 s_box(name, 0, 0, black, 20, 0);
1385 /* FIXME: The following sets the bounding Box for the character.
1386 It is wrong for two reasons:
1387 a) It may be too small (in case objects in the movie clip at the borders)
1388 b) it may be too big (because the poor movie never got autocropped)
1392 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1393 swf_SetU16(tag, id);
1394 swf_SetU16(tag, swf.frameCount);
1396 swf_Relocate(&swf, idmap);
1398 ftag = swf.firstTag;
1402 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1403 if(cutout[t] == ftag->id) {
1407 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1409 if(ftag->id == ST_END)
1413 /* We simply dump all tags right after the sprite
1414 header, relying on the fact that swf_OptimizeTagOrder() will
1415 sort things out for us later.
1416 We also rely on the fact that the imported SWF is well-formed.
1418 tag = swf_InsertTag(tag, ftag->id);
1419 swf_SetBlock(tag, ftag->data, ftag->len);
1423 syntaxerror("Included file %s contains errors", filename);
1424 tag = swf_InsertTag(tag, ST_END);
1428 s_addcharacter(name, id, tag, r);
1431 SRECT s_getCharBBox(char*name)
1433 character_t* c = dictionary_lookup(&characters, name);
1434 if(!c) syntaxerror("character '%s' unknown(2)", name);
1437 SRECT s_getInstanceBBox(char*name)
1439 instance_t * i = dictionary_lookup(&instances, name);
1441 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1443 if(!c) syntaxerror("internal error(5)");
1446 parameters_t s_getParameters(char*name)
1448 instance_t * i = dictionary_lookup(&instances, name);
1449 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1450 return i->parameters;
1452 void s_startclip(char*instance, char*character, parameters_t p)
1454 character_t* c = dictionary_lookup(&characters, character);
1458 syntaxerror("character %s not known", character);
1460 i = s_addinstance(instance, c, currentdepth);
1462 m = s_instancepos(i->character->size, &p);
1464 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1465 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1466 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1468 i->lastFrame= currentframe;
1470 stack[stackpos].tag = tag;
1471 stack[stackpos].type = 2;
1480 swf_SetTagPos(stack[stackpos].tag, 0);
1481 swf_GetPlaceObject(stack[stackpos].tag, &p);
1482 p.clipdepth = currentdepth;
1484 swf_ClearTag(stack[stackpos].tag);
1485 swf_SetPlaceObject(stack[stackpos].tag, &p);
1489 void s_put(char*instance, char*character, parameters_t p)
1491 character_t* c = dictionary_lookup(&characters, character);
1495 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1498 i = s_addinstance(instance, c, currentdepth);
1500 m = s_instancepos(i->character->size, &p);
1502 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1503 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1505 i->lastFrame = currentframe;
1509 void s_jump(char*instance, parameters_t p)
1511 instance_t* i = dictionary_lookup(&instances, instance);
1514 syntaxerror("instance %s not known", instance);
1518 m = s_instancepos(i->character->size, &p);
1520 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1521 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1523 i->lastFrame = currentframe;
1526 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1530 if(num==0 || num==1)
1532 ratio = (float)pos/(float)num;
1534 p.x = (p2->x-p1->x)*ratio + p1->x;
1535 p.y = (p2->y-p1->y)*ratio + p1->y;
1536 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1537 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1538 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1539 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1541 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1542 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1543 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1544 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1546 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1547 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1548 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1549 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1551 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1552 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1553 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1554 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1558 void s_change(char*instance, parameters_t p2)
1560 instance_t* i = dictionary_lookup(&instances, instance);
1564 int frame, allframes;
1566 syntaxerror("instance %s not known", instance);
1570 allframes = currentframe - i->lastFrame - 1;
1572 warning(".change ignored. can only .put/.change an object once per frame.");
1576 m = s_instancepos(i->character->size, &p2);
1577 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1578 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1581 /* o.k., we got the start and end point set. Now iterate though all the
1582 tags in between, inserting object changes after each new frame */
1585 if(!t) syntaxerror("internal error(6)");
1587 while(frame < allframes) {
1588 if(t->id == ST_SHOWFRAME) {
1593 p = s_interpolate(&p1, &p2, frame, allframes);
1594 m = s_instancepos(i->character->size, &p); //needed?
1595 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1596 i->lastFrame = currentframe;
1597 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1599 if(frame == allframes)
1604 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1608 void s_delinstance(char*instance)
1610 instance_t* i = dictionary_lookup(&instances, instance);
1612 syntaxerror("instance %s not known", instance);
1614 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1615 swf_SetU16(tag, i->depth);
1616 dictionary_del(&instances, instance);
1619 void s_qchange(char*instance, parameters_t p)
1626 syntaxerror(".end unexpected");
1627 if(stack[stackpos-1].type == 0)
1629 else if(stack[stackpos-1].type == 1)
1631 else if(stack[stackpos-1].type == 2)
1633 else if(stack[stackpos-1].type == 3)
1635 else syntaxerror("internal error 1");
1638 // ------------------------------------------------------------------------
1640 typedef int command_func_t(map_t*args);
1642 SRECT parseBox(char*str)
1645 float xmin, xmax, ymin, ymax;
1646 char*x = strchr(str, 'x');
1648 if(!strcmp(str, "autocrop")) {
1649 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1653 d1 = strchr(x+1, ':');
1655 d2 = strchr(d1+1, ':');
1657 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1661 else if(d1 && !d2) {
1662 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1668 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1673 r.xmin = (SCOORD)(xmin*20);
1674 r.ymin = (SCOORD)(ymin*20);
1675 r.xmax = (SCOORD)(xmax*20);
1676 r.ymax = (SCOORD)(ymax*20);
1679 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1682 float parseFloat(char*str)
1686 int parseInt(char*str)
1691 if(str[0]=='+' || str[0]=='-')
1695 if(str[t]<'0' || str[t]>'9')
1696 syntaxerror("Not an Integer: \"%s\"", str);
1699 int parseTwip(char*str)
1703 if(str[0]=='+' || str[0]=='-') {
1708 dot = strchr(str, '.');
1712 return sign*parseInt(str)*20;
1714 int l=strlen(++dot);
1716 for(s=str;s<dot-1;s++)
1717 if(*s<'0' || *s>'9')
1718 syntaxerror("Not a coordinate: \"%s\"", str);
1720 if(*s<'0' || *s>'9')
1721 syntaxerror("Not a coordinate: \"%s\"", str);
1723 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1724 warning("precision loss: %s converted to twip: %s", str, dot);
1729 return sign*atoi(str)*20;
1731 return sign*atoi(str)*20+atoi(dot)*2;
1733 return sign*atoi(str)*20+atoi(dot)/5;
1738 int isPoint(char*str)
1740 if(strchr(str, '('))
1746 SPOINT parsePoint(char*str)
1750 int l = strlen(str);
1751 char*comma = strchr(str, ',');
1752 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1753 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1754 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1755 p.x = parseTwip(tmp);
1756 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1757 p.y = parseTwip(tmp);
1761 int parseColor2(char*str, RGBA*color)
1763 int l = strlen(str);
1767 struct {unsigned char r,g,b;char*name;} colors[] =
1768 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1769 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1770 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1771 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1772 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1773 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1774 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1775 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1776 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1777 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1778 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1779 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1783 if(str[0]=='#' && (l==7 || l==9)) {
1784 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1786 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1788 color->r = r; color->g = g; color->b = b; color->a = a;
1791 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1792 if(!strcmp(str, colors[t].name)) {
1797 color->r = r; color->g = g; color->b = b; color->a = a;
1803 RGBA parseColor(char*str)
1806 if(!parseColor2(str, &c))
1807 syntaxerror("Expression '%s' is not a color", str);
1811 typedef struct _muladd {
1816 MULADD parseMulAdd(char*str)
1819 char* str2 = (char*)malloc(strlen(str)+5);
1826 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1827 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1828 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1829 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1830 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1831 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1832 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1833 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1834 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1835 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1837 syntaxerror("'%s' is not a valid color transform expression", str);
1839 m.add = (int)(add*256);
1840 m.mul = (int)(mul*256);
1845 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1847 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1848 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1850 if(a<-32768) a=-32768;
1851 if(a>32767) a=32767;
1852 if(m<-32768) m=-32768;
1853 if(m>32767) m=32767;
1859 float parsePxOrPercent(char*fontname, char*str)
1861 int l = strlen(str);
1862 if(strchr(str, '%'))
1863 return parsePercent(str);
1864 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1865 float p = atof(str);
1866 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1868 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1872 float parsePercent(char*str)
1874 int l = strlen(str);
1878 return atoi(str)/100.0;
1880 syntaxerror("Expression '%s' is not a percentage", str);
1883 int isPercent(char*str)
1885 return str[strlen(str)-1]=='%';
1887 int parseNewSize(char*str, int size)
1890 return parsePercent(str)*size;
1892 return (int)(atof(str)*20);
1895 int isColor(char*str)
1898 return parseColor2(str, &c);
1901 static char* lu(map_t* args, char*name)
1903 char* value = map_lookup(args, name);
1905 map_dump(args, stdout, "");
1906 syntaxerror("internal error 2: value %s should be set", name);
1911 static int c_flash(map_t*args)
1913 char* filename = map_lookup(args, "filename");
1914 char* compressstr = lu(args, "compress");
1915 SRECT bbox = parseBox(lu(args, "bbox"));
1916 int version = parseInt(lu(args, "version"));
1917 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1919 RGBA color = parseColor(lu(args, "background"));
1921 if(!filename || !*filename) {
1922 /* for compatibility */
1923 filename = map_lookup(args, "name");
1924 if(!filename || !*filename) {
1927 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1928 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1932 if(!filename || override_outputname)
1933 filename = outputname;
1935 if(!strcmp(compressstr, "default"))
1936 compress = version==6;
1937 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1939 else if(!strcmp(compressstr, "no"))
1941 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1943 s_swf(filename, bbox, version, fps, compress, color);
1946 int isRelative(char*str)
1948 return !strncmp(str, "<plus>", 6) ||
1949 !strncmp(str, "<minus>", 7);
1951 char* getOffset(char*str)
1953 if(!strncmp(str, "<plus>", 6))
1955 if(!strncmp(str, "<minus>", 7))
1957 syntaxerror("internal error (347)");
1960 int getSign(char*str)
1962 if(!strncmp(str, "<plus>", 6))
1964 if(!strncmp(str, "<minus>", 7))
1966 syntaxerror("internal error (348)");
1969 static dictionary_t points;
1970 static mem_t mpoints;
1971 int points_initialized = 0;
1973 SPOINT getPoint(SRECT r, char*name)
1976 if(!strcmp(name, "center")) {
1978 p.x = (r.xmin + r.xmax)/2;
1979 p.y = (r.ymin + r.ymax)/2;
1983 if(points_initialized)
1984 l = (int)dictionary_lookup(&points, name);
1986 syntaxerror("Invalid point: \"%s\".", name);
1989 return *(SPOINT*)&mpoints.buffer[l];
1992 static int texture2(char*name, char*object, map_t*args, int errors)
1995 char*xstr = map_lookup(args, "x");
1996 char*ystr = map_lookup(args, "y");
1997 char*widthstr = map_lookup(args, "width");
1998 char*heightstr = map_lookup(args, "height");
1999 char*scalestr = map_lookup(args, "scale");
2000 char*scalexstr = map_lookup(args, "scalex");
2001 char*scaleystr = map_lookup(args, "scaley");
2002 char*rotatestr = map_lookup(args, "rotate");
2003 char* shearstr = map_lookup(args, "shear");
2004 char* radiusstr = map_lookup(args, "r");
2006 float scalex = 1.0, scaley = 1.0;
2007 float rotate=0, shear=0;
2009 if(!*xstr && !*ystr) {
2011 syntaxerror("x and y must be set");
2014 if(*scalestr && (*scalexstr || *scaleystr)) {
2015 syntaxerror("scale and scalex/scaley can't both be set");
2018 if((*widthstr || *heightstr) && *radiusstr) {
2019 syntaxerror("width/height and radius can't both be set");
2022 widthstr = radiusstr;
2023 heightstr = radiusstr;
2025 if(!*xstr) xstr="0";
2026 if(!*ystr) ystr="0";
2027 if(!*rotatestr) rotatestr="0";
2028 if(!*shearstr) shearstr="0";
2031 scalex = scaley = parsePercent(scalestr);
2032 } else if(*scalexstr || *scaleystr) {
2033 if(scalexstr) scalex = parsePercent(scalexstr);
2034 if(scaleystr) scaley = parsePercent(scaleystr);
2035 } else if(*widthstr || *heightstr) {
2038 s_getBitmapSize(object, &width, &height);
2040 scalex = (float)parseTwip(widthstr)/(float)width;
2042 scaley = (float)parseTwip(heightstr)/(float)height;
2044 x = parseTwip(xstr);
2045 y = parseTwip(ystr);
2046 rotate = parseFloat(rotatestr);
2047 shear = parseFloat(shearstr);
2049 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2054 static int c_texture(map_t*args)
2056 char*name = lu(args, "instance");
2057 char*object = lu(args, "character");
2058 return texture2(name, object, args, 1);
2061 static int c_gradient(map_t*args)
2063 char*name = lu(args, "name");
2064 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2065 int rotate = parseInt(lu(args, "rotate"));
2069 syntaxerror("colon (:) expected");
2071 s_gradient(name, text, radial, rotate);
2073 /* check whether we also have placement information,
2074 which would make this a positioned gradient.
2075 If there is placement information, texture2() will
2076 add a texture, which has priority over the gradient.
2078 texture2(name, name, args, 0);
2081 static int c_point(map_t*args)
2083 char*name = lu(args, "name");
2087 if(!points_initialized) {
2088 dictionary_init(&points);
2090 points_initialized = 1;
2092 p.x = parseTwip(lu(args, "x"));
2093 p.y = parseTwip(lu(args, "y"));
2094 pos = mem_put(&mpoints, &p, sizeof(p));
2095 string_set(&s1, name);
2097 dictionary_put(&points, s1, (void*)pos);
2100 static int c_play(map_t*args)
2102 char*name = lu(args, "name");
2103 char*loop = lu(args, "loop");
2104 char*nomultiple = lu(args, "nomultiple");
2106 if(!strcmp(nomultiple, "nomultiple"))
2109 nm = parseInt(nomultiple);
2111 if(s_playsound(name, parseInt(loop), nm, 0)) {
2113 } else if(s_swf3action(name, "play")) {
2119 static int c_stop(map_t*args)
2121 char*name = map_lookup(args, "name");
2123 if(s_playsound(name, 0,0,1)) {
2125 } else if(s_swf3action(name, "stop")) {
2128 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2132 static int c_nextframe(map_t*args)
2134 char*name = lu(args, "name");
2136 if(s_swf3action(name, "nextframe")) {
2139 syntaxerror("I don't know anything about movie \"%s\"", name);
2143 static int c_previousframe(map_t*args)
2145 char*name = lu(args, "name");
2147 if(s_swf3action(name, "previousframe")) {
2150 syntaxerror("I don't know anything about movie \"%s\"", name);
2154 static int c_placement(map_t*args, int type)
2156 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2159 char* luminancestr = lu(args, "luminance");
2160 char* scalestr = lu(args, "scale");
2161 char* scalexstr = lu(args, "scalex");
2162 char* scaleystr = lu(args, "scaley");
2163 char* rotatestr = lu(args, "rotate");
2164 char* shearstr = lu(args, "shear");
2165 char* xstr="", *pivotstr="";
2166 char* ystr="", *anglestr="";
2167 char*above = lu(args, "above"); /*FIXME*/
2168 char*below = lu(args, "below");
2169 char* rstr = lu(args, "red");
2170 char* gstr = lu(args, "green");
2171 char* bstr = lu(args, "blue");
2172 char* astr = lu(args, "alpha");
2173 char* pinstr = lu(args, "pin");
2174 char* as = map_lookup(args, "as");
2182 if(type==9) { // (?) .rotate or .arcchange
2183 pivotstr = lu(args, "pivot");
2184 anglestr = lu(args, "angle");
2186 xstr = lu(args, "x");
2187 ystr = lu(args, "y");
2190 luminance = parseMulAdd(luminancestr);
2193 luminance.mul = 256;
2197 if(scalexstr[0]||scaleystr[0])
2198 syntaxerror("scalex/scaley and scale cannot both be set");
2199 scalexstr = scaleystr = scalestr;
2202 if(type == 0 || type == 4) {
2204 character = lu(args, "character");
2205 parameters_clear(&p);
2206 } else if (type == 5) {
2207 character = lu(args, "name");
2208 parameters_clear(&p);
2211 p = s_getParameters(instance);
2216 if(isRelative(xstr)) {
2217 if(type == 0 || type == 4)
2218 syntaxerror("relative x values not allowed for initial put or startclip");
2219 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2221 p.x = parseTwip(xstr);
2225 if(isRelative(ystr)) {
2226 if(type == 0 || type == 4)
2227 syntaxerror("relative y values not allowed for initial put or startclip");
2228 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2230 p.y = parseTwip(ystr);
2234 /* scale, scalex, scaley */
2236 oldbbox = s_getCharBBox(character);
2238 oldbbox = s_getInstanceBBox(instance);
2240 oldwidth = oldbbox.xmax - oldbbox.xmin;
2241 oldheight = oldbbox.ymax - oldbbox.ymin;
2243 if(oldwidth==0) p.scalex = 1.0;
2246 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2250 if(oldheight==0) p.scaley = 1.0;
2253 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2259 if(isRelative(rotatestr)) {
2260 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2262 p.rotate = parseFloat(rotatestr);
2268 if(isRelative(shearstr)) {
2269 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2271 p.shear = parseFloat(shearstr);
2276 if(isPoint(pivotstr))
2277 p.pivot = parsePoint(pivotstr);
2279 p.pivot = getPoint(oldbbox, pivotstr);
2283 p.pin = parsePoint(pinstr);
2285 p.pin = getPoint(oldbbox, pinstr);
2288 /* color transform */
2290 if(rstr[0] || luminancestr[0]) {
2293 r = parseMulAdd(rstr);
2295 r.add = p.cxform.r0;
2296 r.mul = p.cxform.r1;
2298 r = mergeMulAdd(r, luminance);
2299 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2301 if(gstr[0] || luminancestr[0]) {
2304 g = parseMulAdd(gstr);
2306 g.add = p.cxform.g0;
2307 g.mul = p.cxform.g1;
2309 g = mergeMulAdd(g, luminance);
2310 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2312 if(bstr[0] || luminancestr[0]) {
2315 b = parseMulAdd(bstr);
2317 b.add = p.cxform.b0;
2318 b.mul = p.cxform.b1;
2320 b = mergeMulAdd(b, luminance);
2321 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2324 MULADD a = parseMulAdd(astr);
2325 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2329 s_put(instance, character, p);
2331 s_change(instance, p);
2333 s_qchange(instance, p);
2335 s_jump(instance, p);
2337 s_startclip(instance, character, p);
2338 else if(type == 5) {
2340 s_buttonput(character, as, p);
2342 s_buttonput(character, "shape", p);
2347 static int c_put(map_t*args)
2349 c_placement(args, 0);
2352 static int c_change(map_t*args)
2354 c_placement(args, 1);
2357 static int c_qchange(map_t*args)
2359 c_placement(args, 2);
2362 static int c_arcchange(map_t*args)
2364 c_placement(args, 2);
2367 static int c_jump(map_t*args)
2369 c_placement(args, 3);
2372 static int c_startclip(map_t*args)
2374 c_placement(args, 4);
2377 static int c_show(map_t*args)
2379 c_placement(args, 5);
2382 static int c_del(map_t*args)
2384 char*instance = lu(args, "name");
2385 s_delinstance(instance);
2388 static int c_end(map_t*args)
2393 static int c_sprite(map_t*args)
2395 char* name = lu(args, "name");
2399 static int c_frame(map_t*args)
2401 char*framestr = lu(args, "n");
2402 char*cutstr = lu(args, "cut");
2403 char*name = lu(args, "name");
2406 if(strcmp(cutstr, "no"))
2408 if(isRelative(framestr)) {
2409 frame = s_getframe();
2410 if(getSign(framestr)<0)
2411 syntaxerror("relative frame expressions must be positive");
2412 frame += parseInt(getOffset(framestr));
2415 frame = parseInt(framestr);
2416 if(s_getframe() >= frame
2417 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2418 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2420 s_frame(frame, cut, name);
2423 static int c_primitive(map_t*args)
2425 char*name = lu(args, "name");
2426 char*command = lu(args, "commandname");
2427 int width=0, height=0, r=0;
2428 int linewidth = parseTwip(lu(args, "line"));
2429 char*colorstr = lu(args, "color");
2430 RGBA color = parseColor(colorstr);
2431 char*fillstr = lu(args, "fill");
2438 if(!strcmp(command, "circle"))
2440 else if(!strcmp(command, "filled"))
2444 width = parseTwip(lu(args, "width"));
2445 height = parseTwip(lu(args, "height"));
2446 } else if (type==1) {
2447 r = parseTwip(lu(args, "r"));
2448 } else if (type==2) {
2449 outline = lu(args, "outline");
2452 if(!strcmp(fillstr, "fill"))
2454 if(!strcmp(fillstr, "none"))
2456 if(width<0 || height<0 || linewidth<0 || r<0)
2457 syntaxerror("values width, height, line, r must be positive");
2459 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2460 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2461 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2465 static int c_textshape(map_t*args)
2467 char*name = lu(args, "name");
2468 char*text = lu(args, "text");
2469 char*font = lu(args, "font");
2470 float size = parsePxOrPercent(font, lu(args, "size"));
2472 s_textshape(name, font, size, text);
2476 static int c_swf(map_t*args)
2478 char*name = lu(args, "name");
2479 char*filename = lu(args, "filename");
2480 char*command = lu(args, "commandname");
2481 if(!strcmp(command, "shape"))
2482 warning("Please use .swf instead of .shape");
2483 s_includeswf(name, filename);
2487 static int c_font(map_t*args)
2489 char*name = lu(args, "name");
2490 char*filename = lu(args, "filename");
2491 s_font(name, filename);
2495 static int c_sound(map_t*args)
2497 char*name = lu(args, "name");
2498 char*filename = lu(args, "filename");
2499 s_sound(name, filename);
2503 static int c_text(map_t*args)
2505 char*name = lu(args, "name");
2506 char*text = lu(args, "text");
2507 char*font = lu(args, "font");
2508 float size = parsePxOrPercent(font, lu(args, "size"));
2509 RGBA color = parseColor(lu(args, "color"));
2510 s_text(name, font, text, (int)(size*100), color);
2514 static int c_soundtrack(map_t*args)
2519 static int c_quicktime(map_t*args)
2521 char*name = lu(args, "name");
2522 char*url = lu(args, "url");
2523 s_quicktime(name, url);
2527 static int c_image(map_t*args)
2529 char*command = lu(args, "commandname");
2530 char*name = lu(args, "name");
2531 char*filename = lu(args, "filename");
2532 if(!strcmp(command,"jpeg")) {
2533 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2534 s_image(name, "jpeg", filename, quality);
2536 s_image(name, "png", filename, 0);
2541 static int c_outline(map_t*args)
2543 char*name = lu(args, "name");
2544 char*format = lu(args, "format");
2548 syntaxerror("colon (:) expected");
2550 s_outline(name, format, text);
2554 int fakechar(map_t*args)
2556 char*name = lu(args, "name");
2557 s_box(name, 0, 0, black, 20, 0);
2561 static int c_egon(map_t*args) {return fakechar(args);}
2562 static int c_button(map_t*args) {
2563 char*name = lu(args, "name");
2567 static int current_button_flags = 0;
2568 static int c_on_press(map_t*args)
2570 char*position = lu(args, "position");
2572 if(!strcmp(position, "inside")) {
2573 current_button_flags |= BC_OVERUP_OVERDOWN;
2574 } else if(!strcmp(position, "outside")) {
2575 //current_button_flags |= BC_IDLE_OUTDOWN;
2576 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2577 } else if(!strcmp(position, "anywhere")) {
2578 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2581 if(type == RAWDATA) {
2583 s_buttonaction(current_button_flags, action);
2584 current_button_flags = 0;
2590 static int c_on_release(map_t*args)
2592 char*position = lu(args, "position");
2594 if(!strcmp(position, "inside")) {
2595 current_button_flags |= BC_OVERDOWN_OVERUP;
2596 } else if(!strcmp(position, "outside")) {
2597 current_button_flags |= BC_OUTDOWN_IDLE;
2598 } else if(!strcmp(position, "anywhere")) {
2599 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2602 if(type == RAWDATA) {
2604 s_buttonaction(current_button_flags, action);
2605 current_button_flags = 0;
2611 static int c_on_move_in(map_t*args)
2613 char*position = lu(args, "state");
2615 if(!strcmp(position, "pressed")) {
2616 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2617 } else if(!strcmp(position, "not_pressed")) {
2618 current_button_flags |= BC_IDLE_OVERUP;
2619 } else if(!strcmp(position, "any")) {
2620 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2623 if(type == RAWDATA) {
2625 s_buttonaction(current_button_flags, action);
2626 current_button_flags = 0;
2632 static int c_on_move_out(map_t*args)
2634 char*position = lu(args, "state");
2636 if(!strcmp(position, "pressed")) {
2637 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2638 } else if(!strcmp(position, "not_pressed")) {
2639 current_button_flags |= BC_OVERUP_IDLE;
2640 } else if(!strcmp(position, "any")) {
2641 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2644 if(type == RAWDATA) {
2646 s_buttonaction(current_button_flags, action);
2647 current_button_flags = 0;
2653 static int c_on_key(map_t*args)
2655 char*key = lu(args, "key");
2657 if(strlen(key)==1) {
2660 current_button_flags |= 0x4000 + (key[0]*0x200);
2662 syntaxerror("invalid character: %c"+key[0]);
2667 <ctrl-x> = 0x200*(x-'a')
2671 syntaxerror("invalid key: %s",key);
2674 if(type == RAWDATA) {
2676 s_buttonaction(current_button_flags, action);
2677 current_button_flags = 0;
2684 static int c_edittext(map_t*args)
2686 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2687 char*name = lu(args, "name");
2688 char*font = lu(args, "font");
2689 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2690 int width = parseTwip(lu(args, "width"));
2691 int height = parseTwip(lu(args, "height"));
2692 char*text = lu(args, "text");
2693 RGBA color = parseColor(lu(args, "color"));
2694 int maxlength = parseInt(lu(args, "maxlength"));
2695 char*variable = lu(args, "variable");
2696 char*passwordstr = lu(args, "password");
2697 char*wordwrapstr = lu(args, "wordwrap");
2698 char*multilinestr = lu(args, "multiline");
2699 char*htmlstr = lu(args, "html");
2700 char*noselectstr = lu(args, "noselect");
2701 char*readonlystr = lu(args, "readonly");
2702 char*borderstr = lu(args, "border");
2705 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2706 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2707 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2708 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2709 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2710 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2711 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2713 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2717 static int c_morphshape(map_t*args) {return fakechar(args);}
2718 static int c_movie(map_t*args) {return fakechar(args);}
2720 static int c_action(map_t*args)
2722 char* filename = map_lookup(args, "filename");
2723 if(!filename ||!*filename) {
2725 if(type != RAWDATA) {
2726 syntaxerror("colon (:) expected");
2730 FILE*fi = fopen(filename, "rb");
2734 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2735 fseek(fi, 0, SEEK_END);
2737 fseek(fi, 0, SEEK_SET);
2738 text = rfx_alloc(l+1);
2739 fread(text, l, 1, fi);
2751 command_func_t* func;
2754 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2755 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2756 // "import" type stuff
2757 {"swf", c_swf, "name filename"},
2758 {"shape", c_swf, "name filename"},
2759 {"jpeg", c_image, "name filename quality=80%"},
2760 {"png", c_image, "name filename"},
2761 {"movie", c_movie, "name filename"},
2762 {"sound", c_sound, "name filename"},
2763 {"font", c_font, "name filename"},
2764 {"soundtrack", c_soundtrack, "filename"},
2765 {"quicktime", c_quicktime, "url"},
2767 // generators of primitives
2769 {"point", c_point, "name x=0 y=0"},
2770 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2771 {"outline", c_outline, "name format=simple"},
2772 {"textshape", c_textshape, "name font size=100% text"},
2774 // character generators
2775 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2776 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2777 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2779 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2780 {"text", c_text, "name text font size=100% color=white"},
2781 {"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"},
2782 {"morphshape", c_morphshape, "name start end"},
2783 {"button", c_button, "name"},
2784 {"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="},
2785 {"on_press", c_on_press, "position=inside"},
2786 {"on_release", c_on_release, "position=anywhere"},
2787 {"on_move_in", c_on_move_in, "state=not_pressed"},
2788 {"on_move_out", c_on_move_out, "state=not_pressed"},
2789 {"on_key", c_on_key, "key=any"},
2792 {"play", c_play, "name loop=0 @nomultiple=0"},
2793 {"stop", c_stop, "name= "},
2794 {"nextframe", c_nextframe, "name"},
2795 {"previousframe", c_previousframe, "name"},
2797 // object placement tags
2798 {"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="},
2799 {"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="},
2800 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2801 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2802 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2803 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2804 {"del", c_del, "name"},
2805 // virtual object placement
2806 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2808 // commands which start a block
2809 //startclip (see above)
2810 {"sprite", c_sprite, "name"},
2811 {"action", c_action, "filename="},
2817 static map_t parseArguments(char*command, char*pattern)
2833 string_set(&t1, "commandname");
2834 string_set(&t2, command);
2835 map_put(&result, t1, t2);
2837 if(!pattern || !*pattern)
2844 if(!strncmp("<i> ", x, 3)) {
2846 if(type == COMMAND || type == RAWDATA) {
2848 syntaxerror("character name expected");
2850 name[pos].str = "instance";
2852 value[pos].str = text;
2853 value[pos].len = strlen(text);
2857 if(type == ASSIGNMENT)
2860 name[pos].str = "character";
2862 value[pos].str = text;
2863 value[pos].len = strlen(text);
2871 isboolean[pos] = (x[0] =='@');
2884 name[pos].len = d-x;
2889 name[pos].len = e-x;
2890 value[pos].str = e+1;
2891 value[pos].len = d-e-1;
2899 /* for(t=0;t<len;t++) {
2900 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2901 isboolean[t]?"(boolean)":"");
2906 if(type == RAWDATA || type == COMMAND) {
2911 // first, search for boolean arguments
2912 for(pos=0;pos<len;pos++)
2914 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2916 if(type == ASSIGNMENT)
2918 value[pos].str = text;
2919 value[pos].len = strlen(text);
2920 /*printf("setting boolean parameter %s (to %s)\n",
2921 strdup_n(name[pos], namelen[pos]),
2922 strdup_n(value[pos], valuelen[pos]));*/
2927 // second, search for normal arguments
2929 for(pos=0;pos<len;pos++)
2931 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2932 (type != ASSIGNMENT && !set[pos])) {
2934 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2936 if(type == ASSIGNMENT)
2939 value[pos].str = text;
2940 value[pos].len = strlen(text);
2942 printf("setting parameter %s (to %s)\n",
2943 strdup_n(name[pos].str, name[pos].len),
2944 strdup_n(value[pos].str, value[pos].len));
2950 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
2954 for(t=0;t<len;t++) {
2955 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2958 for(t=0;t<len;t++) {
2959 if(value[t].str && value[t].str[0] == '*') {
2960 //relative default- take value from some other parameter
2962 for(s=0;s<len;s++) {
2963 if(value[s].len == value[t].len-1 &&
2964 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2965 value[t].str = value[s].str;
2968 if(value[t].str == 0) {
2970 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2974 /* ok, now construct the dictionary from the parameters */
2978 map_put(&result, name[t], value[t]);
2982 static void parseArgumentsForCommand(char*command)
2987 msg("<verbose> parse Command: %s (line %d)", command, line);
2989 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2990 if(!strcmp(arguments[t].command, command)) {
2992 /* ugly hack- will be removed soon (once documentation and .sc generating
2993 utilities have been changed) */
2994 if(!strcmp(command, "swf") && !stackpos) {
2995 warning("Please use .flash instead of .swf- this will be mandatory soon");
3000 args = parseArguments(command, arguments[t].arguments);
3006 syntaxerror("command %s not known", command);
3008 // catch missing .flash directives at the beginning of a file
3009 if(strcmp(command, "flash") && !stackpos)
3011 syntaxerror("No movie defined- use .flash first");
3015 printf(".%s\n", command);fflush(stdout);
3016 map_dump(&args, stdout, "\t");fflush(stdout);
3019 (*arguments[nr].func)(&args);
3021 /*if(!strcmp(command, "button") ||
3022 !strcmp(command, "action")) {
3025 if(type == COMMAND) {
3026 if(!strcmp(text, "end"))
3040 int main (int argc,char ** argv)
3043 processargs(argc, argv);
3044 initLog(0,-1,0,0,-1,verbose);
3047 args_callback_usage(argv[0]);
3051 file = generateTokens(filename);
3053 printf("parser returned error.\n");
3059 while(!noMoreTokens()) {
3062 syntaxerror("command expected");
3063 parseArgumentsForCommand(text);