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[] = {
54 int args_callback_option(char*name,char*val)
56 if(!strcmp(name, "V")) {
57 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
60 else if(!strcmp(name, "o")) {
62 override_outputname = 1;
65 else if(!strcmp(name, "O")) {
69 else if(!strcmp(name, "v")) {
74 printf("Unknown option: -%s\n", name);
79 int args_callback_longoption(char*name,char*val)
81 return args_long2shortoption(options, name, val);
83 void args_callback_usage(char *name)
86 printf("Usage: %s [-o file.swf] file.sc\n", name);
88 printf("-h , --help Print short help message and exit\n");
89 printf("-V , --version Print version info and exit\n");
90 printf("-v , --verbose Increase verbosity. \n");
91 printf("-o , --output <filename> Set output file to <filename>.\n");
94 int args_callback_command(char*name,char*val)
97 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
104 static struct token_t* file;
113 static void syntaxerror(char*format, ...)
117 va_start(arglist, format);
118 vsprintf(buf, format, arglist);
120 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
124 static void warning(char*format, ...)
128 va_start(arglist, format);
129 vsprintf(buf, format, arglist);
131 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
134 static void readToken()
136 type = file[pos].type;
138 syntaxerror("unexpected end of file");
140 text = file[pos].text;
141 textlen = strlen(text);
142 line = file[pos].line;
143 column = file[pos].column;
145 //printf("---> %d(%s) %s\n", type, type_names[type], text);
148 static void pushBack()
151 if(!pos) syntaxerror("internal error 3");
156 textlen = strlen(text);
159 column = file[p].column;
162 static int noMoreTokens()
164 if(file[pos].type == END)
169 // ------------------------------ swf routines ----------------------------
173 int type; //0=swf, 1=sprite, 2=clip, 3=button
179 /* for sprites (1): */
185 dictionary_t oldinstances;
190 static int stackpos = 0;
192 static dictionary_t characters;
193 static dictionary_t images;
194 static dictionary_t textures;
195 static dictionary_t outlines;
196 static dictionary_t gradients;
197 static char idmap[65536];
198 static TAG*tag = 0; //current tag
200 static int id; //current character id
201 static int currentframe; //current frame in current level
202 static SRECT currentrect; //current bounding box in current level
203 static U16 currentdepth;
204 static dictionary_t instances;
205 static dictionary_t fonts;
206 static dictionary_t sounds;
208 typedef struct _parameters {
210 float scalex, scaley;
218 typedef struct _character {
224 typedef struct _instance {
225 character_t*character;
227 parameters_t parameters;
228 TAG* lastTag; //last tag which set the object
229 U16 lastFrame; //frame lastTag is in
232 typedef struct _outline {
237 typedef struct _gradient {
243 typedef struct _texture {
247 static void character_init(character_t*c)
249 memset(c, 0, sizeof(character_t));
251 static character_t* character_new()
254 c = (character_t*)malloc(sizeof(character_t));
258 static void instance_init(instance_t*i)
260 memset(i, 0, sizeof(instance_t));
262 static instance_t* instance_new()
265 c = (instance_t*)malloc(sizeof(instance_t));
270 static void incrementid()
274 syntaxerror("Out of character ids.");
279 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
281 character_t* c = character_new();
283 c->definingTag = ctag;
286 if(dictionary_lookup(&characters, name))
287 syntaxerror("character %s defined twice", name);
288 dictionary_put2(&characters, name, c);
290 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
292 swf_SetString(tag, name);
293 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
296 swf_SetString(tag, name);
298 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
300 character_t* c = character_new();
301 c->definingTag = ctag;
305 if(dictionary_lookup(&images, name))
306 syntaxerror("image %s defined twice", name);
307 dictionary_put2(&images, name, c);
309 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
311 instance_t* i = instance_new();
314 //swf_GetMatrix(0, &i->matrix);
315 if(dictionary_lookup(&instances, name))
316 syntaxerror("object %s defined twice", name);
317 dictionary_put2(&instances, name, i);
321 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)
324 p->scalex = scalex; p->scaley = scaley;
325 p->pin = pin; p->pivot = pivot;
326 p->rotate = rotate; p->cxform = cxform;
330 static void parameters_clear(parameters_t*p)
333 p->scalex = 1.0; p->scaley = 1.0;
336 p->pivot.x = 0; p->pivot.y = 0;
339 swf_GetCXForm(0, &p->cxform, 1);
342 static void makeMatrix(MATRIX*m, parameters_t*p)
351 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
352 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
353 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
354 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
356 m->sx = (int)(sx*65536+0.5);
357 m->r1 = (int)(r1*65536+0.5);
358 m->r0 = (int)(r0*65536+0.5);
359 m->sy = (int)(sy*65536+0.5);
363 h = swf_TurnPoint(p->pin, m);
368 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
373 r = swf_TurnRect(rect, &m);
374 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
375 currentrect.xmax == 0 && currentrect.ymax == 0)
378 swf_ExpandRect2(¤trect, &r);
382 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
384 SWF*swf = (SWF*)malloc(sizeof(SWF));
387 syntaxerror(".swf blocks can't be nested");
389 memset(swf, 0, sizeof(swf));
390 swf->fileVersion = version;
392 swf->frameRate = fps;
393 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
394 swf->compressed = compress;
395 swf_SetRGB(tag,&background);
397 if(stackpos==sizeof(stack)/sizeof(stack[0]))
398 syntaxerror("too many levels of recursion");
400 dictionary_init(&characters);
401 dictionary_init(&images);
402 dictionary_init(&textures);
403 dictionary_init(&outlines);
404 dictionary_init(&gradients);
405 dictionary_init(&instances);
406 dictionary_init(&fonts);
407 dictionary_init(&sounds);
409 memset(&stack[stackpos], 0, sizeof(stack[0]));
410 stack[stackpos].type = 0;
411 stack[stackpos].filename = strdup(name);
412 stack[stackpos].swf = swf;
413 stack[stackpos].oldframe = -1;
418 memset(¤trect, 0, sizeof(currentrect));
421 memset(idmap, 0, sizeof(idmap));
425 void s_sprite(char*name)
427 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
428 swf_SetU16(tag, id); //id
429 swf_SetU16(tag, 0); //frames
431 memset(&stack[stackpos], 0, sizeof(stack[0]));
432 stack[stackpos].type = 1;
433 stack[stackpos].oldframe = currentframe;
434 stack[stackpos].olddepth = currentdepth;
435 stack[stackpos].oldrect = currentrect;
436 stack[stackpos].oldinstances = instances;
437 stack[stackpos].tag = tag;
438 stack[stackpos].id = id;
439 stack[stackpos].name = strdup(name);
441 /* FIXME: those four fields should be bundled together */
442 dictionary_init(&instances);
445 memset(¤trect, 0, sizeof(currentrect));
451 typedef struct _buttonrecord
459 typedef struct _button
463 buttonrecord_t records[4];
466 static button_t mybutton;
468 void s_button(char*name)
470 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
471 swf_SetU16(tag, id); //id
472 swf_ButtonSetFlags(tag, 0); //menu=no
474 memset(&mybutton, 0, sizeof(mybutton));
476 memset(&stack[stackpos], 0, sizeof(stack[0]));
477 stack[stackpos].type = 3;
478 stack[stackpos].tag = tag;
479 stack[stackpos].id = id;
480 stack[stackpos].name = strdup(name);
481 stack[stackpos].oldrect = currentrect;
482 memset(¤trect, 0, sizeof(currentrect));
487 void s_buttonput(char*character, char*as, parameters_t p)
489 character_t* c = dictionary_lookup(&characters, character);
494 if(!stackpos || (stack[stackpos-1].type != 3)) {
495 syntaxerror(".show may only appear in .button");
498 syntaxerror("character %s not known (in .shape %s)", character, character);
500 if(mybutton.endofshapes) {
501 syntaxerror("a .do may not precede a .show", character, character);
504 m = s_instancepos(c->size, &p);
512 if(*s==',' || *s==0) {
513 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
514 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
515 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
516 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
517 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
518 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
525 static void setbuttonrecords(TAG*tag)
527 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
528 if(!mybutton.endofshapes) {
531 if(!mybutton.records[3].set) {
532 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
536 if(mybutton.records[t].set) {
537 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
540 swf_SetU8(tag,0); // end of button records
541 mybutton.endofshapes = 1;
545 void s_buttonaction(int flags, char*action)
551 setbuttonrecords(stack[stackpos-1].tag);
553 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
555 syntaxerror("Couldn't compile ActionScript");
558 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
559 swf_ActionSet(stack[stackpos-1].tag, a);
560 mybutton.nr_actions++;
565 static void setactionend(TAG*tag)
567 if(!mybutton.nr_actions) {
568 /* no actions means we didn't have an actionoffset,
569 which means we can't signal the end of the
570 buttonaction records, so, *sigh*, we have
571 to insert a dummy record */
572 swf_SetU16(tag, 0); //offset
573 swf_SetU16(tag, 0); //condition
574 swf_SetU8(tag, 0); //action
578 static void s_endButton()
581 setbuttonrecords(stack[stackpos-1].tag);
582 setactionend(stack[stackpos-1].tag);
585 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
589 tag = stack[stackpos].tag;
590 currentrect = stack[stackpos].oldrect;
592 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
593 free(stack[stackpos].name);
596 TAG* removeFromTo(TAG*from, TAG*to)
598 TAG*save = from->prev;
600 TAG*next = from->next;
608 static void s_endSprite()
610 SRECT r = currentrect;
612 if(stack[stackpos].cut)
613 tag = removeFromTo(stack[stackpos].cut, tag);
617 /* TODO: before clearing, prepend "<spritename>." to names and
618 copy into old instances dict */
619 dictionary_clear(&instances);
621 currentframe = stack[stackpos].oldframe;
622 currentrect = stack[stackpos].oldrect;
623 currentdepth = stack[stackpos].olddepth;
624 instances = stack[stackpos].oldinstances;
626 tag = swf_InsertTag(tag, ST_SHOWFRAME);
627 tag = swf_InsertTag(tag, ST_END);
629 tag = stack[stackpos].tag;
632 syntaxerror("internal error(7)");
634 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
635 free(stack[stackpos].name);
638 static void s_endSWF()
644 if(stack[stackpos].cut)
645 tag = removeFromTo(stack[stackpos].cut, tag);
649 swf = stack[stackpos].swf;
650 filename = stack[stackpos].filename;
652 //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
653 // tag = swf_InsertTag(tag, ST_SHOWFRAME);
654 tag = swf_InsertTag(tag, ST_SHOWFRAME);
656 tag = swf_InsertTag(tag, ST_END);
658 swf_OptimizeTagOrder(swf);
664 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
665 swf->movieSize = currentrect; /* "autocrop" */
668 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
669 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
670 swf->movieSize.ymax += 20;
671 warning("Empty bounding box for movie");
674 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
676 syntaxerror("couldn't create output file %s", filename);
679 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
681 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
685 dictionary_clear(&instances);
686 dictionary_clear(&characters);
687 dictionary_clear(&images);
688 dictionary_clear(&textures);
689 dictionary_clear(&outlines);
690 dictionary_clear(&gradients);
691 dictionary_clear(&fonts);
692 dictionary_clear(&sounds);
702 if(stack[stackpos-1].type == 0)
703 syntaxerror("End of file encountered in .flash block");
704 if(stack[stackpos-1].type == 1)
705 syntaxerror("End of file encountered in .sprite block");
706 if(stack[stackpos-1].type == 2)
707 syntaxerror("End of file encountered in .clip block");
713 return currentframe+1;
716 void s_frame(int nr, int cut, char*name)
722 syntaxerror("Illegal frame number");
723 nr--; // internally, frame 1 is frame 0
725 for(t=currentframe;t<nr;t++) {
726 tag = swf_InsertTag(tag, ST_SHOWFRAME);
727 if(t==nr-1 && name && *name) {
728 tag = swf_InsertTag(tag, ST_FRAMELABEL);
729 swf_SetString(tag, name);
732 if(nr == 0 && currentframe == 0 && name) {
733 tag = swf_InsertTag(tag, ST_FRAMELABEL);
734 swf_SetString(tag, name);
739 syntaxerror("Can't cut, frame empty");
741 stack[stackpos].cut = tag;
747 int parseColor2(char*str, RGBA*color);
749 int addFillStyle(SHAPE*s, SRECT*r, char*name)
756 parseColor2(name, &color);
757 return swf_ShapeAddSolidFillStyle(s, &color);
758 } else if ((texture = dictionary_lookup(&textures, name))) {
759 return swf_ShapeAddFillStyle2(s, &texture->fs);
760 } else if((image = dictionary_lookup(&images, name))) {
762 swf_GetMatrix(0, &m);
763 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
764 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
767 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
768 } else if ((gradient = dictionary_lookup(&gradients, name))) {
772 swf_GetMatrix(0, &rot);
773 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
774 csin = sin(-gradient->rotate*2*3.14159265358979/360);
776 rot.r1 = -csin*65536;
779 r2 = swf_TurnRect(*r, &rot);
780 swf_GetMatrix(0, &m);
781 m.sx = (r2.xmax - r2.xmin)*2*ccos;
782 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
783 m.r0 = (r2.ymax - r2.ymin)*2*csin;
784 m.sy = (r2.ymax - r2.ymin)*2*ccos;
785 m.tx = r->xmin + (r->xmax - r->xmin)/2;
786 m.ty = r->ymin + (r->ymax - r->ymin)/2;
787 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
788 } else if (parseColor2(name, &color)) {
789 return swf_ShapeAddSolidFillStyle(s, &color);
791 syntaxerror("not a color/fillstyle: %s", name);
796 RGBA black={r:0,g:0,b:0,a:0};
797 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
806 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
809 linewidth = linewidth>=20?linewidth-20:0;
810 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
813 fs1 = addFillStyle(s, &r2, texture);
816 r.xmin = r2.xmin-linewidth/2;
817 r.ymin = r2.ymin-linewidth/2;
818 r.xmax = r2.xmax+linewidth/2;
819 r.ymax = r2.ymax+linewidth/2;
821 swf_SetShapeHeader(tag,s);
822 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
823 swf_ShapeSetLine(tag,s,width,0);
824 swf_ShapeSetLine(tag,s,0,height);
825 swf_ShapeSetLine(tag,s,-width,0);
826 swf_ShapeSetLine(tag,s,0,-height);
827 swf_ShapeSetEnd(tag);
830 s_addcharacter(name, id, tag, r);
834 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
840 outline = dictionary_lookup(&outlines, outlinename);
842 syntaxerror("outline %s not defined", outlinename);
846 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
849 linewidth = linewidth>=20?linewidth-20:0;
850 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
853 fs1 = addFillStyle(s, &r2, texture);
856 rect.xmin = r2.xmin-linewidth/2;
857 rect.ymin = r2.ymin-linewidth/2;
858 rect.xmax = r2.xmax+linewidth/2;
859 rect.ymax = r2.ymax+linewidth/2;
861 swf_SetRect(tag,&rect);
862 swf_SetShapeStyles(tag, s);
863 swf_ShapeCountBits(s,0,0);
864 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
865 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
866 swf_SetShapeBits(tag, s);
867 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
870 s_addcharacter(name, id, tag, rect);
874 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
879 r2.xmin = r2.ymin = 0;
883 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
886 linewidth = linewidth>=20?linewidth-20:0;
887 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
890 fs1 = addFillStyle(s, &r2, texture);
892 rect.xmin = r2.xmin-linewidth/2;
893 rect.ymin = r2.ymin-linewidth/2;
894 rect.xmax = r2.xmax+linewidth/2;
895 rect.ymax = r2.ymax+linewidth/2;
897 swf_SetRect(tag,&rect);
898 swf_SetShapeHeader(tag,s);
899 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
900 swf_ShapeSetCircle(tag, s, r,r,r,r);
901 swf_ShapeSetEnd(tag);
904 s_addcharacter(name, id, tag, rect);
908 void s_textshape(char*name, char*fontname, float size, char*_text)
911 U8*text = (U8*)_text;
915 font = dictionary_lookup(&fonts, fontname);
917 syntaxerror("font \"%s\" not known!", fontname);
919 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
920 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
921 s_box(name, 0, 0, black, 20, 0);
924 g = font->ascii2glyph[text[0]];
926 outline = malloc(sizeof(outline_t));
927 memset(outline, 0, sizeof(outline_t));
928 outline->shape = font->glyph[g].shape;
929 outline->bbox = font->layout->bounds[g];
933 swf_Shape11DrawerInit(&draw, 0);
934 swf_DrawText(&draw, font, (int)(size*100), _text);
936 outline->shape = swf_ShapeDrawerToShape(&draw);
937 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
941 if(dictionary_lookup(&outlines, name))
942 syntaxerror("outline %s defined twice", name);
943 dictionary_put2(&outlines, name, outline);
946 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
951 font = dictionary_lookup(&fonts, fontname);
953 syntaxerror("font \"%s\" not known!", fontname);
955 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
957 if(!font->numchars) {
958 s_box(name, 0, 0, black, 20, 0);
961 r = swf_SetDefineText(tag, font, &color, text, size);
963 s_addcharacter(name, id, tag, r);
967 void s_quicktime(char*name, char*url)
972 memset(&r, 0, sizeof(r));
974 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
976 swf_SetString(tag, url);
978 s_addcharacter(name, id, tag, r);
982 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
985 EditTextLayout layout;
988 if(fontname && *fontname) {
989 flags |= ET_USEOUTLINES;
990 font = dictionary_lookup(&fonts, fontname);
992 syntaxerror("font \"%s\" not known!", fontname);
994 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
997 layout.leftmargin = 0;
998 layout.rightmargin = 0;
1006 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1008 s_addcharacter(name, id, tag, r);
1012 /* type: either "jpeg" or "png"
1014 void s_image(char*name, char*type, char*filename, int quality)
1016 /* an image is actually two folded: 1st bitmap, 2nd character.
1017 Both of them can be used separately */
1019 /* step 1: the bitmap */
1024 warning("image type \"png\" not supported yet!");
1025 s_box(name, 0, 0, black, 20, 0);
1029 #ifndef HAVE_LIBJPEG
1030 warning("no jpeg support compiled in");
1031 s_box(name, 0, 0, black, 20, 0);
1034 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1035 swf_SetU16(tag, imageID);
1037 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1038 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1041 swf_GetJPEGSize(filename, &width, &height);
1048 s_addimage(name, id, tag, r);
1053 /* step 2: the character */
1054 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1055 swf_SetU16(tag, id);
1056 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1058 s_addcharacter(name, id, tag, r);
1062 void s_getBitmapSize(char*name, int*width, int*height)
1064 character_t* image = dictionary_lookup(&images, name);
1065 gradient_t* gradient = dictionary_lookup(&gradients,name);
1067 *width = image->size.xmax;
1068 *height = image->size.ymax;
1072 /* internal SWF gradient size */
1073 if(gradient->radial) {
1082 syntaxerror("No such bitmap/gradient: %s", name);
1085 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1087 gradient_t* gradient = dictionary_lookup(&gradients, object);
1088 character_t* bitmap = dictionary_lookup(&images, object);
1089 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1091 FILLSTYLE*fs = &texture->fs;
1094 fs->type = FILL_TILED;
1095 fs->id_bitmap = bitmap->id;
1096 } else if(gradient) {
1097 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1098 fs->gradient = gradient->gradient;
1100 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1101 makeMatrix(&fs->m, &p);
1102 if(gradient && !gradient->radial) {
1109 p2 = swf_TurnPoint(p1, &m);
1114 if(dictionary_lookup(&textures, name))
1115 syntaxerror("texture %s defined twice", name);
1116 dictionary_put2(&textures, name, texture);
1119 void dumpSWF(SWF*swf)
1121 TAG* tag = swf->firstTag;
1122 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1124 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1127 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1130 void s_font(char*name, char*filename)
1133 font = swf_LoadFont(filename);
1136 warning("Couldn't open font file \"%s\"", filename);
1137 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1138 memset(font, 0, sizeof(SWFFONT));
1139 dictionary_put2(&fonts, name, font);
1145 /* fix the layout. Only needed for old fonts */
1147 for(t=0;t<font->numchars;t++) {
1148 font->glyph[t].advance = 0;
1151 swf_FontCreateLayout(font);
1153 /* just in case this thing is used in .edittext later on */
1154 swf_FontPrepareForEditText(font);
1157 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1158 swf_FontSetDefine2(tag, font);
1161 if(dictionary_lookup(&fonts, name))
1162 syntaxerror("font %s defined twice", name);
1163 dictionary_put2(&fonts, name, font);
1168 typedef struct _sound_t
1174 void s_sound(char*name, char*filename)
1176 struct WAV wav, wav2;
1182 if(!readWAV(filename, &wav)) {
1183 warning("Couldn't read wav file \"%s\"", filename);
1187 convertWAV2mono(&wav, &wav2, 44100);
1188 samples = (U16*)wav2.data;
1189 numsamples = wav2.size/2;
1191 #ifdef WORDS_BIGENDIAN
1193 for(t=0;t<numsamples;t++) {
1194 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1199 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1200 swf_SetU16(tag, id); //id
1201 swf_SetSoundDefine(tag, samples, numsamples);
1203 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1207 if(dictionary_lookup(&sounds, name))
1208 syntaxerror("sound %s defined twice", name);
1209 dictionary_put2(&sounds, name, sound);
1217 static char* gradient_getToken(const char**p)
1221 while(**p && strchr(" \t\n\r", **p)) {
1225 while(**p && !strchr(" \t\n\r", **p)) {
1228 result = malloc((*p)-start+1);
1229 memcpy(result,start,(*p)-start+1);
1230 result[(*p)-start] = 0;
1234 float parsePercent(char*str);
1235 RGBA parseColor(char*str);
1237 GRADIENT parseGradient(const char*str)
1241 const char* p = str;
1242 memset(&gradient, 0, sizeof(GRADIENT));
1244 char*posstr,*colorstr;
1247 posstr = gradient_getToken(&p);
1250 pos = (int)(parsePercent(posstr)*255.0);
1253 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1254 colorstr = gradient_getToken(&p);
1255 color = parseColor(colorstr);
1256 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1257 warning("gradient record too big- max size is 8, rest ignored");
1260 gradient.ratios[gradient.num] = pos;
1261 gradient.rgba[gradient.num] = color;
1270 void s_gradient(char*name, const char*text, int radial, int rotate)
1272 gradient_t* gradient;
1273 gradient = malloc(sizeof(gradient_t));
1274 memset(gradient, 0, sizeof(gradient_t));
1275 gradient->gradient = parseGradient(text);
1276 gradient->radial = radial;
1277 gradient->rotate = rotate;
1279 if(dictionary_lookup(&gradients, name))
1280 syntaxerror("gradient %s defined twice", name);
1281 dictionary_put2(&gradients, name, gradient);
1284 void s_action(const char*text)
1287 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1289 syntaxerror("Couldn't compile ActionScript");
1292 tag = swf_InsertTag(tag, ST_DOACTION);
1294 swf_ActionSet(tag, a);
1299 void s_initaction(const char*character, const char*text)
1303 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1305 syntaxerror("Couldn't compile ActionScript");
1308 c = (character_t*)dictionary_lookup(&characters, character);
1310 tag = swf_InsertTag(tag, ST_DOINITACTION);
1311 swf_SetU16(tag, c->id);
1312 swf_ActionSet(tag, a);
1317 int s_swf3action(char*name, char*action)
1320 instance_t* object = 0;
1322 dictionary_lookup(&instances, name);
1323 if(!object && name && *name) {
1324 /* we have a name, but couldn't find it. Abort. */
1327 a = action_SetTarget(0, name);
1328 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1329 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1330 else if(!strcmp(action, "stop")) a = action_Stop(a);
1331 else if(!strcmp(action, "play")) a = action_Play(a);
1332 a = action_SetTarget(a, "");
1335 tag = swf_InsertTag(tag, ST_DOACTION);
1336 swf_ActionSet(tag, a);
1341 void s_outline(char*name, char*format, char*source)
1350 //swf_Shape10DrawerInit(&draw, 0);
1351 swf_Shape11DrawerInit(&draw, 0);
1353 draw_string(&draw, source);
1355 shape = swf_ShapeDrawerToShape(&draw);
1356 bounds = swf_ShapeDrawerGetBBox(&draw);
1357 draw.dealloc(&draw);
1359 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1360 outline->shape = shape;
1361 outline->bbox = bounds;
1363 if(dictionary_lookup(&outlines, name))
1364 syntaxerror("outline %s defined twice", name);
1365 dictionary_put2(&outlines, name, outline);
1368 int s_playsound(char*name, int loops, int nomultiple, int stop)
1374 sound = dictionary_lookup(&sounds, name);
1378 tag = swf_InsertTag(tag, ST_STARTSOUND);
1379 swf_SetU16(tag, sound->id); //id
1380 memset(&info, 0, sizeof(info));
1383 info.nomultiple = nomultiple;
1384 swf_SetSoundInfo(tag, &info);
1388 void s_includeswf(char*name, char*filename)
1396 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1397 f = open(filename,O_RDONLY|O_BINARY);
1399 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1400 s_box(name, 0, 0, black, 20, 0);
1403 if (swf_ReadSWF(f,&swf)<0) {
1404 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1405 s_box(name, 0, 0, black, 20, 0);
1410 /* FIXME: The following sets the bounding Box for the character.
1411 It is wrong for two reasons:
1412 a) It may be too small (in case objects in the movie clip at the borders)
1413 b) it may be too big (because the poor movie never got autocropped)
1417 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1418 swf_SetU16(tag, id);
1419 swf_SetU16(tag, swf.frameCount);
1421 swf_Relocate(&swf, idmap);
1423 ftag = swf.firstTag;
1427 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1428 if(cutout[t] == ftag->id) {
1432 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1434 if(ftag->id == ST_END)
1438 /* We simply dump all tags right after the sprite
1439 header, relying on the fact that swf_OptimizeTagOrder() will
1440 sort things out for us later.
1441 We also rely on the fact that the imported SWF is well-formed.
1443 tag = swf_InsertTag(tag, ftag->id);
1444 swf_SetBlock(tag, ftag->data, ftag->len);
1448 syntaxerror("Included file %s contains errors", filename);
1449 tag = swf_InsertTag(tag, ST_END);
1453 s_addcharacter(name, id, tag, r);
1456 SRECT s_getCharBBox(char*name)
1458 character_t* c = dictionary_lookup(&characters, name);
1459 if(!c) syntaxerror("character '%s' unknown(2)", name);
1462 SRECT s_getInstanceBBox(char*name)
1464 instance_t * i = dictionary_lookup(&instances, name);
1466 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1468 if(!c) syntaxerror("internal error(5)");
1471 parameters_t s_getParameters(char*name)
1473 instance_t * i = dictionary_lookup(&instances, name);
1474 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1475 return i->parameters;
1477 void s_startclip(char*instance, char*character, parameters_t p)
1479 character_t* c = dictionary_lookup(&characters, character);
1483 syntaxerror("character %s not known", character);
1485 i = s_addinstance(instance, c, currentdepth);
1487 m = s_instancepos(i->character->size, &p);
1489 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1490 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1491 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1493 i->lastFrame= currentframe;
1495 stack[stackpos].tag = tag;
1496 stack[stackpos].type = 2;
1505 swf_SetTagPos(stack[stackpos].tag, 0);
1506 swf_GetPlaceObject(stack[stackpos].tag, &p);
1507 p.clipdepth = currentdepth;
1509 swf_ClearTag(stack[stackpos].tag);
1510 swf_SetPlaceObject(stack[stackpos].tag, &p);
1514 void s_put(char*instance, char*character, parameters_t p)
1516 character_t* c = dictionary_lookup(&characters, character);
1520 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1523 i = s_addinstance(instance, c, currentdepth);
1525 m = s_instancepos(i->character->size, &p);
1527 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1528 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1530 i->lastFrame = currentframe;
1534 void s_jump(char*instance, parameters_t p)
1536 instance_t* i = dictionary_lookup(&instances, instance);
1539 syntaxerror("instance %s not known", instance);
1543 m = s_instancepos(i->character->size, &p);
1545 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1546 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1548 i->lastFrame = currentframe;
1551 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1555 if(num==0 || num==1)
1557 ratio = (float)pos/(float)num;
1559 p.x = (p2->x-p1->x)*ratio + p1->x;
1560 p.y = (p2->y-p1->y)*ratio + p1->y;
1561 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1562 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1563 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1564 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1566 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1567 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1568 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1569 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1571 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1572 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1573 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1574 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1576 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1577 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1578 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1579 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1583 void s_change(char*instance, parameters_t p2)
1585 instance_t* i = dictionary_lookup(&instances, instance);
1589 int frame, allframes;
1591 syntaxerror("instance %s not known", instance);
1595 allframes = currentframe - i->lastFrame - 1;
1597 warning(".change ignored. can only .put/.change an object once per frame.");
1601 m = s_instancepos(i->character->size, &p2);
1602 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1603 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1606 /* o.k., we got the start and end point set. Now iterate though all the
1607 tags in between, inserting object changes after each new frame */
1610 if(!t) syntaxerror("internal error(6)");
1612 while(frame < allframes) {
1613 if(t->id == ST_SHOWFRAME) {
1618 p = s_interpolate(&p1, &p2, frame, allframes);
1619 m = s_instancepos(i->character->size, &p); //needed?
1620 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1621 i->lastFrame = currentframe;
1622 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1624 if(frame == allframes)
1629 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1633 void s_delinstance(char*instance)
1635 instance_t* i = dictionary_lookup(&instances, instance);
1637 syntaxerror("instance %s not known", instance);
1639 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1640 swf_SetU16(tag, i->depth);
1641 dictionary_del(&instances, instance);
1644 void s_qchange(char*instance, parameters_t p)
1651 syntaxerror(".end unexpected");
1652 if(stack[stackpos-1].type == 0)
1654 else if(stack[stackpos-1].type == 1)
1656 else if(stack[stackpos-1].type == 2)
1658 else if(stack[stackpos-1].type == 3)
1660 else syntaxerror("internal error 1");
1663 // ------------------------------------------------------------------------
1665 typedef int command_func_t(map_t*args);
1667 SRECT parseBox(char*str)
1670 float xmin, xmax, ymin, ymax;
1671 char*x = strchr(str, 'x');
1673 if(!strcmp(str, "autocrop")) {
1674 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1678 d1 = strchr(x+1, ':');
1680 d2 = strchr(d1+1, ':');
1682 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1686 else if(d1 && !d2) {
1687 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1693 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1698 r.xmin = (SCOORD)(xmin*20);
1699 r.ymin = (SCOORD)(ymin*20);
1700 r.xmax = (SCOORD)(xmax*20);
1701 r.ymax = (SCOORD)(ymax*20);
1704 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1707 float parseFloat(char*str)
1711 int parseInt(char*str)
1716 if(str[0]=='+' || str[0]=='-')
1720 if(str[t]<'0' || str[t]>'9')
1721 syntaxerror("Not an Integer: \"%s\"", str);
1724 int parseTwip(char*str)
1728 if(str[0]=='+' || str[0]=='-') {
1733 dot = strchr(str, '.');
1737 return sign*parseInt(str)*20;
1739 int l=strlen(++dot);
1741 for(s=str;s<dot-1;s++)
1742 if(*s<'0' || *s>'9')
1743 syntaxerror("Not a coordinate: \"%s\"", str);
1745 if(*s<'0' || *s>'9')
1746 syntaxerror("Not a coordinate: \"%s\"", str);
1748 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1749 warning("precision loss: %s converted to twip: %s", str, dot);
1754 return sign*atoi(str)*20;
1756 return sign*atoi(str)*20+atoi(dot)*2;
1758 return sign*atoi(str)*20+atoi(dot)/5;
1763 int isPoint(char*str)
1765 if(strchr(str, '('))
1771 SPOINT parsePoint(char*str)
1775 int l = strlen(str);
1776 char*comma = strchr(str, ',');
1777 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1778 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1779 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1780 p.x = parseTwip(tmp);
1781 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1782 p.y = parseTwip(tmp);
1786 int parseColor2(char*str, RGBA*color)
1788 int l = strlen(str);
1792 struct {unsigned char r,g,b;char*name;} colors[] =
1793 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1794 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1795 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1796 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1797 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1798 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1799 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1800 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1801 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1802 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1803 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1804 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1808 if(str[0]=='#' && (l==7 || l==9)) {
1809 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1811 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1813 color->r = r; color->g = g; color->b = b; color->a = a;
1816 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1817 if(!strcmp(str, colors[t].name)) {
1822 color->r = r; color->g = g; color->b = b; color->a = a;
1828 RGBA parseColor(char*str)
1831 if(!parseColor2(str, &c))
1832 syntaxerror("Expression '%s' is not a color", str);
1836 typedef struct _muladd {
1841 MULADD parseMulAdd(char*str)
1844 char* str2 = (char*)malloc(strlen(str)+5);
1851 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1852 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1853 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1854 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1855 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1856 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1857 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1858 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1859 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1860 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1862 syntaxerror("'%s' is not a valid color transform expression", str);
1864 m.add = (int)(add*256);
1865 m.mul = (int)(mul*256);
1870 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1872 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1873 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1875 if(a<-32768) a=-32768;
1876 if(a>32767) a=32767;
1877 if(m<-32768) m=-32768;
1878 if(m>32767) m=32767;
1884 float parsePxOrPercent(char*fontname, char*str)
1886 int l = strlen(str);
1887 if(strchr(str, '%'))
1888 return parsePercent(str);
1889 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1890 float p = atof(str);
1891 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1893 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1897 float parsePercent(char*str)
1899 int l = strlen(str);
1903 return atoi(str)/100.0;
1905 syntaxerror("Expression '%s' is not a percentage", str);
1908 int isPercent(char*str)
1910 return str[strlen(str)-1]=='%';
1912 int parseNewSize(char*str, int size)
1915 return parsePercent(str)*size;
1917 return (int)(atof(str)*20);
1920 int isColor(char*str)
1923 return parseColor2(str, &c);
1926 static char* lu(map_t* args, char*name)
1928 char* value = map_lookup(args, name);
1930 map_dump(args, stdout, "");
1931 syntaxerror("internal error 2: value %s should be set", name);
1936 static int c_flash(map_t*args)
1938 char* filename = map_lookup(args, "filename");
1939 char* compressstr = lu(args, "compress");
1940 SRECT bbox = parseBox(lu(args, "bbox"));
1941 int version = parseInt(lu(args, "version"));
1942 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1944 RGBA color = parseColor(lu(args, "background"));
1946 if(!filename || !*filename) {
1947 /* for compatibility */
1948 filename = map_lookup(args, "name");
1949 if(!filename || !*filename) {
1952 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1953 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1957 if(!filename || override_outputname)
1958 filename = outputname;
1960 if(!strcmp(compressstr, "default"))
1961 compress = version==6;
1962 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1964 else if(!strcmp(compressstr, "no"))
1966 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1968 s_swf(filename, bbox, version, fps, compress, color);
1971 int isRelative(char*str)
1973 return !strncmp(str, "<plus>", 6) ||
1974 !strncmp(str, "<minus>", 7);
1976 char* getOffset(char*str)
1978 if(!strncmp(str, "<plus>", 6))
1980 if(!strncmp(str, "<minus>", 7))
1982 syntaxerror("internal error (347)");
1985 int getSign(char*str)
1987 if(!strncmp(str, "<plus>", 6))
1989 if(!strncmp(str, "<minus>", 7))
1991 syntaxerror("internal error (348)");
1994 static dictionary_t points;
1995 static mem_t mpoints;
1996 int points_initialized = 0;
1998 SPOINT getPoint(SRECT r, char*name)
2001 if(!strcmp(name, "center")) {
2003 p.x = (r.xmin + r.xmax)/2;
2004 p.y = (r.ymin + r.ymax)/2;
2008 if(points_initialized)
2009 l = (int)dictionary_lookup(&points, name);
2011 syntaxerror("Invalid point: \"%s\".", name);
2014 return *(SPOINT*)&mpoints.buffer[l];
2017 static int texture2(char*name, char*object, map_t*args, int errors)
2020 char*xstr = map_lookup(args, "x");
2021 char*ystr = map_lookup(args, "y");
2022 char*widthstr = map_lookup(args, "width");
2023 char*heightstr = map_lookup(args, "height");
2024 char*scalestr = map_lookup(args, "scale");
2025 char*scalexstr = map_lookup(args, "scalex");
2026 char*scaleystr = map_lookup(args, "scaley");
2027 char*rotatestr = map_lookup(args, "rotate");
2028 char* shearstr = map_lookup(args, "shear");
2029 char* radiusstr = map_lookup(args, "r");
2031 float scalex = 1.0, scaley = 1.0;
2032 float rotate=0, shear=0;
2034 if(!*xstr && !*ystr) {
2036 syntaxerror("x and y must be set");
2039 if(*scalestr && (*scalexstr || *scaleystr)) {
2040 syntaxerror("scale and scalex/scaley can't both be set");
2043 if((*widthstr || *heightstr) && *radiusstr) {
2044 syntaxerror("width/height and radius can't both be set");
2047 widthstr = radiusstr;
2048 heightstr = radiusstr;
2050 if(!*xstr) xstr="0";
2051 if(!*ystr) ystr="0";
2052 if(!*rotatestr) rotatestr="0";
2053 if(!*shearstr) shearstr="0";
2056 scalex = scaley = parsePercent(scalestr);
2057 } else if(*scalexstr || *scaleystr) {
2058 if(scalexstr) scalex = parsePercent(scalexstr);
2059 if(scaleystr) scaley = parsePercent(scaleystr);
2060 } else if(*widthstr || *heightstr) {
2063 s_getBitmapSize(object, &width, &height);
2065 scalex = (float)parseTwip(widthstr)/(float)width;
2067 scaley = (float)parseTwip(heightstr)/(float)height;
2069 x = parseTwip(xstr);
2070 y = parseTwip(ystr);
2071 rotate = parseFloat(rotatestr);
2072 shear = parseFloat(shearstr);
2074 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2079 static int c_texture(map_t*args)
2081 char*name = lu(args, "instance");
2082 char*object = lu(args, "character");
2083 return texture2(name, object, args, 1);
2086 static int c_gradient(map_t*args)
2088 char*name = lu(args, "name");
2089 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2090 int rotate = parseInt(lu(args, "rotate"));
2094 syntaxerror("colon (:) expected");
2096 s_gradient(name, text, radial, rotate);
2098 /* check whether we also have placement information,
2099 which would make this a positioned gradient.
2100 If there is placement information, texture2() will
2101 add a texture, which has priority over the gradient.
2103 texture2(name, name, args, 0);
2106 static int c_point(map_t*args)
2108 char*name = lu(args, "name");
2112 if(!points_initialized) {
2113 dictionary_init(&points);
2115 points_initialized = 1;
2117 p.x = parseTwip(lu(args, "x"));
2118 p.y = parseTwip(lu(args, "y"));
2119 pos = mem_put(&mpoints, &p, sizeof(p));
2120 string_set(&s1, name);
2122 dictionary_put(&points, s1, (void*)pos);
2125 static int c_play(map_t*args)
2127 char*name = lu(args, "name");
2128 char*loop = lu(args, "loop");
2129 char*nomultiple = lu(args, "nomultiple");
2131 if(!strcmp(nomultiple, "nomultiple"))
2134 nm = parseInt(nomultiple);
2136 if(s_playsound(name, parseInt(loop), nm, 0)) {
2138 } else if(s_swf3action(name, "play")) {
2144 static int c_stop(map_t*args)
2146 char*name = map_lookup(args, "name");
2148 if(s_playsound(name, 0,0,1)) {
2150 } else if(s_swf3action(name, "stop")) {
2153 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2157 static int c_nextframe(map_t*args)
2159 char*name = lu(args, "name");
2161 if(s_swf3action(name, "nextframe")) {
2164 syntaxerror("I don't know anything about movie \"%s\"", name);
2168 static int c_previousframe(map_t*args)
2170 char*name = lu(args, "name");
2172 if(s_swf3action(name, "previousframe")) {
2175 syntaxerror("I don't know anything about movie \"%s\"", name);
2179 static int c_placement(map_t*args, int type)
2181 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2184 char* luminancestr = lu(args, "luminance");
2185 char* scalestr = lu(args, "scale");
2186 char* scalexstr = lu(args, "scalex");
2187 char* scaleystr = lu(args, "scaley");
2188 char* rotatestr = lu(args, "rotate");
2189 char* shearstr = lu(args, "shear");
2190 char* xstr="", *pivotstr="";
2191 char* ystr="", *anglestr="";
2192 char*above = lu(args, "above"); /*FIXME*/
2193 char*below = lu(args, "below");
2194 char* rstr = lu(args, "red");
2195 char* gstr = lu(args, "green");
2196 char* bstr = lu(args, "blue");
2197 char* astr = lu(args, "alpha");
2198 char* pinstr = lu(args, "pin");
2199 char* as = map_lookup(args, "as");
2207 if(type==9) { // (?) .rotate or .arcchange
2208 pivotstr = lu(args, "pivot");
2209 anglestr = lu(args, "angle");
2211 xstr = lu(args, "x");
2212 ystr = lu(args, "y");
2215 luminance = parseMulAdd(luminancestr);
2218 luminance.mul = 256;
2222 if(scalexstr[0]||scaleystr[0])
2223 syntaxerror("scalex/scaley and scale cannot both be set");
2224 scalexstr = scaleystr = scalestr;
2227 if(type == 0 || type == 4) {
2229 character = lu(args, "character");
2230 parameters_clear(&p);
2231 } else if (type == 5) {
2232 character = lu(args, "name");
2233 parameters_clear(&p);
2236 p = s_getParameters(instance);
2241 if(isRelative(xstr)) {
2242 if(type == 0 || type == 4)
2243 syntaxerror("relative x values not allowed for initial put or startclip");
2244 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2246 p.x = parseTwip(xstr);
2250 if(isRelative(ystr)) {
2251 if(type == 0 || type == 4)
2252 syntaxerror("relative y values not allowed for initial put or startclip");
2253 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2255 p.y = parseTwip(ystr);
2259 /* scale, scalex, scaley */
2261 oldbbox = s_getCharBBox(character);
2263 oldbbox = s_getInstanceBBox(instance);
2265 oldwidth = oldbbox.xmax - oldbbox.xmin;
2266 oldheight = oldbbox.ymax - oldbbox.ymin;
2268 if(oldwidth==0) p.scalex = 1.0;
2271 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2275 if(oldheight==0) p.scaley = 1.0;
2278 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2284 if(isRelative(rotatestr)) {
2285 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2287 p.rotate = parseFloat(rotatestr);
2293 if(isRelative(shearstr)) {
2294 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2296 p.shear = parseFloat(shearstr);
2301 if(isPoint(pivotstr))
2302 p.pivot = parsePoint(pivotstr);
2304 p.pivot = getPoint(oldbbox, pivotstr);
2308 p.pin = parsePoint(pinstr);
2310 p.pin = getPoint(oldbbox, pinstr);
2313 /* color transform */
2315 if(rstr[0] || luminancestr[0]) {
2318 r = parseMulAdd(rstr);
2320 r.add = p.cxform.r0;
2321 r.mul = p.cxform.r1;
2323 r = mergeMulAdd(r, luminance);
2324 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2326 if(gstr[0] || luminancestr[0]) {
2329 g = parseMulAdd(gstr);
2331 g.add = p.cxform.g0;
2332 g.mul = p.cxform.g1;
2334 g = mergeMulAdd(g, luminance);
2335 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2337 if(bstr[0] || luminancestr[0]) {
2340 b = parseMulAdd(bstr);
2342 b.add = p.cxform.b0;
2343 b.mul = p.cxform.b1;
2345 b = mergeMulAdd(b, luminance);
2346 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2349 MULADD a = parseMulAdd(astr);
2350 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2354 s_put(instance, character, p);
2356 s_change(instance, p);
2358 s_qchange(instance, p);
2360 s_jump(instance, p);
2362 s_startclip(instance, character, p);
2363 else if(type == 5) {
2365 s_buttonput(character, as, p);
2367 s_buttonput(character, "shape", p);
2372 static int c_put(map_t*args)
2374 c_placement(args, 0);
2377 static int c_change(map_t*args)
2379 c_placement(args, 1);
2382 static int c_qchange(map_t*args)
2384 c_placement(args, 2);
2387 static int c_arcchange(map_t*args)
2389 c_placement(args, 2);
2392 static int c_jump(map_t*args)
2394 c_placement(args, 3);
2397 static int c_startclip(map_t*args)
2399 c_placement(args, 4);
2402 static int c_show(map_t*args)
2404 c_placement(args, 5);
2407 static int c_del(map_t*args)
2409 char*instance = lu(args, "name");
2410 s_delinstance(instance);
2413 static int c_end(map_t*args)
2418 static int c_sprite(map_t*args)
2420 char* name = lu(args, "name");
2424 static int c_frame(map_t*args)
2426 char*framestr = lu(args, "n");
2427 char*cutstr = lu(args, "cut");
2428 char*name = lu(args, "name");
2431 if(strcmp(cutstr, "no"))
2433 if(isRelative(framestr)) {
2434 frame = s_getframe();
2435 if(getSign(framestr)<0)
2436 syntaxerror("relative frame expressions must be positive");
2437 frame += parseInt(getOffset(framestr));
2440 frame = parseInt(framestr);
2441 if(s_getframe() >= frame
2442 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2443 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2445 s_frame(frame, cut, name);
2448 static int c_primitive(map_t*args)
2450 char*name = lu(args, "name");
2451 char*command = lu(args, "commandname");
2452 int width=0, height=0, r=0;
2453 int linewidth = parseTwip(lu(args, "line"));
2454 char*colorstr = lu(args, "color");
2455 RGBA color = parseColor(colorstr);
2456 char*fillstr = lu(args, "fill");
2463 if(!strcmp(command, "circle"))
2465 else if(!strcmp(command, "filled"))
2469 width = parseTwip(lu(args, "width"));
2470 height = parseTwip(lu(args, "height"));
2471 } else if (type==1) {
2472 r = parseTwip(lu(args, "r"));
2473 } else if (type==2) {
2474 outline = lu(args, "outline");
2477 if(!strcmp(fillstr, "fill"))
2479 if(!strcmp(fillstr, "none"))
2481 if(width<0 || height<0 || linewidth<0 || r<0)
2482 syntaxerror("values width, height, line, r must be positive");
2484 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2485 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2486 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2490 static int c_textshape(map_t*args)
2492 char*name = lu(args, "name");
2493 char*text = lu(args, "text");
2494 char*font = lu(args, "font");
2495 float size = parsePxOrPercent(font, lu(args, "size"));
2497 s_textshape(name, font, size, text);
2501 static int c_swf(map_t*args)
2503 char*name = lu(args, "name");
2504 char*filename = lu(args, "filename");
2505 char*command = lu(args, "commandname");
2506 if(!strcmp(command, "shape"))
2507 warning("Please use .swf instead of .shape");
2508 s_includeswf(name, filename);
2512 static int c_font(map_t*args)
2514 char*name = lu(args, "name");
2515 char*filename = lu(args, "filename");
2516 s_font(name, filename);
2520 static int c_sound(map_t*args)
2522 char*name = lu(args, "name");
2523 char*filename = lu(args, "filename");
2524 s_sound(name, filename);
2528 static int c_text(map_t*args)
2530 char*name = lu(args, "name");
2531 char*text = lu(args, "text");
2532 char*font = lu(args, "font");
2533 float size = parsePxOrPercent(font, lu(args, "size"));
2534 RGBA color = parseColor(lu(args, "color"));
2535 s_text(name, font, text, (int)(size*100), color);
2539 static int c_soundtrack(map_t*args)
2544 static int c_quicktime(map_t*args)
2546 char*name = lu(args, "name");
2547 char*url = lu(args, "url");
2548 s_quicktime(name, url);
2552 static int c_image(map_t*args)
2554 char*command = lu(args, "commandname");
2555 char*name = lu(args, "name");
2556 char*filename = lu(args, "filename");
2557 if(!strcmp(command,"jpeg")) {
2558 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2559 s_image(name, "jpeg", filename, quality);
2561 s_image(name, "png", filename, 0);
2566 static int c_outline(map_t*args)
2568 char*name = lu(args, "name");
2569 char*format = lu(args, "format");
2573 syntaxerror("colon (:) expected");
2575 s_outline(name, format, text);
2579 int fakechar(map_t*args)
2581 char*name = lu(args, "name");
2582 s_box(name, 0, 0, black, 20, 0);
2586 static int c_egon(map_t*args) {return fakechar(args);}
2587 static int c_button(map_t*args) {
2588 char*name = lu(args, "name");
2592 static int current_button_flags = 0;
2593 static int c_on_press(map_t*args)
2595 char*position = lu(args, "position");
2597 if(!strcmp(position, "inside")) {
2598 current_button_flags |= BC_OVERUP_OVERDOWN;
2599 } else if(!strcmp(position, "outside")) {
2600 //current_button_flags |= BC_IDLE_OUTDOWN;
2601 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2602 } else if(!strcmp(position, "anywhere")) {
2603 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2606 if(type == RAWDATA) {
2608 s_buttonaction(current_button_flags, action);
2609 current_button_flags = 0;
2615 static int c_on_release(map_t*args)
2617 char*position = lu(args, "position");
2619 if(!strcmp(position, "inside")) {
2620 current_button_flags |= BC_OVERDOWN_OVERUP;
2621 } else if(!strcmp(position, "outside")) {
2622 current_button_flags |= BC_OUTDOWN_IDLE;
2623 } else if(!strcmp(position, "anywhere")) {
2624 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2627 if(type == RAWDATA) {
2629 s_buttonaction(current_button_flags, action);
2630 current_button_flags = 0;
2636 static int c_on_move_in(map_t*args)
2638 char*position = lu(args, "state");
2640 if(!strcmp(position, "pressed")) {
2641 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2642 } else if(!strcmp(position, "not_pressed")) {
2643 current_button_flags |= BC_IDLE_OVERUP;
2644 } else if(!strcmp(position, "any")) {
2645 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2648 if(type == RAWDATA) {
2650 s_buttonaction(current_button_flags, action);
2651 current_button_flags = 0;
2657 static int c_on_move_out(map_t*args)
2659 char*position = lu(args, "state");
2661 if(!strcmp(position, "pressed")) {
2662 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2663 } else if(!strcmp(position, "not_pressed")) {
2664 current_button_flags |= BC_OVERUP_IDLE;
2665 } else if(!strcmp(position, "any")) {
2666 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2669 if(type == RAWDATA) {
2671 s_buttonaction(current_button_flags, action);
2672 current_button_flags = 0;
2678 static int c_on_key(map_t*args)
2680 char*key = lu(args, "key");
2682 if(strlen(key)==1) {
2685 current_button_flags |= 0x4000 + (key[0]*0x200);
2687 syntaxerror("invalid character: %c"+key[0]);
2692 <ctrl-x> = 0x200*(x-'a')
2696 syntaxerror("invalid key: %s",key);
2699 if(type == RAWDATA) {
2701 s_buttonaction(current_button_flags, action);
2702 current_button_flags = 0;
2709 static int c_edittext(map_t*args)
2711 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2712 char*name = lu(args, "name");
2713 char*font = lu(args, "font");
2714 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2715 int width = parseTwip(lu(args, "width"));
2716 int height = parseTwip(lu(args, "height"));
2717 char*text = lu(args, "text");
2718 RGBA color = parseColor(lu(args, "color"));
2719 int maxlength = parseInt(lu(args, "maxlength"));
2720 char*variable = lu(args, "variable");
2721 char*passwordstr = lu(args, "password");
2722 char*wordwrapstr = lu(args, "wordwrap");
2723 char*multilinestr = lu(args, "multiline");
2724 char*htmlstr = lu(args, "html");
2725 char*noselectstr = lu(args, "noselect");
2726 char*readonlystr = lu(args, "readonly");
2727 char*borderstr = lu(args, "border");
2730 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2731 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2732 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2733 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2734 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2735 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2736 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2738 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2742 static int c_morphshape(map_t*args) {return fakechar(args);}
2743 static int c_movie(map_t*args) {return fakechar(args);}
2745 static char* readfile(const char*filename)
2747 FILE*fi = fopen(filename, "rb");
2751 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2752 fseek(fi, 0, SEEK_END);
2754 fseek(fi, 0, SEEK_SET);
2755 text = rfx_alloc(l+1);
2756 fread(text, l, 1, fi);
2762 static int c_action(map_t*args)
2764 char* filename = map_lookup(args, "filename");
2765 if(!filename ||!*filename) {
2767 if(type != RAWDATA) {
2768 syntaxerror("colon (:) expected");
2772 s_action(readfile(filename));
2778 static int c_initaction(map_t*args)
2780 char* character = lu(args, "name");
2781 char* filename = map_lookup(args, "filename");
2782 if(!filename ||!*filename) {
2784 if(type != RAWDATA) {
2785 syntaxerror("colon (:) expected");
2787 s_initaction(character, text);
2789 s_initaction(character, readfile(filename));
2797 command_func_t* func;
2800 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2801 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2802 // "import" type stuff
2803 {"swf", c_swf, "name filename"},
2804 {"shape", c_swf, "name filename"},
2805 {"jpeg", c_image, "name filename quality=80%"},
2806 {"png", c_image, "name filename"},
2807 {"movie", c_movie, "name filename"},
2808 {"sound", c_sound, "name filename"},
2809 {"font", c_font, "name filename"},
2810 {"soundtrack", c_soundtrack, "filename"},
2811 {"quicktime", c_quicktime, "url"},
2813 // generators of primitives
2815 {"point", c_point, "name x=0 y=0"},
2816 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2817 {"outline", c_outline, "name format=simple"},
2818 {"textshape", c_textshape, "name font size=100% text"},
2820 // character generators
2821 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2822 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2823 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2825 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2826 {"text", c_text, "name text font size=100% color=white"},
2827 {"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"},
2828 {"morphshape", c_morphshape, "name start end"},
2829 {"button", c_button, "name"},
2830 {"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="},
2831 {"on_press", c_on_press, "position=inside"},
2832 {"on_release", c_on_release, "position=anywhere"},
2833 {"on_move_in", c_on_move_in, "state=not_pressed"},
2834 {"on_move_out", c_on_move_out, "state=not_pressed"},
2835 {"on_key", c_on_key, "key=any"},
2838 {"play", c_play, "name loop=0 @nomultiple=0"},
2839 {"stop", c_stop, "name= "},
2840 {"nextframe", c_nextframe, "name"},
2841 {"previousframe", c_previousframe, "name"},
2843 // object placement tags
2844 {"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="},
2845 {"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="},
2846 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2847 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2848 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2849 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2850 {"del", c_del, "name"},
2851 // virtual object placement
2852 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2854 // commands which start a block
2855 //startclip (see above)
2856 {"sprite", c_sprite, "name"},
2857 {"action", c_action, "filename="},
2858 {"initaction", c_initaction, "name filename="},
2864 static map_t parseArguments(char*command, char*pattern)
2880 string_set(&t1, "commandname");
2881 string_set(&t2, command);
2882 map_put(&result, t1, t2);
2884 if(!pattern || !*pattern)
2891 if(!strncmp("<i> ", x, 3)) {
2893 if(type == COMMAND || type == RAWDATA) {
2895 syntaxerror("character name expected");
2897 name[pos].str = "instance";
2899 value[pos].str = text;
2900 value[pos].len = strlen(text);
2904 if(type == ASSIGNMENT)
2907 name[pos].str = "character";
2909 value[pos].str = text;
2910 value[pos].len = strlen(text);
2918 isboolean[pos] = (x[0] =='@');
2931 name[pos].len = d-x;
2936 name[pos].len = e-x;
2937 value[pos].str = e+1;
2938 value[pos].len = d-e-1;
2946 /* for(t=0;t<len;t++) {
2947 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2948 isboolean[t]?"(boolean)":"");
2953 if(type == RAWDATA || type == COMMAND) {
2958 // first, search for boolean arguments
2959 for(pos=0;pos<len;pos++)
2961 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2963 if(type == ASSIGNMENT)
2965 value[pos].str = text;
2966 value[pos].len = strlen(text);
2967 /*printf("setting boolean parameter %s (to %s)\n",
2968 strdup_n(name[pos], namelen[pos]),
2969 strdup_n(value[pos], valuelen[pos]));*/
2974 // second, search for normal arguments
2976 for(pos=0;pos<len;pos++)
2978 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2979 (type != ASSIGNMENT && !set[pos])) {
2981 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2983 if(type == ASSIGNMENT)
2986 value[pos].str = text;
2987 value[pos].len = strlen(text);
2989 printf("setting parameter %s (to %s)\n",
2990 strdup_n(name[pos].str, name[pos].len),
2991 strdup_n(value[pos].str, value[pos].len));
2997 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3001 for(t=0;t<len;t++) {
3002 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3005 for(t=0;t<len;t++) {
3006 if(value[t].str && value[t].str[0] == '*') {
3007 //relative default- take value from some other parameter
3009 for(s=0;s<len;s++) {
3010 if(value[s].len == value[t].len-1 &&
3011 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3012 value[t].str = value[s].str;
3015 if(value[t].str == 0) {
3017 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3021 /* ok, now construct the dictionary from the parameters */
3025 map_put(&result, name[t], value[t]);
3029 static void parseArgumentsForCommand(char*command)
3034 msg("<verbose> parse Command: %s (line %d)", command, line);
3036 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3037 if(!strcmp(arguments[t].command, command)) {
3039 /* ugly hack- will be removed soon (once documentation and .sc generating
3040 utilities have been changed) */
3041 if(!strcmp(command, "swf") && !stackpos) {
3042 warning("Please use .flash instead of .swf- this will be mandatory soon");
3047 args = parseArguments(command, arguments[t].arguments);
3053 syntaxerror("command %s not known", command);
3055 // catch missing .flash directives at the beginning of a file
3056 if(strcmp(command, "flash") && !stackpos)
3058 syntaxerror("No movie defined- use .flash first");
3062 printf(".%s\n", command);fflush(stdout);
3063 map_dump(&args, stdout, "\t");fflush(stdout);
3066 (*arguments[nr].func)(&args);
3068 /*if(!strcmp(command, "button") ||
3069 !strcmp(command, "action")) {
3072 if(type == COMMAND) {
3073 if(!strcmp(text, "end"))
3087 int main (int argc,char ** argv)
3090 processargs(argc, argv);
3091 initLog(0,-1,0,0,-1,verbose);
3094 args_callback_usage(argv[0]);
3098 file = generateTokens(filename);
3100 printf("parser returned error.\n");
3106 while(!noMoreTokens()) {
3109 syntaxerror("command expected");
3110 parseArgumentsForCommand(text);