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"
37 #include "../lib/png.h"
41 static char * filename = 0;
42 static char * outputname = "output.swf";
43 static int verbose = 2;
44 static int optimize = 0;
45 static int override_outputname = 0;
47 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);
731 swf_SetU8(tag, 1); //make this an anchor
734 if(nr == 0 && currentframe == 0 && name) {
735 tag = swf_InsertTag(tag, ST_FRAMELABEL);
736 swf_SetString(tag, name);
737 swf_SetU8(tag, 1); //make this an anchor
742 syntaxerror("Can't cut, frame empty");
744 stack[stackpos].cut = tag;
750 int parseColor2(char*str, RGBA*color);
752 int addFillStyle(SHAPE*s, SRECT*r, char*name)
759 parseColor2(name, &color);
760 return swf_ShapeAddSolidFillStyle(s, &color);
761 } else if ((texture = dictionary_lookup(&textures, name))) {
762 return swf_ShapeAddFillStyle2(s, &texture->fs);
763 } else if((image = dictionary_lookup(&images, name))) {
765 swf_GetMatrix(0, &m);
766 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
767 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
770 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
771 } else if ((gradient = dictionary_lookup(&gradients, name))) {
775 swf_GetMatrix(0, &rot);
776 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
777 csin = sin(-gradient->rotate*2*3.14159265358979/360);
779 rot.r1 = -csin*65536;
782 r2 = swf_TurnRect(*r, &rot);
783 swf_GetMatrix(0, &m);
784 m.sx = (r2.xmax - r2.xmin)*2*ccos;
785 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
786 m.r0 = (r2.ymax - r2.ymin)*2*csin;
787 m.sy = (r2.ymax - r2.ymin)*2*ccos;
788 m.tx = r->xmin + (r->xmax - r->xmin)/2;
789 m.ty = r->ymin + (r->ymax - r->ymin)/2;
790 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
791 } else if (parseColor2(name, &color)) {
792 return swf_ShapeAddSolidFillStyle(s, &color);
794 syntaxerror("not a color/fillstyle: %s", name);
799 RGBA black={r:0,g:0,b:0,a:0};
800 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
809 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
812 linewidth = linewidth>=20?linewidth-20:0;
813 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
816 fs1 = addFillStyle(s, &r2, texture);
819 r.xmin = r2.xmin-linewidth/2;
820 r.ymin = r2.ymin-linewidth/2;
821 r.xmax = r2.xmax+linewidth/2;
822 r.ymax = r2.ymax+linewidth/2;
824 swf_SetShapeHeader(tag,s);
825 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
826 swf_ShapeSetLine(tag,s,width,0);
827 swf_ShapeSetLine(tag,s,0,height);
828 swf_ShapeSetLine(tag,s,-width,0);
829 swf_ShapeSetLine(tag,s,0,-height);
830 swf_ShapeSetEnd(tag);
833 s_addcharacter(name, id, tag, r);
837 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
843 outline = dictionary_lookup(&outlines, outlinename);
845 syntaxerror("outline %s not defined", outlinename);
849 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
852 linewidth = linewidth>=20?linewidth-20:0;
853 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
856 fs1 = addFillStyle(s, &r2, texture);
859 rect.xmin = r2.xmin-linewidth/2;
860 rect.ymin = r2.ymin-linewidth/2;
861 rect.xmax = r2.xmax+linewidth/2;
862 rect.ymax = r2.ymax+linewidth/2;
864 swf_SetRect(tag,&rect);
865 swf_SetShapeStyles(tag, s);
866 swf_ShapeCountBits(s,0,0);
867 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
868 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
869 swf_SetShapeBits(tag, s);
870 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
873 s_addcharacter(name, id, tag, rect);
877 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
882 r2.xmin = r2.ymin = 0;
886 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
889 linewidth = linewidth>=20?linewidth-20:0;
890 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
893 fs1 = addFillStyle(s, &r2, texture);
895 rect.xmin = r2.xmin-linewidth/2;
896 rect.ymin = r2.ymin-linewidth/2;
897 rect.xmax = r2.xmax+linewidth/2;
898 rect.ymax = r2.ymax+linewidth/2;
900 swf_SetRect(tag,&rect);
901 swf_SetShapeHeader(tag,s);
902 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
903 swf_ShapeSetCircle(tag, s, r,r,r,r);
904 swf_ShapeSetEnd(tag);
907 s_addcharacter(name, id, tag, rect);
911 void s_textshape(char*name, char*fontname, float size, char*_text)
914 U8*text = (U8*)_text;
918 font = dictionary_lookup(&fonts, fontname);
920 syntaxerror("font \"%s\" not known!", fontname);
922 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
923 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
924 s_box(name, 0, 0, black, 20, 0);
927 g = font->ascii2glyph[text[0]];
929 outline = malloc(sizeof(outline_t));
930 memset(outline, 0, sizeof(outline_t));
931 outline->shape = font->glyph[g].shape;
932 outline->bbox = font->layout->bounds[g];
936 swf_Shape11DrawerInit(&draw, 0);
937 swf_DrawText(&draw, font, (int)(size*100), _text);
939 outline->shape = swf_ShapeDrawerToShape(&draw);
940 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
944 if(dictionary_lookup(&outlines, name))
945 syntaxerror("outline %s defined twice", name);
946 dictionary_put2(&outlines, name, outline);
949 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
954 font = dictionary_lookup(&fonts, fontname);
956 syntaxerror("font \"%s\" not known!", fontname);
958 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
960 if(!font->numchars) {
961 s_box(name, 0, 0, black, 20, 0);
964 r = swf_SetDefineText(tag, font, &color, text, size);
966 s_addcharacter(name, id, tag, r);
970 void s_quicktime(char*name, char*url)
975 memset(&r, 0, sizeof(r));
977 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
979 swf_SetString(tag, url);
981 s_addcharacter(name, id, tag, r);
985 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags, int align)
988 EditTextLayout layout;
991 if(fontname && *fontname) {
992 flags |= ET_USEOUTLINES;
993 font = dictionary_lookup(&fonts, fontname);
995 syntaxerror("font \"%s\" not known!", fontname);
997 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
999 layout.align = align;
1000 layout.leftmargin = 0;
1001 layout.rightmargin = 0;
1009 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1011 s_addcharacter(name, id, tag, r);
1015 /* type: either "jpeg" or "png"
1017 void s_image(char*name, char*type, char*filename, int quality)
1019 /* an image is actually two folded: 1st bitmap, 2nd character.
1020 Both of them can be used separately */
1022 /* step 1: the bitmap */
1026 if(!strcmp(type,"jpeg")) {
1027 #ifndef HAVE_LIBJPEG
1028 warning("no jpeg support compiled in");
1029 s_box(name, 0, 0, black, 20, 0);
1032 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1033 swf_SetU16(tag, imageID);
1035 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1036 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1039 swf_GetJPEGSize(filename, &width, &height);
1046 s_addimage(name, id, tag, r);
1049 } else if(!strcmp(type,"png")) {
1051 swf_SetU16(tag, imageID);
1053 getPNG(filename, &width, &height, (unsigned char**)&data);
1056 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1059 /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
1060 tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
1061 swf_SetU16(tag, imageID);
1062 swf_SetLosslessImage(tag, data, width, height);
1068 s_addimage(name, id, tag, r);
1071 warning("image type \"%s\" not supported yet!", type);
1072 s_box(name, 0, 0, black, 20, 0);
1076 /* step 2: the character */
1077 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1078 swf_SetU16(tag, id);
1079 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1081 s_addcharacter(name, id, tag, r);
1085 void s_getBitmapSize(char*name, int*width, int*height)
1087 character_t* image = dictionary_lookup(&images, name);
1088 gradient_t* gradient = dictionary_lookup(&gradients,name);
1090 *width = image->size.xmax;
1091 *height = image->size.ymax;
1095 /* internal SWF gradient size */
1096 if(gradient->radial) {
1105 syntaxerror("No such bitmap/gradient: %s", name);
1108 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1110 gradient_t* gradient = dictionary_lookup(&gradients, object);
1111 character_t* bitmap = dictionary_lookup(&images, object);
1112 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1114 FILLSTYLE*fs = &texture->fs;
1117 fs->type = FILL_TILED;
1118 fs->id_bitmap = bitmap->id;
1119 } else if(gradient) {
1120 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1121 fs->gradient = gradient->gradient;
1123 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1124 makeMatrix(&fs->m, &p);
1125 if(gradient && !gradient->radial) {
1132 p2 = swf_TurnPoint(p1, &m);
1137 if(dictionary_lookup(&textures, name))
1138 syntaxerror("texture %s defined twice", name);
1139 dictionary_put2(&textures, name, texture);
1142 void dumpSWF(SWF*swf)
1144 TAG* tag = swf->firstTag;
1145 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1147 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1150 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1153 void s_font(char*name, char*filename)
1156 font = swf_LoadFont(filename);
1159 warning("Couldn't open font file \"%s\"", filename);
1160 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1161 memset(font, 0, sizeof(SWFFONT));
1162 dictionary_put2(&fonts, name, font);
1168 /* fix the layout. Only needed for old fonts */
1170 for(t=0;t<font->numchars;t++) {
1171 font->glyph[t].advance = 0;
1174 swf_FontCreateLayout(font);
1176 /* just in case this thing is used in .edittext later on */
1177 swf_FontPrepareForEditText(font);
1180 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1181 swf_FontSetDefine2(tag, font);
1182 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1184 swf_SetU16(tag, id);
1185 swf_SetString(tag, name);
1188 if(dictionary_lookup(&fonts, name))
1189 syntaxerror("font %s defined twice", name);
1190 dictionary_put2(&fonts, name, font);
1195 typedef struct _sound_t
1201 void s_sound(char*name, char*filename)
1203 struct WAV wav, wav2;
1208 int blocksize = 1152;
1210 if(!readWAV(filename, &wav)) {
1211 warning("Couldn't read wav file \"%s\"", filename);
1215 convertWAV2mono(&wav, &wav2, 44100);
1216 samples = (U16*)wav2.data;
1217 numsamples = wav2.size/2;
1219 #ifdef WORDS_BIGENDIAN
1221 for(t=0;t<numsamples;t++) {
1222 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1227 if(numsamples%blocksize != 0)
1229 // apply padding, so that block is a multiple of blocksize
1230 int numblocks = (numsamples+blocksize-1)/blocksize;
1233 numsamples2 = numblocks * blocksize;
1234 samples2 = malloc(sizeof(U16)*numsamples2);
1235 memcpy(samples2, samples, numsamples*sizeof(U16));
1236 memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1237 numsamples = numsamples2;
1241 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1242 swf_SetU16(tag, id); //id
1243 swf_SetSoundDefine(tag, samples, numsamples);
1245 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1249 if(dictionary_lookup(&sounds, name))
1250 syntaxerror("sound %s defined twice", name);
1251 dictionary_put2(&sounds, name, sound);
1259 static char* gradient_getToken(const char**p)
1263 while(**p && strchr(" \t\n\r", **p)) {
1267 while(**p && !strchr(" \t\n\r", **p)) {
1270 result = malloc((*p)-start+1);
1271 memcpy(result,start,(*p)-start+1);
1272 result[(*p)-start] = 0;
1276 float parsePercent(char*str);
1277 RGBA parseColor(char*str);
1279 GRADIENT parseGradient(const char*str)
1283 const char* p = str;
1284 memset(&gradient, 0, sizeof(GRADIENT));
1286 char*posstr,*colorstr;
1289 posstr = gradient_getToken(&p);
1292 pos = (int)(parsePercent(posstr)*255.0);
1295 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1296 colorstr = gradient_getToken(&p);
1297 color = parseColor(colorstr);
1298 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1299 warning("gradient record too big- max size is 8, rest ignored");
1302 gradient.ratios[gradient.num] = pos;
1303 gradient.rgba[gradient.num] = color;
1312 void s_gradient(char*name, const char*text, int radial, int rotate)
1314 gradient_t* gradient;
1315 gradient = malloc(sizeof(gradient_t));
1316 memset(gradient, 0, sizeof(gradient_t));
1317 gradient->gradient = parseGradient(text);
1318 gradient->radial = radial;
1319 gradient->rotate = rotate;
1321 if(dictionary_lookup(&gradients, name))
1322 syntaxerror("gradient %s defined twice", name);
1323 dictionary_put2(&gradients, name, gradient);
1326 void s_action(const char*text)
1329 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1331 syntaxerror("Couldn't compile ActionScript");
1334 tag = swf_InsertTag(tag, ST_DOACTION);
1336 swf_ActionSet(tag, a);
1341 void s_initaction(const char*character, const char*text)
1345 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1347 syntaxerror("Couldn't compile ActionScript");
1350 c = (character_t*)dictionary_lookup(&characters, character);
1352 tag = swf_InsertTag(tag, ST_DOINITACTION);
1353 swf_SetU16(tag, c->id);
1354 swf_ActionSet(tag, a);
1359 int s_swf3action(char*name, char*action)
1362 instance_t* object = 0;
1364 dictionary_lookup(&instances, name);
1365 if(!object && name && *name) {
1366 /* we have a name, but couldn't find it. Abort. */
1369 a = action_SetTarget(0, name);
1370 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1371 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1372 else if(!strcmp(action, "stop")) a = action_Stop(a);
1373 else if(!strcmp(action, "play")) a = action_Play(a);
1374 a = action_SetTarget(a, "");
1377 tag = swf_InsertTag(tag, ST_DOACTION);
1378 swf_ActionSet(tag, a);
1383 void s_outline(char*name, char*format, char*source)
1392 //swf_Shape10DrawerInit(&draw, 0);
1393 swf_Shape11DrawerInit(&draw, 0);
1395 draw_string(&draw, source);
1397 shape = swf_ShapeDrawerToShape(&draw);
1398 bounds = swf_ShapeDrawerGetBBox(&draw);
1399 draw.dealloc(&draw);
1401 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1402 outline->shape = shape;
1403 outline->bbox = bounds;
1405 if(dictionary_lookup(&outlines, name))
1406 syntaxerror("outline %s defined twice", name);
1407 dictionary_put2(&outlines, name, outline);
1410 int s_playsound(char*name, int loops, int nomultiple, int stop)
1416 sound = dictionary_lookup(&sounds, name);
1420 tag = swf_InsertTag(tag, ST_STARTSOUND);
1421 swf_SetU16(tag, sound->id); //id
1422 memset(&info, 0, sizeof(info));
1425 info.nomultiple = nomultiple;
1426 swf_SetSoundInfo(tag, &info);
1430 void s_includeswf(char*name, char*filename)
1438 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1439 f = open(filename,O_RDONLY|O_BINARY);
1441 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1442 s_box(name, 0, 0, black, 20, 0);
1445 if (swf_ReadSWF(f,&swf)<0) {
1446 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1447 s_box(name, 0, 0, black, 20, 0);
1452 /* FIXME: The following sets the bounding Box for the character.
1453 It is wrong for two reasons:
1454 a) It may be too small (in case objects in the movie clip at the borders)
1455 b) it may be too big (because the poor movie never got autocropped)
1459 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1460 swf_SetU16(tag, id);
1461 swf_SetU16(tag, swf.frameCount);
1463 swf_Relocate(&swf, idmap);
1465 ftag = swf.firstTag;
1469 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1470 if(cutout[t] == ftag->id) {
1474 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1476 if(ftag->id == ST_END)
1480 /* We simply dump all tags right after the sprite
1481 header, relying on the fact that swf_OptimizeTagOrder() will
1482 sort things out for us later.
1483 We also rely on the fact that the imported SWF is well-formed.
1485 tag = swf_InsertTag(tag, ftag->id);
1486 swf_SetBlock(tag, ftag->data, ftag->len);
1490 syntaxerror("Included file %s contains errors", filename);
1491 tag = swf_InsertTag(tag, ST_END);
1495 s_addcharacter(name, id, tag, r);
1498 SRECT s_getCharBBox(char*name)
1500 character_t* c = dictionary_lookup(&characters, name);
1501 if(!c) syntaxerror("character '%s' unknown(2)", name);
1504 SRECT s_getInstanceBBox(char*name)
1506 instance_t * i = dictionary_lookup(&instances, name);
1508 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1510 if(!c) syntaxerror("internal error(5)");
1513 parameters_t s_getParameters(char*name)
1515 instance_t * i = dictionary_lookup(&instances, name);
1516 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1517 return i->parameters;
1519 void s_startclip(char*instance, char*character, parameters_t p)
1521 character_t* c = dictionary_lookup(&characters, character);
1525 syntaxerror("character %s not known", character);
1527 i = s_addinstance(instance, c, currentdepth);
1529 m = s_instancepos(i->character->size, &p);
1531 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1532 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1533 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1535 i->lastFrame= currentframe;
1537 stack[stackpos].tag = tag;
1538 stack[stackpos].type = 2;
1547 swf_SetTagPos(stack[stackpos].tag, 0);
1548 swf_GetPlaceObject(stack[stackpos].tag, &p);
1549 p.clipdepth = currentdepth;
1551 swf_ClearTag(stack[stackpos].tag);
1552 swf_SetPlaceObject(stack[stackpos].tag, &p);
1556 void s_put(char*instance, char*character, parameters_t p)
1558 character_t* c = dictionary_lookup(&characters, character);
1562 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1565 i = s_addinstance(instance, c, currentdepth);
1567 m = s_instancepos(i->character->size, &p);
1569 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1570 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1572 i->lastFrame = currentframe;
1576 void s_jump(char*instance, parameters_t p)
1578 instance_t* i = dictionary_lookup(&instances, instance);
1581 syntaxerror("instance %s not known", instance);
1585 m = s_instancepos(i->character->size, &p);
1587 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1588 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1590 i->lastFrame = currentframe;
1593 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1597 if(num==0 || num==1)
1599 ratio = (float)pos/(float)num;
1601 p.x = (p2->x-p1->x)*ratio + p1->x;
1602 p.y = (p2->y-p1->y)*ratio + p1->y;
1603 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1604 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1605 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1606 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1608 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1609 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1610 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1611 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1613 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1614 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1615 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1616 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1618 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1619 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1620 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1621 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1625 void s_change(char*instance, parameters_t p2)
1627 instance_t* i = dictionary_lookup(&instances, instance);
1631 int frame, allframes;
1633 syntaxerror("instance %s not known", instance);
1637 allframes = currentframe - i->lastFrame - 1;
1639 warning(".change ignored. can only .put/.change an object once per frame.");
1643 m = s_instancepos(i->character->size, &p2);
1644 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1645 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1648 /* o.k., we got the start and end point set. Now iterate though all the
1649 tags in between, inserting object changes after each new frame */
1652 if(!t) syntaxerror("internal error(6)");
1654 while(frame < allframes) {
1655 if(t->id == ST_SHOWFRAME) {
1660 p = s_interpolate(&p1, &p2, frame, allframes);
1661 m = s_instancepos(i->character->size, &p); //needed?
1662 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1663 i->lastFrame = currentframe;
1664 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1666 if(frame == allframes)
1671 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1675 void s_delinstance(char*instance)
1677 instance_t* i = dictionary_lookup(&instances, instance);
1679 syntaxerror("instance %s not known", instance);
1681 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1682 swf_SetU16(tag, i->depth);
1683 dictionary_del(&instances, instance);
1686 void s_qchange(char*instance, parameters_t p)
1693 syntaxerror(".end unexpected");
1694 if(stack[stackpos-1].type == 0)
1696 else if(stack[stackpos-1].type == 1)
1698 else if(stack[stackpos-1].type == 2)
1700 else if(stack[stackpos-1].type == 3)
1702 else syntaxerror("internal error 1");
1705 // ------------------------------------------------------------------------
1707 typedef int command_func_t(map_t*args);
1709 SRECT parseBox(char*str)
1712 float xmin, xmax, ymin, ymax;
1713 char*x = strchr(str, 'x');
1715 if(!strcmp(str, "autocrop")) {
1716 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1720 d1 = strchr(x+1, ':');
1722 d2 = strchr(d1+1, ':');
1724 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1728 else if(d1 && !d2) {
1729 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1735 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1740 r.xmin = (SCOORD)(xmin*20);
1741 r.ymin = (SCOORD)(ymin*20);
1742 r.xmax = (SCOORD)(xmax*20);
1743 r.ymax = (SCOORD)(ymax*20);
1746 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1749 float parseFloat(char*str)
1753 int parseInt(char*str)
1758 if(str[0]=='+' || str[0]=='-')
1762 if(str[t]<'0' || str[t]>'9')
1763 syntaxerror("Not an Integer: \"%s\"", str);
1766 int parseTwip(char*str)
1770 if(str[0]=='+' || str[0]=='-') {
1775 dot = strchr(str, '.');
1779 return sign*parseInt(str)*20;
1781 int l=strlen(++dot);
1783 for(s=str;s<dot-1;s++)
1784 if(*s<'0' || *s>'9')
1785 syntaxerror("Not a coordinate: \"%s\"", str);
1787 if(*s<'0' || *s>'9')
1788 syntaxerror("Not a coordinate: \"%s\"", str);
1790 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1791 warning("precision loss: %s converted to twip: %s", str, dot);
1796 return sign*atoi(str)*20;
1798 return sign*atoi(str)*20+atoi(dot)*2;
1800 return sign*atoi(str)*20+atoi(dot)/5;
1805 int isPoint(char*str)
1807 if(strchr(str, '('))
1813 SPOINT parsePoint(char*str)
1817 int l = strlen(str);
1818 char*comma = strchr(str, ',');
1819 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1820 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1821 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1822 p.x = parseTwip(tmp);
1823 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1824 p.y = parseTwip(tmp);
1828 int parseColor2(char*str, RGBA*color)
1830 int l = strlen(str);
1834 struct {unsigned char r,g,b;char*name;} colors[] =
1835 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1836 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1837 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1838 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1839 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1840 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1841 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1842 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1843 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1844 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1845 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1846 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1850 if(str[0]=='#' && (l==7 || l==9)) {
1851 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1853 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1855 color->r = r; color->g = g; color->b = b; color->a = a;
1858 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1859 if(!strcmp(str, colors[t].name)) {
1864 color->r = r; color->g = g; color->b = b; color->a = a;
1870 RGBA parseColor(char*str)
1873 if(!parseColor2(str, &c))
1874 syntaxerror("Expression '%s' is not a color", str);
1878 typedef struct _muladd {
1883 MULADD parseMulAdd(char*str)
1886 char* str2 = (char*)malloc(strlen(str)+5);
1893 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1894 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1895 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1896 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1897 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1898 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1899 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1900 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1901 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1902 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1904 syntaxerror("'%s' is not a valid color transform expression", str);
1906 m.add = (int)(add*256);
1907 m.mul = (int)(mul*256);
1912 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1914 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1915 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1917 if(a<-32768) a=-32768;
1918 if(a>32767) a=32767;
1919 if(m<-32768) m=-32768;
1920 if(m>32767) m=32767;
1926 float parsePxOrPercent(char*fontname, char*str)
1928 int l = strlen(str);
1929 if(strchr(str, '%'))
1930 return parsePercent(str);
1931 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1932 float p = atof(str);
1933 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1935 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1939 float parsePercent(char*str)
1941 int l = strlen(str);
1945 return atoi(str)/100.0;
1947 syntaxerror("Expression '%s' is not a percentage", str);
1950 int isPercent(char*str)
1952 return str[strlen(str)-1]=='%';
1954 int parseNewSize(char*str, int size)
1957 return parsePercent(str)*size;
1959 return (int)(atof(str)*20);
1962 int isColor(char*str)
1965 return parseColor2(str, &c);
1968 static char* lu(map_t* args, char*name)
1970 char* value = map_lookup(args, name);
1972 map_dump(args, stdout, "");
1973 syntaxerror("internal error 2: value %s should be set", name);
1978 static int c_flash(map_t*args)
1980 char* filename = map_lookup(args, "filename");
1981 char* compressstr = lu(args, "compress");
1982 SRECT bbox = parseBox(lu(args, "bbox"));
1983 int version = parseInt(lu(args, "version"));
1984 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1986 RGBA color = parseColor(lu(args, "background"));
1988 if(!filename || !*filename) {
1989 /* for compatibility */
1990 filename = map_lookup(args, "name");
1991 if(!filename || !*filename) {
1994 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1995 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1999 if(!filename || override_outputname)
2000 filename = outputname;
2002 if(!strcmp(compressstr, "default"))
2003 compress = version==6;
2004 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2006 else if(!strcmp(compressstr, "no"))
2008 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2010 s_swf(filename, bbox, version, fps, compress, color);
2013 int isRelative(char*str)
2015 return !strncmp(str, "<plus>", 6) ||
2016 !strncmp(str, "<minus>", 7);
2018 char* getOffset(char*str)
2020 if(!strncmp(str, "<plus>", 6))
2022 if(!strncmp(str, "<minus>", 7))
2024 syntaxerror("internal error (347)");
2027 int getSign(char*str)
2029 if(!strncmp(str, "<plus>", 6))
2031 if(!strncmp(str, "<minus>", 7))
2033 syntaxerror("internal error (348)");
2036 static dictionary_t points;
2037 static mem_t mpoints;
2038 int points_initialized = 0;
2040 SPOINT getPoint(SRECT r, char*name)
2043 if(!strcmp(name, "center")) {
2045 p.x = (r.xmin + r.xmax)/2;
2046 p.y = (r.ymin + r.ymax)/2;
2050 if(points_initialized)
2051 l = (int)dictionary_lookup(&points, name);
2053 syntaxerror("Invalid point: \"%s\".", name);
2056 return *(SPOINT*)&mpoints.buffer[l];
2059 static int texture2(char*name, char*object, map_t*args, int errors)
2062 char*xstr = map_lookup(args, "x");
2063 char*ystr = map_lookup(args, "y");
2064 char*widthstr = map_lookup(args, "width");
2065 char*heightstr = map_lookup(args, "height");
2066 char*scalestr = map_lookup(args, "scale");
2067 char*scalexstr = map_lookup(args, "scalex");
2068 char*scaleystr = map_lookup(args, "scaley");
2069 char*rotatestr = map_lookup(args, "rotate");
2070 char* shearstr = map_lookup(args, "shear");
2071 char* radiusstr = map_lookup(args, "r");
2073 float scalex = 1.0, scaley = 1.0;
2074 float rotate=0, shear=0;
2076 if(!*xstr && !*ystr) {
2078 syntaxerror("x and y must be set");
2081 if(*scalestr && (*scalexstr || *scaleystr)) {
2082 syntaxerror("scale and scalex/scaley can't both be set");
2085 if((*widthstr || *heightstr) && *radiusstr) {
2086 syntaxerror("width/height and radius can't both be set");
2089 widthstr = radiusstr;
2090 heightstr = radiusstr;
2092 if(!*xstr) xstr="0";
2093 if(!*ystr) ystr="0";
2094 if(!*rotatestr) rotatestr="0";
2095 if(!*shearstr) shearstr="0";
2098 scalex = scaley = parsePercent(scalestr);
2099 } else if(*scalexstr || *scaleystr) {
2100 if(scalexstr) scalex = parsePercent(scalexstr);
2101 if(scaleystr) scaley = parsePercent(scaleystr);
2102 } else if(*widthstr || *heightstr) {
2105 s_getBitmapSize(object, &width, &height);
2107 scalex = (float)parseTwip(widthstr)/(float)width;
2109 scaley = (float)parseTwip(heightstr)/(float)height;
2111 x = parseTwip(xstr);
2112 y = parseTwip(ystr);
2113 rotate = parseFloat(rotatestr);
2114 shear = parseFloat(shearstr);
2116 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2121 static int c_texture(map_t*args)
2123 char*name = lu(args, "instance");
2124 char*object = lu(args, "character");
2125 return texture2(name, object, args, 1);
2128 static int c_gradient(map_t*args)
2130 char*name = lu(args, "name");
2131 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2132 int rotate = parseInt(lu(args, "rotate"));
2136 syntaxerror("colon (:) expected");
2138 s_gradient(name, text, radial, rotate);
2140 /* check whether we also have placement information,
2141 which would make this a positioned gradient.
2142 If there is placement information, texture2() will
2143 add a texture, which has priority over the gradient.
2145 texture2(name, name, args, 0);
2148 static int c_point(map_t*args)
2150 char*name = lu(args, "name");
2154 if(!points_initialized) {
2155 dictionary_init(&points);
2157 points_initialized = 1;
2159 p.x = parseTwip(lu(args, "x"));
2160 p.y = parseTwip(lu(args, "y"));
2161 pos = mem_put(&mpoints, &p, sizeof(p));
2162 string_set(&s1, name);
2164 dictionary_put(&points, s1, (void*)pos);
2167 static int c_play(map_t*args)
2169 char*name = lu(args, "name");
2170 char*loop = lu(args, "loop");
2171 char*nomultiple = lu(args, "nomultiple");
2173 if(!strcmp(nomultiple, "nomultiple"))
2176 nm = parseInt(nomultiple);
2178 if(s_playsound(name, parseInt(loop), nm, 0)) {
2180 } else if(s_swf3action(name, "play")) {
2186 static int c_stop(map_t*args)
2188 char*name = map_lookup(args, "name");
2190 if(s_playsound(name, 0,0,1)) {
2192 } else if(s_swf3action(name, "stop")) {
2195 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2199 static int c_nextframe(map_t*args)
2201 char*name = lu(args, "name");
2203 if(s_swf3action(name, "nextframe")) {
2206 syntaxerror("I don't know anything about movie \"%s\"", name);
2210 static int c_previousframe(map_t*args)
2212 char*name = lu(args, "name");
2214 if(s_swf3action(name, "previousframe")) {
2217 syntaxerror("I don't know anything about movie \"%s\"", name);
2221 static int c_placement(map_t*args, int type)
2223 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2226 char* luminancestr = lu(args, "luminance");
2227 char* scalestr = lu(args, "scale");
2228 char* scalexstr = lu(args, "scalex");
2229 char* scaleystr = lu(args, "scaley");
2230 char* rotatestr = lu(args, "rotate");
2231 char* shearstr = lu(args, "shear");
2232 char* xstr="", *pivotstr="";
2233 char* ystr="", *anglestr="";
2234 char*above = lu(args, "above"); /*FIXME*/
2235 char*below = lu(args, "below");
2236 char* rstr = lu(args, "red");
2237 char* gstr = lu(args, "green");
2238 char* bstr = lu(args, "blue");
2239 char* astr = lu(args, "alpha");
2240 char* pinstr = lu(args, "pin");
2241 char* as = map_lookup(args, "as");
2249 if(type==9) { // (?) .rotate or .arcchange
2250 pivotstr = lu(args, "pivot");
2251 anglestr = lu(args, "angle");
2253 xstr = lu(args, "x");
2254 ystr = lu(args, "y");
2257 luminance = parseMulAdd(luminancestr);
2260 luminance.mul = 256;
2264 if(scalexstr[0]||scaleystr[0])
2265 syntaxerror("scalex/scaley and scale cannot both be set");
2266 scalexstr = scaleystr = scalestr;
2269 if(type == 0 || type == 4) {
2271 character = lu(args, "character");
2272 parameters_clear(&p);
2273 } else if (type == 5) {
2274 character = lu(args, "name");
2275 parameters_clear(&p);
2278 p = s_getParameters(instance);
2283 if(isRelative(xstr)) {
2284 if(type == 0 || type == 4)
2285 syntaxerror("relative x values not allowed for initial put or startclip");
2286 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2288 p.x = parseTwip(xstr);
2292 if(isRelative(ystr)) {
2293 if(type == 0 || type == 4)
2294 syntaxerror("relative y values not allowed for initial put or startclip");
2295 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2297 p.y = parseTwip(ystr);
2301 /* scale, scalex, scaley */
2303 oldbbox = s_getCharBBox(character);
2305 oldbbox = s_getInstanceBBox(instance);
2307 oldwidth = oldbbox.xmax - oldbbox.xmin;
2308 oldheight = oldbbox.ymax - oldbbox.ymin;
2310 if(oldwidth==0) p.scalex = 1.0;
2313 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2317 if(oldheight==0) p.scaley = 1.0;
2320 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2326 if(isRelative(rotatestr)) {
2327 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2329 p.rotate = parseFloat(rotatestr);
2335 if(isRelative(shearstr)) {
2336 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2338 p.shear = parseFloat(shearstr);
2343 if(isPoint(pivotstr))
2344 p.pivot = parsePoint(pivotstr);
2346 p.pivot = getPoint(oldbbox, pivotstr);
2350 p.pin = parsePoint(pinstr);
2352 p.pin = getPoint(oldbbox, pinstr);
2355 /* color transform */
2357 if(rstr[0] || luminancestr[0]) {
2360 r = parseMulAdd(rstr);
2362 r.add = p.cxform.r0;
2363 r.mul = p.cxform.r1;
2365 r = mergeMulAdd(r, luminance);
2366 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2368 if(gstr[0] || luminancestr[0]) {
2371 g = parseMulAdd(gstr);
2373 g.add = p.cxform.g0;
2374 g.mul = p.cxform.g1;
2376 g = mergeMulAdd(g, luminance);
2377 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2379 if(bstr[0] || luminancestr[0]) {
2382 b = parseMulAdd(bstr);
2384 b.add = p.cxform.b0;
2385 b.mul = p.cxform.b1;
2387 b = mergeMulAdd(b, luminance);
2388 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2391 MULADD a = parseMulAdd(astr);
2392 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2396 s_put(instance, character, p);
2398 s_change(instance, p);
2400 s_qchange(instance, p);
2402 s_jump(instance, p);
2404 s_startclip(instance, character, p);
2405 else if(type == 5) {
2407 s_buttonput(character, as, p);
2409 s_buttonput(character, "shape", p);
2414 static int c_put(map_t*args)
2416 c_placement(args, 0);
2419 static int c_change(map_t*args)
2421 c_placement(args, 1);
2424 static int c_qchange(map_t*args)
2426 c_placement(args, 2);
2429 static int c_arcchange(map_t*args)
2431 c_placement(args, 2);
2434 static int c_jump(map_t*args)
2436 c_placement(args, 3);
2439 static int c_startclip(map_t*args)
2441 c_placement(args, 4);
2444 static int c_show(map_t*args)
2446 c_placement(args, 5);
2449 static int c_del(map_t*args)
2451 char*instance = lu(args, "name");
2452 s_delinstance(instance);
2455 static int c_end(map_t*args)
2460 static int c_sprite(map_t*args)
2462 char* name = lu(args, "name");
2466 static int c_frame(map_t*args)
2468 char*framestr = lu(args, "n");
2469 char*cutstr = lu(args, "cut");
2470 char*name = lu(args, "name");
2473 if(strcmp(cutstr, "no"))
2475 if(isRelative(framestr)) {
2476 frame = s_getframe();
2477 if(getSign(framestr)<0)
2478 syntaxerror("relative frame expressions must be positive");
2479 frame += parseInt(getOffset(framestr));
2482 frame = parseInt(framestr);
2483 if(s_getframe() >= frame
2484 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2485 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2487 s_frame(frame, cut, name);
2490 static int c_primitive(map_t*args)
2492 char*name = lu(args, "name");
2493 char*command = lu(args, "commandname");
2494 int width=0, height=0, r=0;
2495 int linewidth = parseTwip(lu(args, "line"));
2496 char*colorstr = lu(args, "color");
2497 RGBA color = parseColor(colorstr);
2498 char*fillstr = lu(args, "fill");
2505 if(!strcmp(command, "circle"))
2507 else if(!strcmp(command, "filled"))
2511 width = parseTwip(lu(args, "width"));
2512 height = parseTwip(lu(args, "height"));
2513 } else if (type==1) {
2514 r = parseTwip(lu(args, "r"));
2515 } else if (type==2) {
2516 outline = lu(args, "outline");
2519 if(!strcmp(fillstr, "fill"))
2521 if(!strcmp(fillstr, "none"))
2523 if(width<0 || height<0 || linewidth<0 || r<0)
2524 syntaxerror("values width, height, line, r must be positive");
2526 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2527 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2528 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2532 static int c_textshape(map_t*args)
2534 char*name = lu(args, "name");
2535 char*text = lu(args, "text");
2536 char*font = lu(args, "font");
2537 float size = parsePxOrPercent(font, lu(args, "size"));
2539 s_textshape(name, font, size, text);
2543 static int c_swf(map_t*args)
2545 char*name = lu(args, "name");
2546 char*filename = lu(args, "filename");
2547 char*command = lu(args, "commandname");
2548 if(!strcmp(command, "shape"))
2549 warning("Please use .swf instead of .shape");
2550 s_includeswf(name, filename);
2554 static int c_font(map_t*args)
2556 char*name = lu(args, "name");
2557 char*filename = lu(args, "filename");
2558 s_font(name, filename);
2562 static int c_sound(map_t*args)
2564 char*name = lu(args, "name");
2565 char*filename = lu(args, "filename");
2566 s_sound(name, filename);
2570 static int c_text(map_t*args)
2572 char*name = lu(args, "name");
2573 char*text = lu(args, "text");
2574 char*font = lu(args, "font");
2575 float size = parsePxOrPercent(font, lu(args, "size"));
2576 RGBA color = parseColor(lu(args, "color"));
2577 s_text(name, font, text, (int)(size*100), color);
2581 static int c_soundtrack(map_t*args)
2586 static int c_quicktime(map_t*args)
2588 char*name = lu(args, "name");
2589 char*url = lu(args, "url");
2590 s_quicktime(name, url);
2594 static int c_image(map_t*args)
2596 char*command = lu(args, "commandname");
2597 char*name = lu(args, "name");
2598 char*filename = lu(args, "filename");
2599 if(!strcmp(command,"jpeg")) {
2600 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2601 s_image(name, "jpeg", filename, quality);
2603 s_image(name, "png", filename, 0);
2608 static int c_outline(map_t*args)
2610 char*name = lu(args, "name");
2611 char*format = lu(args, "format");
2615 syntaxerror("colon (:) expected");
2617 s_outline(name, format, text);
2621 int fakechar(map_t*args)
2623 char*name = lu(args, "name");
2624 s_box(name, 0, 0, black, 20, 0);
2628 static int c_egon(map_t*args) {return fakechar(args);}
2629 static int c_button(map_t*args) {
2630 char*name = lu(args, "name");
2634 static int current_button_flags = 0;
2635 static int c_on_press(map_t*args)
2637 char*position = lu(args, "position");
2639 if(!strcmp(position, "inside")) {
2640 current_button_flags |= BC_OVERUP_OVERDOWN;
2641 } else if(!strcmp(position, "outside")) {
2642 //current_button_flags |= BC_IDLE_OUTDOWN;
2643 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2644 } else if(!strcmp(position, "anywhere")) {
2645 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2648 if(type == RAWDATA) {
2650 s_buttonaction(current_button_flags, action);
2651 current_button_flags = 0;
2657 static int c_on_release(map_t*args)
2659 char*position = lu(args, "position");
2661 if(!strcmp(position, "inside")) {
2662 current_button_flags |= BC_OVERDOWN_OVERUP;
2663 } else if(!strcmp(position, "outside")) {
2664 current_button_flags |= BC_OUTDOWN_IDLE;
2665 } else if(!strcmp(position, "anywhere")) {
2666 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_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_move_in(map_t*args)
2680 char*position = lu(args, "state");
2682 if(!strcmp(position, "pressed")) {
2683 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2684 } else if(!strcmp(position, "not_pressed")) {
2685 current_button_flags |= BC_IDLE_OVERUP;
2686 } else if(!strcmp(position, "any")) {
2687 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2690 if(type == RAWDATA) {
2692 s_buttonaction(current_button_flags, action);
2693 current_button_flags = 0;
2699 static int c_on_move_out(map_t*args)
2701 char*position = lu(args, "state");
2703 if(!strcmp(position, "pressed")) {
2704 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2705 } else if(!strcmp(position, "not_pressed")) {
2706 current_button_flags |= BC_OVERUP_IDLE;
2707 } else if(!strcmp(position, "any")) {
2708 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2711 if(type == RAWDATA) {
2713 s_buttonaction(current_button_flags, action);
2714 current_button_flags = 0;
2720 static int c_on_key(map_t*args)
2722 char*key = lu(args, "key");
2724 if(strlen(key)==1) {
2727 current_button_flags |= 0x4000 + (key[0]*0x200);
2729 syntaxerror("invalid character: %c"+key[0]);
2734 <ctrl-x> = 0x200*(x-'a')
2738 syntaxerror("invalid key: %s",key);
2741 if(type == RAWDATA) {
2743 s_buttonaction(current_button_flags, action);
2744 current_button_flags = 0;
2751 static int c_edittext(map_t*args)
2753 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @autosize=0"},
2754 char*name = lu(args, "name");
2755 char*font = lu(args, "font");
2756 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2757 int width = parseTwip(lu(args, "width"));
2758 int height = parseTwip(lu(args, "height"));
2759 char*text = lu(args, "text");
2760 RGBA color = parseColor(lu(args, "color"));
2761 int maxlength = parseInt(lu(args, "maxlength"));
2762 char*variable = lu(args, "variable");
2763 char*passwordstr = lu(args, "password");
2764 char*wordwrapstr = lu(args, "wordwrap");
2765 char*multilinestr = lu(args, "multiline");
2766 char*htmlstr = lu(args, "html");
2767 char*noselectstr = lu(args, "noselect");
2768 char*readonlystr = lu(args, "readonly");
2769 char*borderstr = lu(args, "border");
2770 char*autosizestr = lu(args, "autosize");
2771 char*alignstr = lu(args, "align");
2775 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2776 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2777 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2778 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2779 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2780 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2781 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2782 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2783 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2784 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2785 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2786 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2787 else syntaxerror("Unknown alignment: %s", alignstr);
2789 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2793 static int c_morphshape(map_t*args) {return fakechar(args);}
2794 static int c_movie(map_t*args) {return fakechar(args);}
2796 static char* readfile(const char*filename)
2798 FILE*fi = fopen(filename, "rb");
2802 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2803 fseek(fi, 0, SEEK_END);
2805 fseek(fi, 0, SEEK_SET);
2806 text = rfx_alloc(l+1);
2807 fread(text, l, 1, fi);
2813 static int c_action(map_t*args)
2815 char* filename = map_lookup(args, "filename");
2816 if(!filename ||!*filename) {
2818 if(type != RAWDATA) {
2819 syntaxerror("colon (:) expected");
2823 s_action(readfile(filename));
2829 static int c_initaction(map_t*args)
2831 char* character = lu(args, "name");
2832 char* filename = map_lookup(args, "filename");
2833 if(!filename ||!*filename) {
2835 if(type != RAWDATA) {
2836 syntaxerror("colon (:) expected");
2838 s_initaction(character, text);
2840 s_initaction(character, readfile(filename));
2848 command_func_t* func;
2851 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2852 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2853 // "import" type stuff
2854 {"swf", c_swf, "name filename"},
2855 {"shape", c_swf, "name filename"},
2856 {"jpeg", c_image, "name filename quality=80%"},
2857 {"png", c_image, "name filename"},
2858 {"movie", c_movie, "name filename"},
2859 {"sound", c_sound, "name filename"},
2860 {"font", c_font, "name filename"},
2861 {"soundtrack", c_soundtrack, "filename"},
2862 {"quicktime", c_quicktime, "url"},
2864 // generators of primitives
2866 {"point", c_point, "name x=0 y=0"},
2867 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2868 {"outline", c_outline, "name format=simple"},
2869 {"textshape", c_textshape, "name font size=100% text"},
2871 // character generators
2872 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2873 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2874 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2876 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2877 {"text", c_text, "name text font size=100% color=white"},
2878 {"edittext", c_edittext, "name font= size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0 @autosize=0 align="},
2879 {"morphshape", c_morphshape, "name start end"},
2880 {"button", c_button, "name"},
2881 {"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="},
2882 {"on_press", c_on_press, "position=inside"},
2883 {"on_release", c_on_release, "position=anywhere"},
2884 {"on_move_in", c_on_move_in, "state=not_pressed"},
2885 {"on_move_out", c_on_move_out, "state=not_pressed"},
2886 {"on_key", c_on_key, "key=any"},
2889 {"play", c_play, "name loop=0 @nomultiple=0"},
2890 {"stop", c_stop, "name= "},
2891 {"nextframe", c_nextframe, "name"},
2892 {"previousframe", c_previousframe, "name"},
2894 // object placement tags
2895 {"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="},
2896 {"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="},
2897 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2898 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2899 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2900 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2901 {"del", c_del, "name"},
2902 // virtual object placement
2903 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2905 // commands which start a block
2906 //startclip (see above)
2907 {"sprite", c_sprite, "name"},
2908 {"action", c_action, "filename="},
2909 {"initaction", c_initaction, "name filename="},
2915 static map_t parseArguments(char*command, char*pattern)
2931 string_set(&t1, "commandname");
2932 string_set(&t2, command);
2933 map_put(&result, t1, t2);
2935 if(!pattern || !*pattern)
2942 if(!strncmp("<i> ", x, 3)) {
2944 if(type == COMMAND || type == RAWDATA) {
2946 syntaxerror("character name expected");
2948 name[pos].str = "instance";
2950 value[pos].str = text;
2951 value[pos].len = strlen(text);
2955 if(type == ASSIGNMENT)
2958 name[pos].str = "character";
2960 value[pos].str = text;
2961 value[pos].len = strlen(text);
2969 isboolean[pos] = (x[0] =='@');
2982 name[pos].len = d-x;
2987 name[pos].len = e-x;
2988 value[pos].str = e+1;
2989 value[pos].len = d-e-1;
2997 /* for(t=0;t<len;t++) {
2998 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2999 isboolean[t]?"(boolean)":"");
3004 if(type == RAWDATA || type == COMMAND) {
3009 // first, search for boolean arguments
3010 for(pos=0;pos<len;pos++)
3012 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3014 if(type == ASSIGNMENT)
3016 value[pos].str = text;
3017 value[pos].len = strlen(text);
3018 /*printf("setting boolean parameter %s (to %s)\n",
3019 strdup_n(name[pos], namelen[pos]),
3020 strdup_n(value[pos], valuelen[pos]));*/
3025 // second, search for normal arguments
3027 for(pos=0;pos<len;pos++)
3029 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3030 (type != ASSIGNMENT && !set[pos])) {
3032 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3034 if(type == ASSIGNMENT)
3037 value[pos].str = text;
3038 value[pos].len = strlen(text);
3040 printf("setting parameter %s (to %s)\n",
3041 strdup_n(name[pos].str, name[pos].len),
3042 strdup_n(value[pos].str, value[pos].len));
3048 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3052 for(t=0;t<len;t++) {
3053 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3056 for(t=0;t<len;t++) {
3057 if(value[t].str && value[t].str[0] == '*') {
3058 //relative default- take value from some other parameter
3060 for(s=0;s<len;s++) {
3061 if(value[s].len == value[t].len-1 &&
3062 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3063 value[t].str = value[s].str;
3066 if(value[t].str == 0) {
3068 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3072 /* ok, now construct the dictionary from the parameters */
3076 map_put(&result, name[t], value[t]);
3080 static void parseArgumentsForCommand(char*command)
3085 msg("<verbose> parse Command: %s (line %d)", command, line);
3087 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3088 if(!strcmp(arguments[t].command, command)) {
3090 /* ugly hack- will be removed soon (once documentation and .sc generating
3091 utilities have been changed) */
3092 if(!strcmp(command, "swf") && !stackpos) {
3093 warning("Please use .flash instead of .swf- this will be mandatory soon");
3098 args = parseArguments(command, arguments[t].arguments);
3104 syntaxerror("command %s not known", command);
3106 // catch missing .flash directives at the beginning of a file
3107 if(strcmp(command, "flash") && !stackpos)
3109 syntaxerror("No movie defined- use .flash first");
3113 printf(".%s\n", command);fflush(stdout);
3114 map_dump(&args, stdout, "\t");fflush(stdout);
3117 (*arguments[nr].func)(&args);
3119 /*if(!strcmp(command, "button") ||
3120 !strcmp(command, "action")) {
3123 if(type == COMMAND) {
3124 if(!strcmp(text, "end"))
3138 int main (int argc,char ** argv)
3141 processargs(argc, argv);
3142 initLog(0,-1,0,0,-1,verbose);
3145 args_callback_usage(argv[0]);
3149 file = generateTokens(filename);
3151 printf("parser returned error.\n");
3157 while(!noMoreTokens()) {
3160 syntaxerror("command expected");
3161 parseArgumentsForCommand(text);