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 override_outputname = 0;
45 static struct options_t options[] = {
53 int args_callback_option(char*name,char*val)
55 if(!strcmp(name, "V")) {
56 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
59 else if(!strcmp(name, "o")) {
61 override_outputname = 1;
64 else if(!strcmp(name, "v")) {
69 printf("Unknown option: -%s\n", name);
74 int args_callback_longoption(char*name,char*val)
76 return args_long2shortoption(options, name, val);
78 void args_callback_usage(char *name)
81 printf("Usage: %s [-o file.swf] file.sc\n", name);
83 printf("-h , --help Print short help message and exit\n");
84 printf("-V , --version Print version info and exit\n");
85 printf("-v , --verbose Increase verbosity. \n");
86 printf("-o , --output <filename> Set output file to <filename>.\n");
89 int args_callback_command(char*name,char*val)
92 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
99 static struct token_t* file;
108 static void syntaxerror(char*format, ...)
112 va_start(arglist, format);
113 vsprintf(buf, format, arglist);
115 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
119 static void warning(char*format, ...)
123 va_start(arglist, format);
124 vsprintf(buf, format, arglist);
126 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
129 static void readToken()
131 type = file[pos].type;
133 syntaxerror("unexpected end of file");
135 text = file[pos].text;
136 textlen = strlen(text);
137 line = file[pos].line;
138 column = file[pos].column;
140 //printf("---> %d(%s) %s\n", type, type_names[type], text);
143 static void pushBack()
146 if(!pos) syntaxerror("internal error 3");
151 textlen = strlen(text);
154 column = file[p].column;
157 static int noMoreTokens()
159 if(file[pos].type == END)
164 // ------------------------------ swf routines ----------------------------
168 int type; //0=swf, 1=sprite, 2=clip, 3=button
174 /* for sprites (1): */
180 dictionary_t oldinstances;
185 static int stackpos = 0;
187 static dictionary_t characters;
188 static dictionary_t images;
189 static dictionary_t outlines;
190 static dictionary_t gradients;
191 static char idmap[65536];
192 static TAG*tag = 0; //current tag
194 static int id; //current character id
195 static int currentframe; //current frame in current level
196 static SRECT currentrect; //current bounding box in current level
197 static U16 currentdepth;
198 static dictionary_t instances;
199 static dictionary_t fonts;
200 static dictionary_t sounds;
202 typedef struct _parameters {
204 float scalex, scaley;
212 typedef struct _character {
218 typedef struct _instance {
219 character_t*character;
221 parameters_t parameters;
222 TAG* lastTag; //last tag which set the object
223 U16 lastFrame; //frame lastTag is in
226 typedef struct _outline {
231 typedef struct _gradient {
236 static void character_init(character_t*c)
238 memset(c, 0, sizeof(character_t));
240 static character_t* character_new()
243 c = (character_t*)malloc(sizeof(character_t));
247 static void instance_init(instance_t*i)
249 memset(i, 0, sizeof(instance_t));
251 static instance_t* instance_new()
254 c = (instance_t*)malloc(sizeof(instance_t));
259 static void incrementid()
263 syntaxerror("Out of character ids.");
268 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
270 character_t* c = character_new();
272 c->definingTag = ctag;
275 if(dictionary_lookup(&characters, name))
276 syntaxerror("character %s defined twice", name);
277 dictionary_put2(&characters, name, c);
279 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
281 swf_SetString(tag, name);
282 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
285 swf_SetString(tag, name);
287 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
289 character_t* c = character_new();
290 c->definingTag = ctag;
294 if(dictionary_lookup(&images, name))
295 syntaxerror("image %s defined twice", name);
296 dictionary_put2(&images, name, c);
298 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
300 instance_t* i = instance_new();
303 //swf_GetMatrix(0, &i->matrix);
304 if(dictionary_lookup(&instances, name))
305 syntaxerror("object %s defined twice", name);
306 dictionary_put2(&instances, name, i);
310 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)
313 p->scalex = scalex; p->scaley = scaley;
314 p->pin = pin; p->pivot = pivot;
315 p->rotate = rotate; p->cxform = cxform;
319 static void parameters_clear(parameters_t*p)
322 p->scalex = 1.0; p->scaley = 1.0;
325 p->pivot.x = 0; p->pivot.y = 0;
328 swf_GetCXForm(0, &p->cxform, 1);
331 static void makeMatrix(MATRIX*m, parameters_t*p)
340 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
341 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
342 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
343 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
345 m->sx = (int)(sx*65536+0.5);
346 m->r1 = (int)(r1*65536+0.5);
347 m->r0 = (int)(r0*65536+0.5);
348 m->sy = (int)(sy*65536+0.5);
352 h = swf_TurnPoint(p->pin, m);
357 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
362 r = swf_TurnRect(rect, &m);
363 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
364 currentrect.xmax == 0 && currentrect.ymax == 0)
367 swf_ExpandRect2(¤trect, &r);
371 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
373 SWF*swf = (SWF*)malloc(sizeof(SWF));
376 syntaxerror(".swf blocks can't be nested");
378 memset(swf, 0, sizeof(swf));
379 swf->fileVersion = version;
381 swf->frameRate = fps;
382 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
383 swf->compressed = compress;
384 swf_SetRGB(tag,&background);
386 if(stackpos==sizeof(stack)/sizeof(stack[0]))
387 syntaxerror("too many levels of recursion");
389 dictionary_init(&characters);
390 dictionary_init(&images);
391 dictionary_init(&outlines);
392 dictionary_init(&gradients);
393 dictionary_init(&instances);
394 dictionary_init(&fonts);
395 dictionary_init(&sounds);
397 memset(&stack[stackpos], 0, sizeof(stack[0]));
398 stack[stackpos].type = 0;
399 stack[stackpos].filename = strdup(name);
400 stack[stackpos].swf = swf;
401 stack[stackpos].oldframe = -1;
406 memset(¤trect, 0, sizeof(currentrect));
409 memset(idmap, 0, sizeof(idmap));
413 void s_sprite(char*name)
415 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
416 swf_SetU16(tag, id); //id
417 swf_SetU16(tag, 0); //frames
419 memset(&stack[stackpos], 0, sizeof(stack[0]));
420 stack[stackpos].type = 1;
421 stack[stackpos].oldframe = currentframe;
422 stack[stackpos].olddepth = currentdepth;
423 stack[stackpos].oldrect = currentrect;
424 stack[stackpos].oldinstances = instances;
425 stack[stackpos].tag = tag;
426 stack[stackpos].id = id;
427 stack[stackpos].name = strdup(name);
429 /* FIXME: those four fields should be bundled together */
430 dictionary_init(&instances);
433 memset(¤trect, 0, sizeof(currentrect));
439 typedef struct _buttonrecord
447 typedef struct _button
451 buttonrecord_t records[4];
454 static button_t mybutton;
456 void s_button(char*name)
458 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
459 swf_SetU16(tag, id); //id
460 swf_ButtonSetFlags(tag, 0); //menu=no
462 memset(&mybutton, 0, sizeof(mybutton));
464 memset(&stack[stackpos], 0, sizeof(stack[0]));
465 stack[stackpos].type = 3;
466 stack[stackpos].tag = tag;
467 stack[stackpos].id = id;
468 stack[stackpos].name = strdup(name);
469 stack[stackpos].oldrect = currentrect;
470 memset(¤trect, 0, sizeof(currentrect));
475 void s_buttonput(char*character, char*as, parameters_t p)
477 character_t* c = dictionary_lookup(&characters, character);
482 if(!stackpos || (stack[stackpos-1].type != 3)) {
483 syntaxerror(".show may only appear in .button");
486 syntaxerror("character %s not known (in .shape %s)", character, character);
488 if(mybutton.endofshapes) {
489 syntaxerror("a .do may not precede a .show", character, character);
492 m = s_instancepos(c->size, &p);
500 if(*s==',' || *s==0) {
501 if(!strncmp(o,"idle",s-o)) mybutton.records[0]=r;
502 else if(!strncmp(o,"shape",s-o)) mybutton.records[0]=r;
503 else if(!strncmp(o,"hover",s-o)) mybutton.records[1]=r;
504 else if(!strncmp(o,"pressed",s-o)) mybutton.records[2]=r;
505 else if(!strncmp(o,"area",s-o)) mybutton.records[3]=r;
506 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
513 static void setbuttonrecords(TAG*tag)
515 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
516 if(!mybutton.endofshapes) {
519 if(!mybutton.records[3].set) {
520 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
524 if(mybutton.records[t].set) {
525 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
528 swf_SetU8(tag,0); // end of button records
529 mybutton.endofshapes = 1;
533 void s_buttonaction(int flags, char*action)
539 setbuttonrecords(stack[stackpos-1].tag);
541 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
543 syntaxerror("Couldn't compile ActionScript");
546 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
547 swf_ActionSet(stack[stackpos-1].tag, a);
548 mybutton.nr_actions++;
553 static void setactionend(TAG*tag)
555 if(!mybutton.nr_actions) {
556 /* no actions means we didn't have an actionoffset,
557 which means we can't signal the end of the
558 buttonaction records, so, *sigh*, we have
559 to insert a dummy record */
560 swf_SetU16(tag, 0); //offset
561 swf_SetU16(tag, 0); //condition
562 swf_SetU8(tag, 0); //action
566 static void s_endButton()
569 setbuttonrecords(stack[stackpos-1].tag);
570 setactionend(stack[stackpos-1].tag);
573 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
577 tag = stack[stackpos].tag;
578 currentrect = stack[stackpos].oldrect;
580 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
581 free(stack[stackpos].name);
584 TAG* removeFromTo(TAG*from, TAG*to)
586 TAG*save = from->prev;
588 TAG*next = from->next;
596 static void s_endSprite()
598 SRECT r = currentrect;
600 if(stack[stackpos].cut)
601 tag = removeFromTo(stack[stackpos].cut, tag);
605 /* TODO: before clearing, prepend "<spritename>." to names and
606 copy into old instances dict */
607 dictionary_clear(&instances);
609 currentframe = stack[stackpos].oldframe;
610 currentrect = stack[stackpos].oldrect;
611 currentdepth = stack[stackpos].olddepth;
612 instances = stack[stackpos].oldinstances;
614 tag = swf_InsertTag(tag, ST_END);
616 tag = stack[stackpos].tag;
619 syntaxerror("internal error(7)");
621 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
622 free(stack[stackpos].name);
625 static void s_endSWF()
631 if(stack[stackpos].cut)
632 tag = removeFromTo(stack[stackpos].cut, tag);
636 swf = stack[stackpos].swf;
637 filename = stack[stackpos].filename;
639 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
641 tag = swf_InsertTag(tag, ST_END);
643 swf_OptimizeTagOrder(swf);
645 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
646 swf->movieSize = currentrect; /* "autocrop" */
649 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
650 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
651 swf->movieSize.ymax += 20;
654 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
656 syntaxerror("couldn't create output file %s", filename);
659 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
661 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
665 dictionary_clear(&instances);
666 dictionary_clear(&characters);
667 dictionary_clear(&images);
668 dictionary_clear(&outlines);
669 dictionary_clear(&gradients);
670 dictionary_clear(&fonts);
671 dictionary_clear(&sounds);
681 if(stack[stackpos-1].type == 0)
682 syntaxerror("End of file encountered in .flash block");
683 if(stack[stackpos-1].type == 1)
684 syntaxerror("End of file encountered in .sprite block");
685 if(stack[stackpos-1].type == 2)
686 syntaxerror("End of file encountered in .clip block");
695 void s_frame(int nr, int cut)
700 for(t=currentframe;t<nr;t++) {
701 tag = swf_InsertTag(tag, ST_SHOWFRAME);
706 syntaxerror("Can't cut, frame empty");
708 stack[stackpos].cut = tag;
714 int parseColor2(char*str, RGBA*color);
716 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
721 if(texture[0] == '#') {
722 parseColor2(texture, &color);
723 return swf_ShapeAddSolidFillStyle(s, &color);
724 } else if((image = dictionary_lookup(&images, texture))) {
726 swf_GetMatrix(0, &m);
727 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
728 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
731 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
732 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
733 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
735 swf_GetMatrix(0, &m);
736 m.sx = (r->xmax - r->xmin)*2;
737 m.sy = (r->ymax - r->ymin)*2;
738 m.tx = r->xmin + (r->xmax - r->xmin)/2;
739 m.ty = r->ymin + (r->ymax - r->ymin)/2;
740 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
741 } else if (parseColor2(texture, &color)) {
742 return swf_ShapeAddSolidFillStyle(s, &color);
744 syntaxerror("not a color/fillstyle: %s", texture);
749 RGBA black={r:0,g:0,b:0,a:0};
750 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
759 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
761 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
764 fs1 = addFillStyle(s, &r2, texture);
767 r.xmin = r2.xmin-linewidth-linewidth/2;
768 r.ymin = r2.ymin-linewidth-linewidth/2;
769 r.xmax = r2.xmax+linewidth+linewidth/2;
770 r.ymax = r2.ymax+linewidth+linewidth/2;
772 swf_SetShapeHeader(tag,s);
773 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
774 swf_ShapeSetLine(tag,s,width,0);
775 swf_ShapeSetLine(tag,s,0,height);
776 swf_ShapeSetLine(tag,s,-width,0);
777 swf_ShapeSetLine(tag,s,0,-height);
778 swf_ShapeSetEnd(tag);
781 s_addcharacter(name, id, tag, r);
785 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
791 outline = dictionary_lookup(&outlines, outlinename);
793 syntaxerror("outline %s not defined", outlinename);
797 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
799 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
801 fs1 = addFillStyle(s, &r2, texture);
803 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
805 rect.xmin = r2.xmin-linewidth-linewidth/2;
806 rect.ymin = r2.ymin-linewidth-linewidth/2;
807 rect.xmax = r2.xmax+linewidth+linewidth/2;
808 rect.ymax = r2.ymax+linewidth+linewidth/2;
810 swf_SetRect(tag,&rect);
811 swf_SetShapeStyles(tag, s);
812 swf_SetShapeBits(tag, outline->shape); //does not count bits!
813 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
816 s_addcharacter(name, id, tag, rect);
820 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
825 r2.xmin = r2.ymin = 0;
829 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
831 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
833 fs1 = addFillStyle(s, &r2, texture);
835 rect.xmin = r2.xmin-linewidth-linewidth/2;
836 rect.ymin = r2.ymin-linewidth-linewidth/2;
837 rect.xmax = r2.xmax+linewidth+linewidth/2;
838 rect.ymax = r2.ymax+linewidth+linewidth/2;
840 swf_SetRect(tag,&rect);
841 swf_SetShapeHeader(tag,s);
842 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
843 swf_ShapeSetCircle(tag, s, r,r,r,r);
844 swf_ShapeSetEnd(tag);
847 s_addcharacter(name, id, tag, rect);
851 void s_textshape(char*name, char*fontname, float size, char*_text)
854 U8*text = (U8*)_text;
858 font = dictionary_lookup(&fonts, fontname);
860 syntaxerror("font \"%s\" not known!", fontname);
862 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
863 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
864 s_box(name, 0, 0, black, 20, 0);
867 g = font->ascii2glyph[text[0]];
869 outline = malloc(sizeof(outline_t));
870 memset(outline, 0, sizeof(outline_t));
871 outline->shape = font->glyph[g].shape;
872 outline->bbox = font->layout->bounds[g];
876 swf_Shape11DrawerInit(&draw, 0);
877 swf_DrawText(&draw, font, (int)(size*100), _text);
879 outline->shape = swf_ShapeDrawerToShape(&draw);
880 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
884 if(dictionary_lookup(&outlines, name))
885 syntaxerror("outline %s defined twice", name);
886 dictionary_put2(&outlines, name, outline);
889 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
894 font = dictionary_lookup(&fonts, fontname);
896 syntaxerror("font \"%s\" not known!", fontname);
898 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
900 if(!font->numchars) {
901 s_box(name, 0, 0, black, 20, 0);
904 r = swf_SetDefineText(tag, font, &color, text, size);
906 s_addcharacter(name, id, tag, r);
910 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
913 EditTextLayout layout;
916 font = dictionary_lookup(&fonts, fontname);
918 syntaxerror("font \"%s\" not known!", fontname);
919 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
922 layout.leftmargin = 0;
923 layout.rightmargin = 0;
930 swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
932 s_addcharacter(name, id, tag, r);
936 /* type: either "jpeg" or "png"
938 void s_image(char*name, char*type, char*filename, int quality)
940 /* an image is actually two folded: 1st bitmap, 2nd character.
941 Both of them can be used separately */
943 /* step 1: the bitmap */
948 warning("image type \"png\" not supported yet!");
949 s_box(name, 0, 0, black, 20, 0);
954 warning("no jpeg support compiled in");
955 s_box(name, 0, 0, black, 20, 0);
958 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
959 swf_SetU16(tag, imageID);
961 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
962 syntaxerror("Image \"%s\" not found, or contains errors", filename);
965 swf_GetJPEGSize(filename, &width, &height);
972 s_addimage(name, id, tag, r);
977 /* step 2: the character */
978 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
980 swf_ShapeSetBitmapRect(tag, imageID, width, height);
982 s_addcharacter(name, id, tag, r);
986 void dumpSWF(SWF*swf)
988 TAG* tag = swf->firstTag;
989 printf("vvvvvvvvvvvvvvvvvvvvv\n");
991 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
994 printf("^^^^^^^^^^^^^^^^^^^^^\n");
997 void s_font(char*name, char*filename)
1000 font = swf_LoadFont(filename);
1003 warning("Couldn't open font file \"%s\"", filename);
1004 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1005 memset(font, 0, sizeof(SWFFONT));
1006 dictionary_put2(&fonts, name, font);
1012 /* fix the layout. Only needed for old fonts */
1014 for(t=0;t<font->numchars;t++) {
1015 font->glyph[t].advance = 0;
1018 swf_FontCreateLayout(font);
1022 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1023 swf_FontSetDefine2(tag, font);
1026 if(dictionary_lookup(&fonts, name))
1027 syntaxerror("font %s defined twice", name);
1028 dictionary_put2(&fonts, name, font);
1033 typedef struct _sound_t
1039 void s_sound(char*name, char*filename)
1041 struct WAV wav, wav2;
1046 if(!readWAV(filename, &wav)) {
1047 warning("Couldn't read wav file \"%s\"", filename);
1051 convertWAV2mono(&wav, &wav2, 44100);
1052 samples = (U16*)wav2.data;
1053 numsamples = wav2.size/2;
1057 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1058 swf_SetU16(tag, id); //id
1059 swf_SetSoundDefine(tag, samples, numsamples);
1061 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1065 if(dictionary_lookup(&sounds, name))
1066 syntaxerror("sound %s defined twice", name);
1067 dictionary_put2(&sounds, name, sound);
1075 static char* gradient_getToken(const char**p)
1079 while(**p && strchr(" \t\n\r", **p)) {
1083 while(**p && !strchr(" \t\n\r", **p)) {
1086 result = malloc((*p)-start+1);
1087 memcpy(result,start,(*p)-start+1);
1088 result[(*p)-start] = 0;
1092 float parsePercent(char*str);
1093 RGBA parseColor(char*str);
1095 GRADIENT parseGradient(const char*str)
1098 const char* p = str;
1099 memset(&gradient, 0, sizeof(GRADIENT));
1101 char*posstr,*colorstr;
1104 posstr = gradient_getToken(&p);
1107 pos = parsePercent(posstr);
1108 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1109 colorstr = gradient_getToken(&p);
1110 color = parseColor(colorstr);
1111 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1112 warning("gradient record too big- max size is 8, rest ignored");
1115 gradient.ratios[gradient.num] = (int)(pos*255.0);
1116 gradient.rgba[gradient.num] = color;
1124 void s_gradient(char*name, const char*text, int radial)
1126 gradient_t* gradient;
1127 gradient = malloc(sizeof(gradient_t));
1128 memset(gradient, 0, sizeof(gradient_t));
1129 gradient->gradient = parseGradient(text);
1130 gradient->radial = radial;
1132 if(dictionary_lookup(&gradients, name))
1133 syntaxerror("gradient %s defined twice", name);
1134 dictionary_put2(&gradients, name, gradient);
1137 void s_action(const char*text)
1140 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1142 syntaxerror("Couldn't compile ActionScript");
1145 tag = swf_InsertTag(tag, ST_DOACTION);
1147 swf_ActionSet(tag, a);
1152 int s_swf3action(char*name, char*action)
1155 instance_t* object = dictionary_lookup(&instances, name);
1159 a = action_SetTarget(0, name);
1160 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1161 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1162 else if(!strcmp(action, "stop")) a = action_Stop(a);
1163 else if(!strcmp(action, "play")) a = action_Play(a);
1164 a = action_SetTarget(a, "");
1167 tag = swf_InsertTag(tag, ST_DOACTION);
1168 swf_ActionSet(tag, a);
1173 void s_outline(char*name, char*format, char*source)
1182 swf_Shape11DrawerInit(&draw, 0);
1183 draw_string(&draw, source);
1185 shape = swf_ShapeDrawerToShape(&draw);
1186 //shape2 = swf_ShapeToShape2(shape);
1187 //bounds = swf_GetShapeBoundingBox(shape2);
1188 //swf_Shape2Free(shape2);
1189 bounds = swf_ShapeDrawerGetBBox(&draw);
1190 draw.dealloc(&draw);
1192 outline = (outline_t*)malloc(sizeof(outline_t));
1193 memset(outline, 0, sizeof(outline_t));
1194 outline->shape = shape;
1195 outline->bbox = bounds;
1197 if(dictionary_lookup(&outlines, name))
1198 syntaxerror("outline %s defined twice", name);
1199 dictionary_put2(&outlines, name, outline);
1202 int s_playsound(char*name, int loops, int nomultiple, int stop)
1204 sound_t* sound = dictionary_lookup(&sounds, name);
1209 tag = swf_InsertTag(tag, ST_STARTSOUND);
1210 swf_SetU16(tag, sound->id); //id
1211 memset(&info, 0, sizeof(info));
1214 info.nomultiple = nomultiple;
1215 swf_SetSoundInfo(tag, &info);
1219 void s_includeswf(char*name, char*filename)
1227 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1228 f = open(filename,O_RDONLY);
1230 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1231 s_box(name, 0, 0, black, 20, 0);
1234 if (swf_ReadSWF(f,&swf)<0) {
1235 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1236 s_box(name, 0, 0, black, 20, 0);
1241 /* FIXME: The following sets the bounding Box for the character.
1242 It is wrong for two reasons:
1243 a) It may be too small (in case objects in the movie clip at the borders)
1244 b) it may be too big (because the poor movie never got autocropped)
1248 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1249 swf_SetU16(tag, id);
1252 swf_Relocate(&swf, idmap);
1254 ftag = swf.firstTag;
1258 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1259 if(cutout[t] == ftag->id) {
1263 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1265 if(ftag->id == ST_END)
1269 /* We simply dump all tags right after the sprite
1270 header, relying on the fact that swf_OptimizeTagOrder() will
1271 sort things out for us later.
1272 We also rely on the fact that the imported SWF is well-formed.
1274 tag = swf_InsertTag(tag, ftag->id);
1275 swf_SetBlock(tag, ftag->data, ftag->len);
1279 syntaxerror("Included file %s contains errors", filename);
1280 tag = swf_InsertTag(tag, ST_END);
1284 s_addcharacter(name, id, tag, r);
1287 SRECT s_getCharBBox(char*name)
1289 character_t* c = dictionary_lookup(&characters, name);
1290 if(!c) syntaxerror("character '%s' unknown(2)", name);
1293 SRECT s_getInstanceBBox(char*name)
1295 instance_t * i = dictionary_lookup(&instances, name);
1297 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1299 if(!c) syntaxerror("internal error(5)");
1302 parameters_t s_getParameters(char*name)
1304 instance_t * i = dictionary_lookup(&instances, name);
1305 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1306 return i->parameters;
1308 void s_startclip(char*instance, char*character, parameters_t p)
1310 character_t* c = dictionary_lookup(&characters, character);
1314 syntaxerror("character %s not known", character);
1316 i = s_addinstance(instance, c, currentdepth);
1318 m = s_instancepos(i->character->size, &p);
1320 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1321 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1322 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1324 i->lastFrame= currentframe;
1326 stack[stackpos].tag = tag;
1327 stack[stackpos].type = 2;
1336 swf_SetTagPos(stack[stackpos].tag, 0);
1337 swf_GetPlaceObject(stack[stackpos].tag, &p);
1338 p.clipdepth = currentdepth;
1340 swf_ClearTag(stack[stackpos].tag);
1341 swf_SetPlaceObject(stack[stackpos].tag, &p);
1345 void s_put(char*instance, char*character, parameters_t p)
1347 character_t* c = dictionary_lookup(&characters, character);
1351 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1354 i = s_addinstance(instance, c, currentdepth);
1356 m = s_instancepos(i->character->size, &p);
1358 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1359 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1361 i->lastFrame = currentframe;
1365 void s_jump(char*instance, parameters_t p)
1367 instance_t* i = dictionary_lookup(&instances, instance);
1370 syntaxerror("instance %s not known", instance);
1374 m = s_instancepos(i->character->size, &p);
1376 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1377 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1379 i->lastFrame = currentframe;
1382 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1386 if(num==0 || num==1)
1388 ratio = (float)pos/(float)num;
1390 p.x = (p2->x-p1->x)*ratio + p1->x;
1391 p.y = (p2->y-p1->y)*ratio + p1->y;
1392 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1393 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1394 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1395 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1397 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1398 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1399 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1400 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1402 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1403 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1404 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1405 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1407 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1408 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1409 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1410 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1414 void s_change(char*instance, parameters_t p2)
1416 instance_t* i = dictionary_lookup(&instances, instance);
1420 int frame, allframes;
1422 syntaxerror("instance %s not known", instance);
1426 allframes = currentframe - i->lastFrame - 1;
1428 warning(".change ignored. can only .put/.change an object once per frame.");
1432 m = s_instancepos(i->character->size, &p2);
1433 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1434 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1437 /* o.k., we got the start and end point set. Now iterate though all the
1438 tags in between, inserting object changes after each new frame */
1441 if(!t) syntaxerror("internal error(6)");
1443 while(frame < allframes) {
1444 if(t->id == ST_SHOWFRAME) {
1449 p = s_interpolate(&p1, &p2, frame, allframes);
1450 m = s_instancepos(i->character->size, &p); //needed?
1451 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1452 i->lastFrame = currentframe;
1453 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1455 if(frame == allframes)
1460 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1464 void s_delinstance(char*instance)
1466 instance_t* i = dictionary_lookup(&instances, instance);
1468 syntaxerror("instance %s not known", instance);
1470 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1471 swf_SetU16(tag, i->depth);
1472 dictionary_del(&instances, instance);
1475 void s_qchange(char*instance, parameters_t p)
1482 syntaxerror(".end unexpected");
1483 if(stack[stackpos-1].type == 0)
1485 else if(stack[stackpos-1].type == 1)
1487 else if(stack[stackpos-1].type == 2)
1489 else if(stack[stackpos-1].type == 3)
1491 else syntaxerror("internal error 1");
1494 // ------------------------------------------------------------------------
1496 typedef int command_func_t(map_t*args);
1498 SRECT parseBox(char*str)
1501 float xmin, xmax, ymin, ymax;
1502 char*x = strchr(str, 'x');
1504 if(!strcmp(str, "autocrop")) {
1505 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1509 d1 = strchr(x+1, ':');
1511 d2 = strchr(d1+1, ':');
1513 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1517 else if(d1 && !d2) {
1518 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1524 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1529 r.xmin = (SCOORD)(xmin*20);
1530 r.ymin = (SCOORD)(ymin*20);
1531 r.xmax = (SCOORD)(xmax*20);
1532 r.ymax = (SCOORD)(ymax*20);
1535 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1538 float parseFloat(char*str)
1542 int parseInt(char*str)
1547 if(str[0]=='+' || str[0]=='-')
1551 if(str[t]<'0' || str[t]>'9')
1552 syntaxerror("Not an Integer: \"%s\"", str);
1555 int parseTwip(char*str)
1559 if(str[0]=='+' || str[0]=='-') {
1564 dot = strchr(str, '.');
1568 return sign*parseInt(str)*20;
1570 int l=strlen(++dot);
1572 for(s=str;s<dot-1;s++)
1573 if(*s<'0' || *s>'9')
1574 syntaxerror("Not a coordinate: \"%s\"", str);
1576 if(*s<'0' || *s>'9')
1577 syntaxerror("Not a coordinate: \"%s\"", str);
1579 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1580 warning("precision loss: %s converted to twip", str);
1585 return sign*atoi(str)*20;
1587 return sign*atoi(str)*20+atoi(dot)*2;
1589 return sign*atoi(str)*20+atoi(dot)/5;
1594 int isPoint(char*str)
1596 if(strchr(str, '('))
1602 SPOINT parsePoint(char*str)
1606 int l = strlen(str);
1607 char*comma = strchr(str, ',');
1608 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1609 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1610 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1611 p.x = parseTwip(tmp);
1612 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1613 p.y = parseTwip(tmp);
1617 int parseColor2(char*str, RGBA*color)
1619 int l = strlen(str);
1623 struct {unsigned char r,g,b;char*name;} colors[] =
1624 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1625 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1626 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1627 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1628 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1629 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1630 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1631 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1632 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1633 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1634 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1635 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1639 if(str[0]=='#' && (l==7 || l==9)) {
1640 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1642 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1644 color->r = r; color->g = g; color->b = b; color->a = a;
1647 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1648 if(!strcmp(str, colors[t].name)) {
1653 color->r = r; color->g = g; color->b = b; color->a = a;
1659 RGBA parseColor(char*str)
1662 if(!parseColor2(str, &c))
1663 syntaxerror("Expression '%s' is not a color", str);
1667 typedef struct _muladd {
1672 MULADD parseMulAdd(char*str)
1675 char* str2 = (char*)malloc(strlen(str)+5);
1682 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1683 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1684 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1685 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1686 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1687 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1688 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1689 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1690 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1691 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1693 syntaxerror("'%s' is not a valid color transform expression", str);
1695 m.add = (int)(add*256);
1696 m.mul = (int)(mul*256);
1701 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1703 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1704 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1706 if(a<-32768) a=-32768;
1707 if(a>32767) a=32767;
1708 if(m<-32768) m=-32768;
1709 if(m>32767) m=32767;
1715 float parsePercent(char*str)
1717 int l = strlen(str);
1721 return atoi(str)/100.0;
1723 syntaxerror("Expression '%s' is not a percentage", str);
1726 int isPercent(char*str)
1728 return str[strlen(str)-1]=='%';
1730 int parseNewSize(char*str, int size)
1733 return parsePercent(str)*size;
1735 return (int)(atof(str)*20);
1738 int isColor(char*str)
1741 return parseColor2(str, &c);
1744 static char* lu(map_t* args, char*name)
1746 char* value = map_lookup(args, name);
1748 map_dump(args, stdout, "");
1749 syntaxerror("internal error 2: value %s should be set", name);
1754 static int c_flash(map_t*args)
1756 char* name = lu(args, "name");
1757 char* compressstr = lu(args, "compress");
1758 SRECT bbox = parseBox(lu(args, "bbox"));
1759 int version = parseInt(lu(args, "version"));
1760 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1762 RGBA color = parseColor(lu(args, "background"));
1763 if(!strcmp(name, "!default!") || override_outputname)
1766 if(!strcmp(compressstr, "default"))
1767 compress = version==6;
1768 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1770 else if(!strcmp(compressstr, "no"))
1772 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1774 s_swf(name, bbox, version, fps, compress, color);
1777 int isRelative(char*str)
1779 return !strncmp(str, "<plus>", 6) ||
1780 !strncmp(str, "<minus>", 7);
1782 char* getOffset(char*str)
1784 if(!strncmp(str, "<plus>", 6))
1786 if(!strncmp(str, "<minus>", 7))
1788 syntaxerror("internal error (347)");
1791 int getSign(char*str)
1793 if(!strncmp(str, "<plus>", 6))
1795 if(!strncmp(str, "<minus>", 7))
1797 syntaxerror("internal error (348)");
1800 static dictionary_t points;
1801 static mem_t mpoints;
1802 int points_initialized = 0;
1804 SPOINT getPoint(SRECT r, char*name)
1807 if(!strcmp(name, "center")) {
1809 p.x = (r.xmin + r.xmax)/2;
1810 p.y = (r.ymin + r.ymax)/2;
1814 if(points_initialized)
1815 l = (int)dictionary_lookup(&points, name);
1817 syntaxerror("Invalid point: \"%s\".", name);
1820 return *(SPOINT*)&mpoints.buffer[l];
1822 static int c_gradient(map_t*args)
1824 char*name = lu(args, "name");
1825 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1829 syntaxerror("colon (:) expected");
1831 s_gradient(name, text, radial);
1834 static int c_point(map_t*args)
1836 char*name = lu(args, "name");
1840 if(!points_initialized) {
1841 dictionary_init(&points);
1843 points_initialized = 1;
1845 p.x = parseTwip(lu(args, "x"));
1846 p.y = parseTwip(lu(args, "y"));
1847 pos = mem_put(&mpoints, &p, sizeof(p));
1848 string_set(&s1, name);
1850 dictionary_put(&points, s1, (void*)pos);
1853 static int c_play(map_t*args)
1855 char*name = lu(args, "name");
1856 char*loop = lu(args, "loop");
1857 char*nomultiple = lu(args, "nomultiple");
1859 if(!strcmp(nomultiple, "nomultiple"))
1862 nm = parseInt(nomultiple);
1864 if(s_playsound(name, parseInt(loop), nm, 0)) {
1866 } else if(s_swf3action(name, "play")) {
1872 static int c_stop(map_t*args)
1874 char*name = lu(args, "name");
1876 if(s_playsound(name, 0,0,1)) {
1878 } else if(s_swf3action(name, "stop")) {
1881 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1885 static int c_nextframe(map_t*args)
1887 char*name = lu(args, "name");
1889 if(s_swf3action(name, "nextframe")) {
1892 syntaxerror("I don't know anything about movie \"%s\"", name);
1896 static int c_previousframe(map_t*args)
1898 char*name = lu(args, "name");
1900 if(s_swf3action(name, "previousframe")) {
1903 syntaxerror("I don't know anything about movie \"%s\"", name);
1907 static int c_placement(map_t*args, int type)
1909 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1912 char* luminancestr = lu(args, "luminance");
1913 char* scalestr = lu(args, "scale");
1914 char* scalexstr = lu(args, "scalex");
1915 char* scaleystr = lu(args, "scaley");
1916 char* rotatestr = lu(args, "rotate");
1917 char* shearstr = lu(args, "shear");
1918 char* xstr="", *pivotstr="";
1919 char* ystr="", *anglestr="";
1920 char*above = lu(args, "above"); /*FIXME*/
1921 char*below = lu(args, "below");
1922 char* rstr = lu(args, "red");
1923 char* gstr = lu(args, "green");
1924 char* bstr = lu(args, "blue");
1925 char* astr = lu(args, "alpha");
1926 char* pinstr = lu(args, "pin");
1927 char* as = map_lookup(args, "as");
1935 if(type==9) { // (?) .rotate or .arcchange
1936 pivotstr = lu(args, "pivot");
1937 anglestr = lu(args, "angle");
1939 xstr = lu(args, "x");
1940 ystr = lu(args, "y");
1943 luminance = parseMulAdd(luminancestr);
1946 luminance.mul = 256;
1950 if(scalexstr[0]||scaleystr[0])
1951 syntaxerror("scalex/scaley and scale cannot both be set");
1952 scalexstr = scaleystr = scalestr;
1955 if(type == 0 || type == 4) {
1957 character = lu(args, "character");
1958 parameters_clear(&p);
1959 } else if (type == 5) {
1960 character = lu(args, "name");
1961 parameters_clear(&p);
1964 p = s_getParameters(instance);
1969 if(isRelative(xstr)) {
1970 if(type == 0 || type == 4)
1971 syntaxerror("relative x values not allowed for initial put or startclip");
1972 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1974 p.x = parseTwip(xstr);
1978 if(isRelative(ystr)) {
1979 if(type == 0 || type == 4)
1980 syntaxerror("relative y values not allowed for initial put or startclip");
1981 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1983 p.y = parseTwip(ystr);
1987 /* scale, scalex, scaley */
1989 oldbbox = s_getCharBBox(character);
1991 oldbbox = s_getInstanceBBox(instance);
1993 oldwidth = oldbbox.xmax - oldbbox.xmin;
1994 oldheight = oldbbox.ymax - oldbbox.ymin;
1996 if(oldwidth==0) p.scalex = 1.0;
1999 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2003 if(oldheight==0) p.scaley = 1.0;
2006 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2012 if(isRelative(rotatestr)) {
2013 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2015 p.rotate = parseFloat(rotatestr);
2021 if(isRelative(shearstr)) {
2022 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2024 p.shear = parseFloat(shearstr);
2029 if(isPoint(pivotstr))
2030 p.pivot = parsePoint(pivotstr);
2032 p.pivot = getPoint(oldbbox, pivotstr);
2036 p.pin = parsePoint(pinstr);
2038 p.pin = getPoint(oldbbox, pinstr);
2041 /* color transform */
2043 if(rstr[0] || luminancestr[0]) {
2046 r = parseMulAdd(rstr);
2048 r.add = p.cxform.r0;
2049 r.mul = p.cxform.r1;
2051 r = mergeMulAdd(r, luminance);
2052 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2054 if(gstr[0] || luminancestr[0]) {
2057 g = parseMulAdd(gstr);
2059 g.add = p.cxform.g0;
2060 g.mul = p.cxform.g1;
2062 g = mergeMulAdd(g, luminance);
2063 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2065 if(bstr[0] || luminancestr[0]) {
2068 b = parseMulAdd(bstr);
2070 b.add = p.cxform.b0;
2071 b.mul = p.cxform.b1;
2073 b = mergeMulAdd(b, luminance);
2074 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2077 MULADD a = parseMulAdd(astr);
2078 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2082 s_put(instance, character, p);
2084 s_change(instance, p);
2086 s_qchange(instance, p);
2088 s_jump(instance, p);
2090 s_startclip(instance, character, p);
2091 else if(type == 5) {
2093 s_buttonput(character, as, p);
2095 s_buttonput(character, "shape", p);
2100 static int c_put(map_t*args)
2102 c_placement(args, 0);
2105 static int c_change(map_t*args)
2107 c_placement(args, 1);
2110 static int c_qchange(map_t*args)
2112 c_placement(args, 2);
2115 static int c_arcchange(map_t*args)
2117 c_placement(args, 2);
2120 static int c_jump(map_t*args)
2122 c_placement(args, 3);
2125 static int c_startclip(map_t*args)
2127 c_placement(args, 4);
2130 static int c_show(map_t*args)
2132 c_placement(args, 5);
2135 static int c_del(map_t*args)
2137 char*instance = lu(args, "name");
2138 s_delinstance(instance);
2141 static int c_end(map_t*args)
2146 static int c_sprite(map_t*args)
2148 char* name = lu(args, "name");
2152 static int c_frame(map_t*args)
2154 char*framestr = lu(args, "n");
2155 char*cutstr = lu(args, "cut");
2158 if(strcmp(cutstr, "no"))
2160 if(isRelative(framestr)) {
2161 frame = s_getframe();
2162 if(getSign(framestr)<0)
2163 syntaxerror("relative frame expressions must be positive");
2164 frame += parseInt(getOffset(framestr));
2167 frame = parseInt(framestr);
2168 if(s_getframe() >= frame
2169 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2170 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2172 s_frame(frame, cut);
2175 static int c_primitive(map_t*args)
2177 char*name = lu(args, "name");
2178 char*command = lu(args, "commandname");
2179 int width=0, height=0, r=0;
2180 int linewidth = parseTwip(lu(args, "line"));
2181 char*colorstr = lu(args, "color");
2182 RGBA color = parseColor(colorstr);
2183 char*fillstr = lu(args, "fill");
2190 if(!strcmp(command, "circle"))
2192 else if(!strcmp(command, "filled"))
2196 width = parseTwip(lu(args, "width"));
2197 height = parseTwip(lu(args, "height"));
2198 } else if (type==1) {
2199 r = parseTwip(lu(args, "r"));
2200 } else if (type==2) {
2201 outline = lu(args, "outline");
2204 if(!strcmp(fillstr, "fill"))
2206 if(!strcmp(fillstr, "none"))
2208 if(width<0 || height<0 || linewidth<0 || r<0)
2209 syntaxerror("values width, height, line, r must be positive");
2211 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2212 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2213 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2217 static int c_textshape(map_t*args)
2219 char*name = lu(args, "name");
2220 char*text = lu(args, "text");
2221 char*font = lu(args, "font");
2222 float size = parsePercent(lu(args, "size"));
2224 s_textshape(name, font, size, text);
2228 static int c_swf(map_t*args)
2230 char*name = lu(args, "name");
2231 char*filename = lu(args, "filename");
2232 char*command = lu(args, "commandname");
2233 if(!strcmp(command, "shape"))
2234 warning("Please use .swf instead of .shape");
2235 s_includeswf(name, filename);
2239 static int c_font(map_t*args)
2241 char*name = lu(args, "name");
2242 char*filename = lu(args, "filename");
2243 s_font(name, filename);
2247 static int c_sound(map_t*args)
2249 char*name = lu(args, "name");
2250 char*filename = lu(args, "filename");
2251 s_sound(name, filename);
2255 static int c_text(map_t*args)
2257 char*name = lu(args, "name");
2258 char*text = lu(args, "text");
2259 char*font = lu(args, "font");
2260 float size = parsePercent(lu(args, "size"));
2261 RGBA color = parseColor(lu(args, "color"));
2262 s_text(name, font, text, (int)(size*100), color);
2266 static int c_soundtrack(map_t*args)
2271 static int c_image(map_t*args)
2273 char*command = lu(args, "commandname");
2274 char*name = lu(args, "name");
2275 char*filename = lu(args, "filename");
2276 if(!strcmp(command,"jpeg")) {
2277 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2278 s_image(name, "jpeg", filename, quality);
2280 s_image(name, "png", filename, 0);
2285 static int c_outline(map_t*args)
2287 char*name = lu(args, "name");
2288 char*format = lu(args, "format");
2292 syntaxerror("colon (:) expected");
2294 s_outline(name, format, text);
2298 int fakechar(map_t*args)
2300 char*name = lu(args, "name");
2301 s_box(name, 0, 0, black, 20, 0);
2305 static int c_egon(map_t*args) {return fakechar(args);}
2306 static int c_button(map_t*args) {
2307 char*name = lu(args, "name");
2311 static int current_button_flags = 0;
2312 static int c_on_press(map_t*args)
2314 char*position = lu(args, "position");
2316 if(!strcmp(position, "inside")) {
2317 current_button_flags |= BC_OVERUP_OVERDOWN;
2318 } else if(!strcmp(position, "outside")) {
2319 //current_button_flags |= BC_IDLE_OUTDOWN;
2320 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2321 } else if(!strcmp(position, "anywhere")) {
2322 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2325 if(type == RAWDATA) {
2327 s_buttonaction(current_button_flags, action);
2328 current_button_flags = 0;
2334 static int c_on_release(map_t*args)
2336 char*position = lu(args, "position");
2338 if(!strcmp(position, "inside")) {
2339 current_button_flags |= BC_OVERDOWN_OVERUP;
2340 } else if(!strcmp(position, "outside")) {
2341 current_button_flags |= BC_OUTDOWN_IDLE;
2342 } else if(!strcmp(position, "anywhere")) {
2343 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2346 if(type == RAWDATA) {
2348 s_buttonaction(current_button_flags, action);
2349 current_button_flags = 0;
2355 static int c_on_move_in(map_t*args)
2357 char*position = lu(args, "state");
2359 if(!strcmp(position, "pressed")) {
2360 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2361 } else if(!strcmp(position, "not_pressed")) {
2362 current_button_flags |= BC_IDLE_OVERUP;
2363 } else if(!strcmp(position, "any")) {
2364 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2367 if(type == RAWDATA) {
2369 s_buttonaction(current_button_flags, action);
2370 current_button_flags = 0;
2376 static int c_on_move_out(map_t*args)
2378 char*position = lu(args, "state");
2380 if(!strcmp(position, "pressed")) {
2381 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2382 } else if(!strcmp(position, "not_pressed")) {
2383 current_button_flags |= BC_OVERUP_IDLE;
2384 } else if(!strcmp(position, "any")) {
2385 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2388 if(type == RAWDATA) {
2390 s_buttonaction(current_button_flags, action);
2391 current_button_flags = 0;
2397 static int c_on_key(map_t*args)
2399 char*key = lu(args, "key");
2401 if(strlen(key)==1) {
2404 current_button_flags |= 0x4000 + (key[0]*0x200);
2406 syntaxerror("invalid character: %c"+key[0]);
2411 <ctrl-x> = 0x200*(x-'a')
2415 syntaxerror("invalid key: %s",key);
2418 if(type == RAWDATA) {
2420 s_buttonaction(current_button_flags, action);
2421 current_button_flags = 0;
2428 static int c_edittext(map_t*args)
2430 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2431 char*name = lu(args, "name");
2432 char*font = lu(args, "font");
2433 int size = (int)(100*20*parsePercent(lu(args, "size")));
2434 int width = parseTwip(lu(args, "width"));
2435 int height = parseTwip(lu(args, "height"));
2436 char*text = lu(args, "text");
2437 RGBA color = parseColor(lu(args, "color"));
2438 int maxlength = parseInt(lu(args, "maxlength"));
2439 char*variable = lu(args, "variable");
2440 char*passwordstr = lu(args, "password");
2441 char*wordwrapstr = lu(args, "wordwrap");
2442 char*multilinestr = lu(args, "multiline");
2443 char*htmlstr = lu(args, "html");
2444 char*noselectstr = lu(args, "noselect");
2445 char*readonlystr = lu(args, "readonly");
2446 char*borderstr = lu(args, "border");
2449 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2450 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2451 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2452 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2453 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2454 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2455 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2457 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2461 static int c_morphshape(map_t*args) {return fakechar(args);}
2462 static int c_movie(map_t*args) {return fakechar(args);}
2464 static int c_texture(map_t*args) {return 0;}
2466 static int c_action(map_t*args)
2469 if(type != RAWDATA) {
2470 syntaxerror("colon (:) expected");
2480 command_func_t* func;
2483 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2484 {"frame", c_frame, "n=<plus>1 @cut=no"},
2485 // "import" type stuff
2486 {"swf", c_swf, "name filename"},
2487 {"shape", c_swf, "name filename"},
2488 {"jpeg", c_image, "name filename quality=80%"},
2489 {"png", c_image, "name filename"},
2490 {"movie", c_movie, "name filename"},
2491 {"sound", c_sound, "name filename"},
2492 {"font", c_font, "name filename"},
2493 {"soundtrack", c_soundtrack, "filename"},
2495 // generators of primitives
2497 {"point", c_point, "name x=0 y=0"},
2498 {"gradient", c_gradient, "name @radial=0"},
2499 {"outline", c_outline, "name format=simple"},
2500 {"textshape", c_textshape, "name font size=100% text"},
2502 // character generators
2503 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2504 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2505 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2507 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2508 {"text", c_text, "name text font size=100% color=white"},
2509 {"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"},
2510 {"morphshape", c_morphshape, "name start end"},
2511 {"button", c_button, "name"},
2512 {"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="},
2513 {"on_press", c_on_press, "position=inside"},
2514 {"on_release", c_on_release, "position=anywhere"},
2515 {"on_move_in", c_on_move_in, "state=not_pressed"},
2516 {"on_move_out", c_on_move_out, "state=not_pressed"},
2517 {"on_key", c_on_key, "key=any"},
2520 {"play", c_play, "name loop=0 @nomultiple=0"},
2521 {"stop", c_stop, "name"},
2522 {"nextframe", c_nextframe, "name"},
2523 {"previousframe", c_previousframe, "name"},
2525 // object placement tags
2526 {"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="},
2527 {"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="},
2528 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2529 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2530 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2531 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2532 {"del", c_del, "name"},
2533 // virtual object placement
2534 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2536 // commands which start a block
2537 //startclip (see above)
2538 {"sprite", c_sprite, "name"},
2539 {"action", c_action, ""},
2545 static map_t parseArguments(char*command, char*pattern)
2561 string_set(&t1, "commandname");
2562 string_set(&t2, command);
2563 map_put(&result, t1, t2);
2565 if(!pattern || !*pattern)
2572 if(!strncmp("<i> ", x, 3)) {
2574 if(type == COMMAND || type == RAWDATA) {
2576 syntaxerror("character name expected");
2578 name[pos].str = "instance";
2580 value[pos].str = text;
2581 value[pos].len = strlen(text);
2585 if(type == ASSIGNMENT)
2588 name[pos].str = "character";
2590 value[pos].str = text;
2591 value[pos].len = strlen(text);
2599 isboolean[pos] = (x[0] =='@');
2612 name[pos].len = d-x;
2617 name[pos].len = e-x;
2618 value[pos].str = e+1;
2619 value[pos].len = d-e-1;
2627 /* for(t=0;t<len;t++) {
2628 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2629 isboolean[t]?"(boolean)":"");
2634 if(type == RAWDATA || type == COMMAND) {
2639 // first, search for boolean arguments
2640 for(pos=0;pos<len;pos++)
2642 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2644 if(type == ASSIGNMENT)
2646 value[pos].str = text;
2647 value[pos].len = strlen(text);
2648 /*printf("setting boolean parameter %s (to %s)\n",
2649 strdup_n(name[pos], namelen[pos]),
2650 strdup_n(value[pos], valuelen[pos]));*/
2655 // second, search for normal arguments
2657 for(pos=0;pos<len;pos++)
2659 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2660 (type != ASSIGNMENT && !set[pos])) {
2662 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2664 if(type == ASSIGNMENT)
2667 value[pos].str = text;
2668 value[pos].len = strlen(text);
2670 printf("setting parameter %s (to %s)\n",
2671 strdup_n(name[pos].str, name[pos].len),
2672 strdup_n(value[pos].str, value[pos].len));
2678 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2682 for(t=0;t<len;t++) {
2683 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2686 for(t=0;t<len;t++) {
2687 if(value[t].str && value[t].str[0] == '*') {
2688 //relative default- take value from some other parameter
2690 for(s=0;s<len;s++) {
2691 if(value[s].len == value[t].len-1 &&
2692 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2693 value[t].str = value[s].str;
2696 if(value[t].str == 0) {
2698 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2702 /* ok, now construct the dictionary from the parameters */
2706 map_put(&result, name[t], value[t]);
2710 static void parseArgumentsForCommand(char*command)
2715 msg("<verbose> parse Command: %s (line %d)", command, line);
2717 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2718 if(!strcmp(arguments[t].command, command)) {
2720 /* ugly hack- will be removed soon (once documentation and .sc generating
2721 utilities have been changed) */
2722 if(!strcmp(command, "swf") && !stackpos) {
2723 warning("Please use .flash instead of .swf- this will be mandatory soon");
2728 args = parseArguments(command, arguments[t].arguments);
2734 syntaxerror("command %s not known", command);
2736 // catch missing .flash directives at the beginning of a file
2737 if(strcmp(command, "flash") && !stackpos)
2739 syntaxerror("No movie defined- use .flash first");
2743 printf(".%s\n", command);fflush(stdout);
2744 map_dump(&args, stdout, "\t");fflush(stdout);
2747 (*arguments[nr].func)(&args);
2749 /*if(!strcmp(command, "button") ||
2750 !strcmp(command, "action")) {
2753 if(type == COMMAND) {
2754 if(!strcmp(text, "end"))
2768 int main (int argc,char ** argv)
2771 processargs(argc, argv);
2772 initLog(0,-1,0,0,-1,verbose);
2775 args_callback_usage(argv[0]);
2779 file = generateTokens(filename);
2781 printf("parser returned error.\n");
2787 while(!noMoreTokens()) {
2790 syntaxerror("command expected");
2791 parseArgumentsForCommand(text);