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;
323 p->pin.x = 1; p->pin.y = 0;
324 p->pivot.x = 0; p->pivot.y = 0;
327 swf_GetCXForm(0, &p->cxform, 1);
330 static void makeMatrix(MATRIX*m, parameters_t*p)
339 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
340 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
341 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
342 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
344 m->sx = (int)(sx*65536+0.5);
345 m->r1 = (int)(r1*65536+0.5);
346 m->r0 = (int)(r0*65536+0.5);
347 m->sy = (int)(sy*65536+0.5);
351 h = swf_TurnPoint(p->pin, m);
356 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
361 r = swf_TurnRect(rect, &m);
362 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
363 currentrect.xmax == 0 && currentrect.ymax == 0)
366 swf_ExpandRect2(¤trect, &r);
370 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
372 SWF*swf = (SWF*)malloc(sizeof(SWF));
375 syntaxerror(".swf blocks can't be nested");
377 memset(swf, 0, sizeof(swf));
378 swf->fileVersion = version;
380 swf->frameRate = fps;
381 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
382 swf->compressed = compress;
383 swf_SetRGB(tag,&background);
385 if(stackpos==sizeof(stack)/sizeof(stack[0]))
386 syntaxerror("too many levels of recursion");
388 dictionary_init(&characters);
389 dictionary_init(&images);
390 dictionary_init(&outlines);
391 dictionary_init(&gradients);
392 dictionary_init(&instances);
393 dictionary_init(&fonts);
394 dictionary_init(&sounds);
396 memset(&stack[stackpos], 0, sizeof(stack[0]));
397 stack[stackpos].type = 0;
398 stack[stackpos].filename = strdup(name);
399 stack[stackpos].swf = swf;
400 stack[stackpos].oldframe = -1;
405 memset(¤trect, 0, sizeof(currentrect));
408 memset(idmap, 0, sizeof(idmap));
412 void s_sprite(char*name)
414 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
415 swf_SetU16(tag, id); //id
416 swf_SetU16(tag, 0); //frames
418 memset(&stack[stackpos], 0, sizeof(stack[0]));
419 stack[stackpos].type = 1;
420 stack[stackpos].oldframe = currentframe;
421 stack[stackpos].olddepth = currentdepth;
422 stack[stackpos].oldrect = currentrect;
423 stack[stackpos].oldinstances = instances;
424 stack[stackpos].tag = tag;
425 stack[stackpos].id = id;
426 stack[stackpos].name = strdup(name);
428 /* FIXME: those four fields should be bundled together */
429 dictionary_init(&instances);
432 memset(¤trect, 0, sizeof(currentrect));
438 typedef struct _buttonrecord
446 typedef struct _button
450 buttonrecord_t records[4];
453 static button_t mybutton;
455 void s_button(char*name)
457 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
458 swf_SetU16(tag, id); //id
459 swf_ButtonSetFlags(tag, 0); //menu=no
461 memset(&mybutton, 0, sizeof(mybutton));
463 memset(&stack[stackpos], 0, sizeof(stack[0]));
464 stack[stackpos].type = 3;
465 stack[stackpos].tag = tag;
466 stack[stackpos].id = id;
467 stack[stackpos].name = strdup(name);
468 stack[stackpos].oldrect = currentrect;
469 memset(¤trect, 0, sizeof(currentrect));
474 void s_buttonput(char*character, char*as, parameters_t p)
476 character_t* c = dictionary_lookup(&characters, character);
481 if(!stackpos || (stack[stackpos-1].type != 3)) {
482 syntaxerror(".show may only appear in .button");
485 syntaxerror("character %s not known (in .shape %s)", character, character);
487 if(mybutton.endofshapes) {
488 syntaxerror("a .do may not precede a .show", character, character);
491 m = s_instancepos(c->size, &p);
499 if(*s==',' || *s==0) {
500 if(!strncmp(o,"idle",s-o)) mybutton.records[0]=r;
501 else if(!strncmp(o,"shape",s-o)) mybutton.records[0]=r;
502 else if(!strncmp(o,"hover",s-o)) mybutton.records[1]=r;
503 else if(!strncmp(o,"pressed",s-o)) mybutton.records[2]=r;
504 else if(!strncmp(o,"area",s-o)) mybutton.records[3]=r;
505 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
512 swf_DumpMatrix(stdout,&mybutton.records[0].matrix);
515 static void setbuttonrecords(TAG*tag)
517 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
518 if(!mybutton.endofshapes) {
521 if(!mybutton.records[3].set) {
522 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
526 if(mybutton.records[t].set) {
527 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
530 swf_SetU8(tag,0); // end of button records
531 mybutton.endofshapes = 1;
535 void s_buttonaction(int flags, char*action)
541 setbuttonrecords(stack[stackpos-1].tag);
543 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
545 syntaxerror("Couldn't compile ActionScript");
548 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
549 swf_ActionSet(stack[stackpos-1].tag, a);
550 mybutton.nr_actions++;
555 static void s_endButton()
558 setbuttonrecords(stack[stackpos-1].tag);
561 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
565 tag = stack[stackpos].tag;
566 currentrect = stack[stackpos].oldrect;
568 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
569 free(stack[stackpos].name);
572 TAG* removeFromTo(TAG*from, TAG*to)
574 TAG*save = from->prev;
576 TAG*next = from->next;
584 static void s_endSprite()
586 SRECT r = currentrect;
588 if(stack[stackpos].cut)
589 tag = removeFromTo(stack[stackpos].cut, tag);
593 /* TODO: before clearing, prepend "<spritename>." to names and
594 copy into old instances dict */
595 dictionary_clear(&instances);
597 currentframe = stack[stackpos].oldframe;
598 currentrect = stack[stackpos].oldrect;
599 currentdepth = stack[stackpos].olddepth;
600 instances = stack[stackpos].oldinstances;
602 tag = swf_InsertTag(tag, ST_END);
604 tag = stack[stackpos].tag;
607 syntaxerror("internal error(7)");
609 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
610 free(stack[stackpos].name);
613 static void s_endSWF()
619 if(stack[stackpos].cut)
620 tag = removeFromTo(stack[stackpos].cut, tag);
624 swf = stack[stackpos].swf;
625 filename = stack[stackpos].filename;
627 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
629 tag = swf_InsertTag(tag, ST_END);
631 swf_OptimizeTagOrder(swf);
633 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
634 swf->movieSize = currentrect; /* "autocrop" */
637 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
638 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
639 swf->movieSize.ymax += 20;
642 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
644 syntaxerror("couldn't create output file %s", filename);
647 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
649 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
653 dictionary_clear(&instances);
654 dictionary_clear(&characters);
655 dictionary_clear(&images);
656 dictionary_clear(&outlines);
657 dictionary_clear(&gradients);
658 dictionary_clear(&fonts);
659 dictionary_clear(&sounds);
669 if(stack[stackpos-1].type == 0)
670 syntaxerror("End of file encountered in .flash block");
671 if(stack[stackpos-1].type == 1)
672 syntaxerror("End of file encountered in .sprite block");
673 if(stack[stackpos-1].type == 2)
674 syntaxerror("End of file encountered in .clip block");
683 void s_frame(int nr, int cut)
688 for(t=currentframe;t<nr;t++) {
689 tag = swf_InsertTag(tag, ST_SHOWFRAME);
694 syntaxerror("Can't cut, frame empty");
696 stack[stackpos].cut = tag;
702 int parseColor2(char*str, RGBA*color);
704 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
709 if(texture[0] == '#') {
710 parseColor2(texture, &color);
711 return swf_ShapeAddSolidFillStyle(s, &color);
712 } else if((image = dictionary_lookup(&images, texture))) {
714 swf_GetMatrix(0, &m);
715 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
716 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
719 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
720 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
721 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
723 swf_GetMatrix(0, &m);
724 m.sx = (r->xmax - r->xmin)*2;
725 m.sy = (r->ymax - r->ymin)*2;
726 m.tx = r->xmin + (r->xmax - r->xmin)/2;
727 m.ty = r->ymin + (r->ymax - r->ymin)/2;
728 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
729 } else if (parseColor2(texture, &color)) {
730 return swf_ShapeAddSolidFillStyle(s, &color);
732 syntaxerror("not a color/fillstyle: %s", texture);
737 RGBA black={r:0,g:0,b:0,a:0};
738 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
747 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
749 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
752 fs1 = addFillStyle(s, &r2, texture);
755 r.xmin = r2.xmin-linewidth-linewidth/2;
756 r.ymin = r2.ymin-linewidth-linewidth/2;
757 r.xmax = r2.xmax+linewidth+linewidth/2;
758 r.ymax = r2.ymax+linewidth+linewidth/2;
760 swf_SetShapeHeader(tag,s);
761 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
762 swf_ShapeSetLine(tag,s,width,0);
763 swf_ShapeSetLine(tag,s,0,height);
764 swf_ShapeSetLine(tag,s,-width,0);
765 swf_ShapeSetLine(tag,s,0,-height);
766 swf_ShapeSetEnd(tag);
769 s_addcharacter(name, id, tag, r);
773 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
779 outline = dictionary_lookup(&outlines, outlinename);
781 syntaxerror("outline %s not defined", outlinename);
785 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
787 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
789 fs1 = addFillStyle(s, &r2, texture);
791 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
793 rect.xmin = r2.xmin-linewidth-linewidth/2;
794 rect.ymin = r2.ymin-linewidth-linewidth/2;
795 rect.xmax = r2.xmax+linewidth+linewidth/2;
796 rect.ymax = r2.ymax+linewidth+linewidth/2;
798 swf_SetRect(tag,&rect);
799 swf_SetShapeStyles(tag, s);
800 swf_SetShapeBits(tag, outline->shape); //does not count bits!
801 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
804 s_addcharacter(name, id, tag, rect);
808 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
813 r2.xmin = r2.ymin = 0;
817 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
819 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
821 fs1 = addFillStyle(s, &r2, texture);
823 rect.xmin = r2.xmin-linewidth-linewidth/2;
824 rect.ymin = r2.ymin-linewidth-linewidth/2;
825 rect.xmax = r2.xmax+linewidth+linewidth/2;
826 rect.ymax = r2.ymax+linewidth+linewidth/2;
828 swf_SetRect(tag,&rect);
829 swf_SetShapeHeader(tag,s);
830 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
831 swf_ShapeSetCircle(tag, s, r,r,r,r);
832 swf_ShapeSetEnd(tag);
835 s_addcharacter(name, id, tag, rect);
839 void s_textshape(char*name, char*fontname, char*_text)
842 U8*text = (U8*)_text;
846 font = dictionary_lookup(&fonts, fontname);
848 syntaxerror("font \"%s\" not known!", fontname);
850 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
851 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
852 s_box(name, 0, 0, black, 20, 0);
855 g = font->ascii2glyph[text[0]];
857 outline = malloc(sizeof(outline_t));
858 memset(outline, 0, sizeof(outline_t));
859 outline->shape = font->glyph[g].shape;
860 outline->bbox = font->layout->bounds[g];
864 swf_Shape11DrawerInit(&draw, 0);
865 swf_DrawText(&draw, font, _text);
867 outline->shape = swf_ShapeDrawerToShape(&draw);
868 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
872 if(dictionary_lookup(&outlines, name))
873 syntaxerror("outline %s defined twice", name);
874 dictionary_put2(&outlines, name, outline);
877 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
882 font = dictionary_lookup(&fonts, fontname);
884 syntaxerror("font \"%s\" not known!", fontname);
886 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
888 if(!font->numchars) {
889 s_box(name, 0, 0, black, 20, 0);
892 r = swf_SetDefineText(tag, font, &color, text, size);
894 s_addcharacter(name, id, tag, r);
898 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
901 EditTextLayout layout;
904 font = dictionary_lookup(&fonts, fontname);
906 syntaxerror("font \"%s\" not known!", fontname);
907 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
910 layout.leftmargin = 0;
911 layout.rightmargin = 0;
918 swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
920 s_addcharacter(name, id, tag, r);
924 /* type: either "jpeg" or "png"
926 void s_image(char*name, char*type, char*filename, int quality)
928 /* an image is actually two folded: 1st bitmap, 2nd character.
929 Both of them can be used separately */
931 /* step 1: the bitmap */
936 warning("image type \"png\" not supported yet!");
937 s_box(name, 0, 0, black, 20, 0);
942 warning("no jpeg support compiled in");
943 s_box(name, 0, 0, black, 20, 0);
946 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
947 swf_SetU16(tag, imageID);
949 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
950 syntaxerror("Image \"%s\" not found, or contains errors", filename);
953 swf_GetJPEGSize(filename, &width, &height);
960 s_addimage(name, id, tag, r);
965 /* step 2: the character */
966 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
968 swf_ShapeSetBitmapRect(tag, imageID, width, height);
970 s_addcharacter(name, id, tag, r);
974 void dumpSWF(SWF*swf)
976 TAG* tag = swf->firstTag;
977 printf("vvvvvvvvvvvvvvvvvvvvv\n");
979 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
982 printf("^^^^^^^^^^^^^^^^^^^^^\n");
985 void s_font(char*name, char*filename)
988 font = swf_LoadFont(filename);
991 warning("Couldn't open font file \"%s\"", filename);
992 font = (SWFFONT*)malloc(sizeof(SWFFONT));
993 memset(font, 0, sizeof(SWFFONT));
994 dictionary_put2(&fonts, name, font);
1000 /* fix the layout. Only needed for old fonts */
1002 for(t=0;t<font->numchars;t++) {
1003 font->glyph[t].advance = 0;
1006 swf_FontCreateLayout(font);
1010 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1011 swf_FontSetDefine2(tag, font);
1014 if(dictionary_lookup(&fonts, name))
1015 syntaxerror("font %s defined twice", name);
1016 dictionary_put2(&fonts, name, font);
1021 typedef struct _sound_t
1027 void s_sound(char*name, char*filename)
1029 struct WAV wav, wav2;
1034 if(!readWAV(filename, &wav)) {
1035 warning("Couldn't read wav file \"%s\"", filename);
1039 convertWAV2mono(&wav, &wav2, 44100);
1040 samples = (U16*)wav2.data;
1041 numsamples = wav2.size/2;
1045 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1046 swf_SetU16(tag, id); //id
1047 swf_SetSoundDefine(tag, samples, numsamples);
1049 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1053 if(dictionary_lookup(&sounds, name))
1054 syntaxerror("sound %s defined twice", name);
1055 dictionary_put2(&sounds, name, sound);
1063 static char* gradient_getToken(const char**p)
1067 while(**p && strchr(" \t\n\r", **p)) {
1071 while(**p && !strchr(" \t\n\r", **p)) {
1074 result = malloc((*p)-start+1);
1075 memcpy(result,start,(*p)-start+1);
1076 result[(*p)-start] = 0;
1080 float parsePercent(char*str);
1081 RGBA parseColor(char*str);
1083 GRADIENT parseGradient(const char*str)
1086 const char* p = str;
1087 memset(&gradient, 0, sizeof(GRADIENT));
1089 char*posstr,*colorstr;
1092 posstr = gradient_getToken(&p);
1095 pos = parsePercent(posstr);
1096 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1097 colorstr = gradient_getToken(&p);
1098 color = parseColor(colorstr);
1099 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1100 warning("gradient record too big- max size is 8, rest ignored");
1103 gradient.ratios[gradient.num] = (int)(pos*255.0);
1104 gradient.rgba[gradient.num] = color;
1112 void s_gradient(char*name, const char*text, int radial)
1114 gradient_t* gradient;
1115 gradient = malloc(sizeof(gradient_t));
1116 memset(gradient, 0, sizeof(gradient_t));
1117 gradient->gradient = parseGradient(text);
1118 gradient->radial = radial;
1120 if(dictionary_lookup(&gradients, name))
1121 syntaxerror("gradient %s defined twice", name);
1122 dictionary_put2(&gradients, name, gradient);
1125 void s_action(const char*text)
1128 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1130 syntaxerror("Couldn't compile ActionScript");
1133 tag = swf_InsertTag(tag, ST_DOACTION);
1135 swf_ActionSet(tag, a);
1140 int s_swf3action(char*name, char*action)
1143 instance_t* object = dictionary_lookup(&instances, name);
1147 a = action_SetTarget(0, name);
1148 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1149 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1150 else if(!strcmp(action, "stop")) a = action_Stop(a);
1151 else if(!strcmp(action, "play")) a = action_Play(a);
1152 a = action_SetTarget(a, "");
1155 tag = swf_InsertTag(tag, ST_DOACTION);
1156 swf_ActionSet(tag, a);
1161 void s_outline(char*name, char*format, char*source)
1170 swf_Shape11DrawerInit(&draw, 0);
1171 draw_string(&draw, source);
1173 shape = swf_ShapeDrawerToShape(&draw);
1174 //shape2 = swf_ShapeToShape2(shape);
1175 //bounds = swf_GetShapeBoundingBox(shape2);
1176 //swf_Shape2Free(shape2);
1177 bounds = swf_ShapeDrawerGetBBox(&draw);
1178 draw.dealloc(&draw);
1180 outline = (outline_t*)malloc(sizeof(outline_t));
1181 memset(outline, 0, sizeof(outline_t));
1182 outline->shape = shape;
1183 outline->bbox = bounds;
1185 if(dictionary_lookup(&outlines, name))
1186 syntaxerror("outline %s defined twice", name);
1187 dictionary_put2(&outlines, name, outline);
1190 int s_playsound(char*name, int loops, int nomultiple, int stop)
1192 sound_t* sound = dictionary_lookup(&sounds, name);
1197 tag = swf_InsertTag(tag, ST_STARTSOUND);
1198 swf_SetU16(tag, sound->id); //id
1199 memset(&info, 0, sizeof(info));
1202 info.nomultiple = nomultiple;
1203 swf_SetSoundInfo(tag, &info);
1207 void s_includeswf(char*name, char*filename)
1215 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1216 f = open(filename,O_RDONLY);
1218 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1219 s_box(name, 0, 0, black, 20, 0);
1222 if (swf_ReadSWF(f,&swf)<0) {
1223 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1224 s_box(name, 0, 0, black, 20, 0);
1229 /* FIXME: The following sets the bounding Box for the character.
1230 It is wrong for two reasons:
1231 a) It may be too small (in case objects in the movie clip at the borders)
1232 b) it may be too big (because the poor movie never got autocropped)
1236 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1237 swf_SetU16(tag, id);
1240 swf_Relocate(&swf, idmap);
1242 ftag = swf.firstTag;
1246 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1247 if(cutout[t] == ftag->id) {
1251 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1253 if(ftag->id == ST_END)
1257 /* We simply dump all tags right after the sprite
1258 header, relying on the fact that swf_OptimizeTagOrder() will
1259 sort things out for us later.
1260 We also rely on the fact that the imported SWF is well-formed.
1262 tag = swf_InsertTag(tag, ftag->id);
1263 swf_SetBlock(tag, ftag->data, ftag->len);
1267 syntaxerror("Included file %s contains errors", filename);
1268 tag = swf_InsertTag(tag, ST_END);
1272 s_addcharacter(name, id, tag, r);
1275 SRECT s_getCharBBox(char*name)
1277 character_t* c = dictionary_lookup(&characters, name);
1278 if(!c) syntaxerror("character '%s' unknown(2)", name);
1281 SRECT s_getInstanceBBox(char*name)
1283 instance_t * i = dictionary_lookup(&instances, name);
1285 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1287 if(!c) syntaxerror("internal error(5)");
1290 parameters_t s_getParameters(char*name)
1292 instance_t * i = dictionary_lookup(&instances, name);
1293 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1294 return i->parameters;
1296 void s_startclip(char*instance, char*character, parameters_t p)
1298 character_t* c = dictionary_lookup(&characters, character);
1302 syntaxerror("character %s not known", character);
1304 i = s_addinstance(instance, c, currentdepth);
1306 m = s_instancepos(i->character->size, &p);
1308 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1309 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1310 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1312 i->lastFrame= currentframe;
1314 stack[stackpos].tag = tag;
1315 stack[stackpos].type = 2;
1324 swf_SetTagPos(stack[stackpos].tag, 0);
1325 swf_GetPlaceObject(stack[stackpos].tag, &p);
1326 p.clipdepth = currentdepth;
1327 swf_ClearTag(stack[stackpos].tag);
1328 swf_SetPlaceObject(stack[stackpos].tag, &p);
1332 void s_put(char*instance, char*character, parameters_t p)
1334 character_t* c = dictionary_lookup(&characters, character);
1338 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1341 i = s_addinstance(instance, c, currentdepth);
1343 m = s_instancepos(i->character->size, &p);
1345 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1346 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1348 i->lastFrame = currentframe;
1352 void s_jump(char*instance, parameters_t p)
1354 instance_t* i = dictionary_lookup(&instances, instance);
1357 syntaxerror("instance %s not known", instance);
1361 m = s_instancepos(i->character->size, &p);
1363 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1364 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1366 i->lastFrame = currentframe;
1369 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1373 if(num==0 || num==1)
1375 ratio = (float)pos/(float)num;
1377 p.x = (p2->x-p1->x)*ratio + p1->x;
1378 p.y = (p2->y-p1->y)*ratio + p1->y;
1379 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1380 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1381 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1382 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1384 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1385 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1386 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1387 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1389 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1390 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1391 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1392 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1394 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1395 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1396 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1397 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1401 void s_change(char*instance, parameters_t p2)
1403 instance_t* i = dictionary_lookup(&instances, instance);
1407 int frame, allframes;
1409 syntaxerror("instance %s not known", instance);
1413 allframes = currentframe - i->lastFrame - 1;
1415 warning(".change ignored. can only .put/.change an object once per frame.");
1419 m = s_instancepos(i->character->size, &p2);
1420 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1421 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1424 /* o.k., we got the start and end point set. Now iterate though all the
1425 tags in between, inserting object changes after each new frame */
1428 if(!t) syntaxerror("internal error(6)");
1430 while(frame < allframes) {
1431 if(t->id == ST_SHOWFRAME) {
1436 p = s_interpolate(&p1, &p2, frame, allframes);
1437 m = s_instancepos(i->character->size, &p); //needed?
1438 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1439 i->lastFrame = currentframe;
1440 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1442 if(frame == allframes)
1447 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1451 void s_delinstance(char*instance)
1453 instance_t* i = dictionary_lookup(&instances, instance);
1455 syntaxerror("instance %s not known", instance);
1457 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1458 swf_SetU16(tag, i->depth);
1459 dictionary_del(&instances, instance);
1462 void s_qchange(char*instance, parameters_t p)
1469 syntaxerror(".end unexpected");
1470 if(stack[stackpos-1].type == 0)
1472 else if(stack[stackpos-1].type == 1)
1474 else if(stack[stackpos-1].type == 2)
1476 else if(stack[stackpos-1].type == 3)
1478 else syntaxerror("internal error 1");
1481 // ------------------------------------------------------------------------
1483 typedef int command_func_t(map_t*args);
1485 SRECT parseBox(char*str)
1488 float xmin, xmax, ymin, ymax;
1489 char*x = strchr(str, 'x');
1491 if(!strcmp(str, "autocrop")) {
1492 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1496 d1 = strchr(x+1, ':');
1498 d2 = strchr(d1+1, ':');
1500 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1504 else if(d1 && !d2) {
1505 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1511 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1516 r.xmin = (SCOORD)(xmin*20);
1517 r.ymin = (SCOORD)(ymin*20);
1518 r.xmax = (SCOORD)(xmax*20);
1519 r.ymax = (SCOORD)(ymax*20);
1522 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1525 float parseFloat(char*str)
1529 int parseInt(char*str)
1534 if(str[0]=='+' || str[0]=='-')
1538 if(str[t]<'0' || str[t]>'9')
1539 syntaxerror("Not an Integer: \"%s\"", str);
1542 int parseTwip(char*str)
1546 if(str[0]=='+' || str[0]=='-') {
1551 dot = strchr(str, '.');
1555 return sign*parseInt(str)*20;
1557 int l=strlen(++dot);
1559 for(s=str;s<dot-1;s++)
1560 if(*s<'0' || *s>'9')
1561 syntaxerror("Not a coordinate: \"%s\"", str);
1563 if(*s<'0' || *s>'9')
1564 syntaxerror("Not a coordinate: \"%s\"", str);
1566 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1567 warning("precision loss: %s converted to twip", str);
1572 return sign*atoi(str)*20;
1574 return sign*atoi(str)*20+atoi(dot)*2;
1576 return sign*atoi(str)*20+atoi(dot)/5;
1581 int isPoint(char*str)
1583 if(strchr(str, '('))
1589 SPOINT parsePoint(char*str)
1593 int l = strlen(str);
1594 char*comma = strchr(str, ',');
1595 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1596 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1597 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1598 p.x = parseTwip(tmp);
1599 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1600 p.y = parseTwip(tmp);
1604 int parseColor2(char*str, RGBA*color)
1606 int l = strlen(str);
1610 struct {unsigned char r,g,b;char*name;} colors[] =
1611 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1612 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1613 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1614 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1615 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1616 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1617 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1618 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1619 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1620 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1621 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1622 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1626 if(str[0]=='#' && (l==7 || l==9)) {
1627 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1629 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1631 color->r = r; color->g = g; color->b = b; color->a = a;
1634 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1635 if(!strcmp(str, colors[t].name)) {
1640 color->r = r; color->g = g; color->b = b; color->a = a;
1646 RGBA parseColor(char*str)
1649 if(!parseColor2(str, &c))
1650 syntaxerror("Expression '%s' is not a color", str);
1654 typedef struct _muladd {
1659 MULADD parseMulAdd(char*str)
1662 char* str2 = (char*)malloc(strlen(str)+5);
1669 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1670 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1671 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1672 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1673 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1674 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1675 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1676 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1677 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1678 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1680 syntaxerror("'%s' is not a valid color transform expression", str);
1682 m.add = (int)(add*256);
1683 m.mul = (int)(mul*256);
1688 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1690 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1691 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1693 if(a<-32768) a=-32768;
1694 if(a>32767) a=32767;
1695 if(m<-32768) m=-32768;
1696 if(m>32767) m=32767;
1702 float parsePercent(char*str)
1704 int l = strlen(str);
1708 return atoi(str)/100.0;
1710 syntaxerror("Expression '%s' is not a percentage", str);
1713 int isPercent(char*str)
1715 return str[strlen(str)-1]=='%';
1717 int parseNewSize(char*str, int size)
1720 return parsePercent(str)*size;
1722 return (int)(atof(str)*20);
1725 int isColor(char*str)
1728 return parseColor2(str, &c);
1731 static char* lu(map_t* args, char*name)
1733 char* value = map_lookup(args, name);
1735 map_dump(args, stdout, "");
1736 syntaxerror("internal error 2: value %s should be set", name);
1741 static int c_flash(map_t*args)
1743 char* name = lu(args, "name");
1744 char* compressstr = lu(args, "compress");
1745 SRECT bbox = parseBox(lu(args, "bbox"));
1746 int version = parseInt(lu(args, "version"));
1747 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1749 RGBA color = parseColor(lu(args, "background"));
1750 if(!strcmp(name, "!default!") || override_outputname)
1753 if(!strcmp(compressstr, "default"))
1754 compress = version==6;
1755 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1757 else if(!strcmp(compressstr, "no"))
1759 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1761 s_swf(name, bbox, version, fps, compress, color);
1764 int isRelative(char*str)
1766 return !strncmp(str, "<plus>", 6) ||
1767 !strncmp(str, "<minus>", 7);
1769 char* getOffset(char*str)
1771 if(!strncmp(str, "<plus>", 6))
1773 if(!strncmp(str, "<minus>", 7))
1775 syntaxerror("internal error (347)");
1778 int getSign(char*str)
1780 if(!strncmp(str, "<plus>", 6))
1782 if(!strncmp(str, "<minus>", 7))
1784 syntaxerror("internal error (348)");
1787 static dictionary_t points;
1788 static mem_t mpoints;
1789 int points_initialized = 0;
1791 SPOINT getPoint(SRECT r, char*name)
1794 if(!strcmp(name, "center")) {
1796 p.x = (r.xmin + r.xmax)/2;
1797 p.y = (r.ymin + r.ymax)/2;
1801 if(points_initialized)
1802 l = (int)dictionary_lookup(&points, name);
1804 syntaxerror("Invalid point: \"%s\".", name);
1807 return *(SPOINT*)&mpoints.buffer[l];
1809 static int c_gradient(map_t*args)
1811 char*name = lu(args, "name");
1812 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1816 syntaxerror("colon (:) expected");
1818 s_gradient(name, text, radial);
1821 static int c_point(map_t*args)
1823 char*name = lu(args, "name");
1827 if(!points_initialized) {
1828 dictionary_init(&points);
1830 points_initialized = 1;
1832 p.x = parseTwip(lu(args, "x"));
1833 p.y = parseTwip(lu(args, "y"));
1834 pos = mem_put(&mpoints, &p, sizeof(p));
1835 string_set(&s1, name);
1837 dictionary_put(&points, s1, (void*)pos);
1840 static int c_play(map_t*args)
1842 char*name = lu(args, "name");
1843 char*loop = lu(args, "loop");
1844 char*nomultiple = lu(args, "nomultiple");
1846 if(!strcmp(nomultiple, "nomultiple"))
1849 nm = parseInt(nomultiple);
1851 if(s_playsound(name, parseInt(loop), nm, 0)) {
1853 } else if(s_swf3action(name, "play")) {
1859 static int c_stop(map_t*args)
1861 char*name = lu(args, "name");
1863 if(s_playsound(name, 0,0,1)) {
1865 } else if(s_swf3action(name, "stop")) {
1868 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1872 static int c_nextframe(map_t*args)
1874 char*name = lu(args, "name");
1876 if(s_swf3action(name, "nextframe")) {
1879 syntaxerror("I don't know anything about movie \"%s\"", name);
1883 static int c_previousframe(map_t*args)
1885 char*name = lu(args, "name");
1887 if(s_swf3action(name, "previousframe")) {
1890 syntaxerror("I don't know anything about movie \"%s\"", name);
1894 static int c_placement(map_t*args, int type)
1896 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1899 char* luminancestr = lu(args, "luminance");
1900 char* scalestr = lu(args, "scale");
1901 char* scalexstr = lu(args, "scalex");
1902 char* scaleystr = lu(args, "scaley");
1903 char* rotatestr = lu(args, "rotate");
1904 char* shearstr = lu(args, "shear");
1905 char* xstr="", *pivotstr="";
1906 char* ystr="", *anglestr="";
1907 char*above = lu(args, "above"); /*FIXME*/
1908 char*below = lu(args, "below");
1909 char* rstr = lu(args, "red");
1910 char* gstr = lu(args, "green");
1911 char* bstr = lu(args, "blue");
1912 char* astr = lu(args, "alpha");
1913 char* pinstr = lu(args, "pin");
1914 char* as = map_lookup(args, "as");
1922 if(type==9) { // (?) .rotate or .arcchange
1923 pivotstr = lu(args, "pivot");
1924 anglestr = lu(args, "angle");
1926 xstr = lu(args, "x");
1927 ystr = lu(args, "y");
1930 luminance = parseMulAdd(luminancestr);
1933 luminance.mul = 256;
1937 if(scalexstr[0]||scaleystr[0])
1938 syntaxerror("scalex/scaley and scale cannot both be set");
1939 scalexstr = scaleystr = scalestr;
1942 if(type == 0 || type == 4) {
1944 character = lu(args, "character");
1945 parameters_clear(&p);
1946 } else if (type == 5) {
1947 character = lu(args, "name");
1948 parameters_clear(&p);
1951 p = s_getParameters(instance);
1956 if(isRelative(xstr)) {
1957 if(type == 0 || type == 4)
1958 syntaxerror("relative x values not allowed for initial put or startclip");
1959 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1961 p.x = parseTwip(xstr);
1965 if(isRelative(ystr)) {
1966 if(type == 0 || type == 4)
1967 syntaxerror("relative y values not allowed for initial put or startclip");
1968 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1970 p.y = parseTwip(ystr);
1974 /* scale, scalex, scaley */
1976 oldbbox = s_getCharBBox(character);
1978 oldbbox = s_getInstanceBBox(instance);
1980 oldwidth = oldbbox.xmax - oldbbox.xmin;
1981 oldheight = oldbbox.ymax - oldbbox.ymin;
1983 if(oldwidth==0) p.scalex = 1.0;
1986 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1990 if(oldheight==0) p.scaley = 1.0;
1993 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1999 if(isRelative(rotatestr)) {
2000 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2002 p.rotate = parseFloat(rotatestr);
2008 if(isRelative(shearstr)) {
2009 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2011 p.shear = parseFloat(shearstr);
2016 if(isPoint(pivotstr))
2017 p.pivot = parsePoint(pivotstr);
2019 p.pivot = getPoint(oldbbox, pivotstr);
2023 p.pin = parsePoint(pinstr);
2025 p.pin = getPoint(oldbbox, pinstr);
2028 /* color transform */
2030 if(rstr[0] || luminancestr[0]) {
2033 r = parseMulAdd(rstr);
2035 r.add = p.cxform.r0;
2036 r.mul = p.cxform.r1;
2038 r = mergeMulAdd(r, luminance);
2039 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2041 if(gstr[0] || luminancestr[0]) {
2044 g = parseMulAdd(gstr);
2046 g.add = p.cxform.g0;
2047 g.mul = p.cxform.g1;
2049 g = mergeMulAdd(g, luminance);
2050 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2052 if(bstr[0] || luminancestr[0]) {
2055 b = parseMulAdd(bstr);
2057 b.add = p.cxform.b0;
2058 b.mul = p.cxform.b1;
2060 b = mergeMulAdd(b, luminance);
2061 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2064 MULADD a = parseMulAdd(astr);
2065 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2069 s_put(instance, character, p);
2071 s_change(instance, p);
2073 s_qchange(instance, p);
2075 s_jump(instance, p);
2077 s_startclip(instance, character, p);
2078 else if(type == 5) {
2080 s_buttonput(character, as, p);
2082 s_buttonput(character, "shape", p);
2087 static int c_put(map_t*args)
2089 c_placement(args, 0);
2092 static int c_change(map_t*args)
2094 c_placement(args, 1);
2097 static int c_qchange(map_t*args)
2099 c_placement(args, 2);
2102 static int c_arcchange(map_t*args)
2104 c_placement(args, 2);
2107 static int c_jump(map_t*args)
2109 c_placement(args, 3);
2112 static int c_startclip(map_t*args)
2114 c_placement(args, 4);
2117 static int c_show(map_t*args)
2119 c_placement(args, 5);
2122 static int c_del(map_t*args)
2124 char*instance = lu(args, "name");
2125 s_delinstance(instance);
2128 static int c_end(map_t*args)
2133 static int c_sprite(map_t*args)
2135 char* name = lu(args, "name");
2139 static int c_frame(map_t*args)
2141 char*framestr = lu(args, "n");
2142 char*cutstr = lu(args, "cut");
2145 if(strcmp(cutstr, "no"))
2147 if(isRelative(framestr)) {
2148 frame = s_getframe();
2149 if(getSign(framestr)<0)
2150 syntaxerror("relative frame expressions must be positive");
2151 frame += parseInt(getOffset(framestr));
2154 frame = parseInt(framestr);
2155 if(s_getframe() >= frame
2156 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2157 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2159 s_frame(frame, cut);
2162 static int c_primitive(map_t*args)
2164 char*name = lu(args, "name");
2165 char*command = lu(args, "commandname");
2166 int width=0, height=0, r=0;
2167 int linewidth = parseTwip(lu(args, "line"));
2168 char*colorstr = lu(args, "color");
2169 RGBA color = parseColor(colorstr);
2170 char*fillstr = lu(args, "fill");
2177 if(!strcmp(command, "circle"))
2179 else if(!strcmp(command, "filled"))
2183 width = parseTwip(lu(args, "width"));
2184 height = parseTwip(lu(args, "height"));
2185 } else if (type==1) {
2186 r = parseTwip(lu(args, "r"));
2187 } else if (type==2) {
2188 outline = lu(args, "outline");
2191 if(!strcmp(fillstr, "fill"))
2193 if(!strcmp(fillstr, "none"))
2195 if(width<0 || height<0 || linewidth<0 || r<0)
2196 syntaxerror("values width, height, line, r must be positive");
2198 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2199 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2200 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2204 static int c_textshape(map_t*args)
2206 char*name = lu(args, "name");
2207 char*text = lu(args, "text");
2208 char*font = lu(args, "font");
2210 s_textshape(name, font, text);
2214 static int c_swf(map_t*args)
2216 char*name = lu(args, "name");
2217 char*filename = lu(args, "filename");
2218 char*command = lu(args, "commandname");
2219 if(!strcmp(command, "shape"))
2220 warning("Please use .swf instead of .shape");
2221 s_includeswf(name, filename);
2225 static int c_font(map_t*args)
2227 char*name = lu(args, "name");
2228 char*filename = lu(args, "filename");
2229 s_font(name, filename);
2233 static int c_sound(map_t*args)
2235 char*name = lu(args, "name");
2236 char*filename = lu(args, "filename");
2237 s_sound(name, filename);
2241 static int c_text(map_t*args)
2243 char*name = lu(args, "name");
2244 char*text = lu(args, "text");
2245 char*font = lu(args, "font");
2246 float size = parsePercent(lu(args, "size"));
2247 RGBA color = parseColor(lu(args, "color"));
2248 s_text(name, font, text, (int)(size*100), color);
2252 static int c_soundtrack(map_t*args)
2257 static int c_image(map_t*args)
2259 char*command = lu(args, "commandname");
2260 char*name = lu(args, "name");
2261 char*filename = lu(args, "filename");
2262 if(!strcmp(command,"jpeg")) {
2263 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2264 s_image(name, "jpeg", filename, quality);
2266 s_image(name, "png", filename, 0);
2271 static int c_outline(map_t*args)
2273 char*name = lu(args, "name");
2274 char*format = lu(args, "format");
2278 syntaxerror("colon (:) expected");
2280 s_outline(name, format, text);
2284 int fakechar(map_t*args)
2286 char*name = lu(args, "name");
2287 s_box(name, 0, 0, black, 20, 0);
2291 static int c_egon(map_t*args) {return fakechar(args);}
2292 static int c_button(map_t*args) {
2293 char*name = lu(args, "name");
2297 static int current_button_flags = 0;
2298 static int c_on_press(map_t*args)
2300 char*position = lu(args, "position");
2302 if(!strcmp(position, "inside")) {
2303 current_button_flags |= BC_OVERUP_OVERDOWN;
2304 } else if(!strcmp(position, "outside")) {
2305 //current_button_flags |= BC_IDLE_OUTDOWN;
2306 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2307 } else if(!strcmp(position, "anywhere")) {
2308 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2311 if(type == RAWDATA) {
2313 s_buttonaction(current_button_flags, action);
2314 current_button_flags = 0;
2320 static int c_on_release(map_t*args)
2322 char*position = lu(args, "position");
2324 if(!strcmp(position, "inside")) {
2325 current_button_flags |= BC_OVERDOWN_OVERUP;
2326 } else if(!strcmp(position, "outside")) {
2327 current_button_flags |= BC_OUTDOWN_IDLE;
2328 } else if(!strcmp(position, "anywhere")) {
2329 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2332 if(type == RAWDATA) {
2334 s_buttonaction(current_button_flags, action);
2335 current_button_flags = 0;
2341 static int c_on_move_in(map_t*args)
2343 char*position = lu(args, "state");
2345 if(!strcmp(position, "pressed")) {
2346 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2347 } else if(!strcmp(position, "not_pressed")) {
2348 current_button_flags |= BC_IDLE_OVERUP;
2349 } else if(!strcmp(position, "any")) {
2350 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2353 if(type == RAWDATA) {
2355 s_buttonaction(current_button_flags, action);
2356 current_button_flags = 0;
2362 static int c_on_move_out(map_t*args)
2364 char*position = lu(args, "state");
2366 if(!strcmp(position, "pressed")) {
2367 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2368 } else if(!strcmp(position, "not_pressed")) {
2369 current_button_flags |= BC_OVERUP_IDLE;
2370 } else if(!strcmp(position, "any")) {
2371 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2374 if(type == RAWDATA) {
2376 s_buttonaction(current_button_flags, action);
2377 current_button_flags = 0;
2383 static int c_on_key(map_t*args)
2385 char*key = lu(args, "key");
2387 if(strlen(key)==1) {
2390 current_button_flags |= 0x4000 + (key[0]*0x200);
2392 syntaxerror("invalid character: %c"+key[0]);
2397 <ctrl-x> = 0x200*(x-'a')
2401 syntaxerror("invalid key: %s",key);
2404 if(type == RAWDATA) {
2406 s_buttonaction(current_button_flags, action);
2407 current_button_flags = 0;
2414 static int c_edittext(map_t*args)
2416 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2417 char*name = lu(args, "name");
2418 char*font = lu(args, "font");
2419 int size = (int)(100*20*parsePercent(lu(args, "size")));
2420 int width = parseTwip(lu(args, "width"));
2421 int height = parseTwip(lu(args, "height"));
2422 char*text = lu(args, "text");
2423 RGBA color = parseColor(lu(args, "color"));
2424 int maxlength = parseInt(lu(args, "maxlength"));
2425 char*variable = lu(args, "variable");
2426 char*passwordstr = lu(args, "password");
2427 char*wordwrapstr = lu(args, "wordwrap");
2428 char*multilinestr = lu(args, "multiline");
2429 char*htmlstr = lu(args, "html");
2430 char*noselectstr = lu(args, "noselect");
2431 char*readonlystr = lu(args, "readonly");
2434 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2435 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2436 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2437 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2438 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2439 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2441 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2445 static int c_morphshape(map_t*args) {return fakechar(args);}
2446 static int c_movie(map_t*args) {return fakechar(args);}
2448 static int c_texture(map_t*args) {return 0;}
2450 static int c_action(map_t*args)
2453 if(type != RAWDATA) {
2454 syntaxerror("colon (:) expected");
2464 command_func_t* func;
2467 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2468 {"frame", c_frame, "n=<plus>1 @cut=no"},
2469 // "import" type stuff
2470 {"swf", c_swf, "name filename"},
2471 {"shape", c_swf, "name filename"},
2472 {"jpeg", c_image, "name filename quality=80%"},
2473 {"png", c_image, "name filename"},
2474 {"movie", c_movie, "name filename"},
2475 {"sound", c_sound, "name filename"},
2476 {"font", c_font, "name filename"},
2477 {"soundtrack", c_soundtrack, "filename"},
2479 // generators of primitives
2481 {"point", c_point, "name x=0 y=0"},
2482 {"gradient", c_gradient, "name @radial=0"},
2483 {"outline", c_outline, "name format=simple"},
2484 {"textshape", c_textshape, "name text font"},
2486 // character generators
2487 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2488 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2489 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2491 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2492 {"text", c_text, "name text font size=100% color=white"},
2493 {"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"},
2494 {"morphshape", c_morphshape, "name start end"},
2495 {"button", c_button, "name"},
2496 {"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="},
2497 {"on_press", c_on_press, "position=inside"},
2498 {"on_release", c_on_release, "position=anywhere"},
2499 {"on_move_in", c_on_move_out, "state=not_pressed"},
2500 {"on_move_out", c_on_move_out, "state=not_pressed"},
2501 {"on_key", c_on_key, "key=any"},
2504 {"play", c_play, "name loop=0 @nomultiple=0"},
2505 {"stop", c_stop, "name"},
2506 {"nextframe", c_nextframe, "name"},
2507 {"previousframe", c_previousframe, "name"},
2509 // object placement tags
2510 {"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="},
2511 {"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="},
2512 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2513 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2514 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2515 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2516 {"del", c_del, "name"},
2517 // virtual object placement
2518 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2520 // commands which start a block
2521 //startclip (see above)
2522 {"sprite", c_sprite, "name"},
2523 {"action", c_action, ""},
2529 static map_t parseArguments(char*command, char*pattern)
2545 string_set(&t1, "commandname");
2546 string_set(&t2, command);
2547 map_put(&result, t1, t2);
2549 if(!pattern || !*pattern)
2556 if(!strncmp("<i> ", x, 3)) {
2558 if(type == COMMAND || type == RAWDATA) {
2560 syntaxerror("character name expected");
2562 name[pos].str = "instance";
2564 value[pos].str = text;
2565 value[pos].len = strlen(text);
2569 if(type == ASSIGNMENT)
2572 name[pos].str = "character";
2574 value[pos].str = text;
2575 value[pos].len = strlen(text);
2583 isboolean[pos] = (x[0] =='@');
2596 name[pos].len = d-x;
2601 name[pos].len = e-x;
2602 value[pos].str = e+1;
2603 value[pos].len = d-e-1;
2611 /* for(t=0;t<len;t++) {
2612 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2613 isboolean[t]?"(boolean)":"");
2618 if(type == RAWDATA || type == COMMAND) {
2623 // first, search for boolean arguments
2624 for(pos=0;pos<len;pos++)
2626 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2628 if(type == ASSIGNMENT)
2630 value[pos].str = text;
2631 value[pos].len = strlen(text);
2632 /*printf("setting boolean parameter %s (to %s)\n",
2633 strdup_n(name[pos], namelen[pos]),
2634 strdup_n(value[pos], valuelen[pos]));*/
2639 // second, search for normal arguments
2641 for(pos=0;pos<len;pos++)
2643 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2644 (type != ASSIGNMENT && !set[pos])) {
2646 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2648 if(type == ASSIGNMENT)
2651 value[pos].str = text;
2652 value[pos].len = strlen(text);
2654 printf("setting parameter %s (to %s)\n",
2655 strdup_n(name[pos].str, name[pos].len),
2656 strdup_n(value[pos].str, value[pos].len));
2662 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2666 for(t=0;t<len;t++) {
2667 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2670 for(t=0;t<len;t++) {
2671 if(value[t].str && value[t].str[0] == '*') {
2672 //relative default- take value from some other parameter
2674 for(s=0;s<len;s++) {
2675 if(value[s].len == value[t].len-1 &&
2676 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2677 value[t].str = value[s].str;
2680 if(value[t].str == 0) {
2682 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2686 /* ok, now construct the dictionary from the parameters */
2690 map_put(&result, name[t], value[t]);
2694 static void parseArgumentsForCommand(char*command)
2699 msg("<verbose> parse Command: %s (line %d)", command, line);
2701 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2702 if(!strcmp(arguments[t].command, command)) {
2704 /* ugly hack- will be removed soon (once documentation and .sc generating
2705 utilities have been changed) */
2706 if(!strcmp(command, "swf") && !stackpos) {
2707 warning("Please use .flash instead of .swf- this will be mandatory soon");
2712 args = parseArguments(command, arguments[t].arguments);
2718 syntaxerror("command %s not known", command);
2720 // catch missing .flash directives at the beginning of a file
2721 if(strcmp(command, "flash") && !stackpos)
2723 syntaxerror("No movie defined- use .flash first");
2727 printf(".%s\n", command);fflush(stdout);
2728 map_dump(&args, stdout, "\t");fflush(stdout);
2731 (*arguments[nr].func)(&args);
2733 /*if(!strcmp(command, "button") ||
2734 !strcmp(command, "action")) {
2737 if(type == COMMAND) {
2738 if(!strcmp(text, "end"))
2752 int main (int argc,char ** argv)
2755 processargs(argc, argv);
2756 initLog(0,-1,0,0,-1,verbose);
2759 args_callback_usage(argv[0]);
2763 file = generateTokens(filename);
2765 printf("parser returned error.\n");
2771 while(!noMoreTokens()) {
2774 syntaxerror("command expected");
2775 parseArgumentsForCommand(text);