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 printf("%f %f %f %f\n", r.xmin/20.0, r.ymin/20.0, r.xmax/20.0, r.ymax/20.0);
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 _button
446 static button_t mybutton;
448 void s_button(char*name)
450 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
451 swf_SetU16(tag, id); //id
452 swf_ButtonSetFlags(tag, 0); //menu=no
454 mybutton.endofshapes = 0;
455 mybutton.shapes[0] = mybutton.shapes[1] = mybutton.shapes[2] = mybutton.shapes[3] = 0;
456 mybutton.nr_actions = 0;
458 memset(&stack[stackpos], 0, sizeof(stack[0]));
459 stack[stackpos].type = 3;
460 stack[stackpos].tag = tag;
461 stack[stackpos].id = id;
462 stack[stackpos].name = strdup(name);
467 void s_buttonput(char*character, char*as, parameters_t p)
469 character_t* c = dictionary_lookup(&characters, character);
473 syntaxerror("character %s not known (in .shape %s)", character, character);
475 if(strstr(as, "idle")) {flags |= BS_UP;mybutton.shapes[0]=c->id;}
476 if(strstr(as, "hover")) {flags |= BS_OVER;mybutton.shapes[1]=c->id;}
477 if(strstr(as, "pressed")) {flags |= BS_DOWN;mybutton.shapes[2]=c->id;}
478 if(strstr(as, "area")) {flags |= BS_HIT;mybutton.shapes[3]=c->id;}
480 if(mybutton.endofshapes) {
481 syntaxerror("a .do may not precede a .show", character, character);
484 m = s_instancepos(c->size, &p);
486 swf_ButtonSetRecord(stack[stackpos-1].tag,flags,c->id,1,&m,&p.cxform);
488 void s_buttonaction(int flags, char*action)
491 if(!mybutton.endofshapes) {
492 swf_SetU8(stack[stackpos-1].tag,0); // end of button records
493 mybutton.endofshapes = 1;
496 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
498 syntaxerror("Couldn't compile ActionScript");
501 swf_ButtonSetCondition(stack[stackpos-1].tag, BC_OVERDOWN_OVERUP);
502 swf_ActionSet(stack[stackpos-1].tag, a);
503 mybutton.nr_actions++;
508 static void s_endButton()
511 if(!mybutton.endofshapes) {
512 swf_SetU8(stack[stackpos].tag,0); // end of button records
513 mybutton.endofshapes = 1;
517 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
519 tag = stack[stackpos].tag;
520 free(stack[stackpos].name);
523 TAG* removeFromTo(TAG*from, TAG*to)
525 TAG*save = from->prev;
527 TAG*next = from->next;
535 static void s_endSprite()
537 SRECT r = currentrect;
539 if(stack[stackpos].cut)
540 tag = removeFromTo(stack[stackpos].cut, tag);
544 /* TODO: before clearing, prepend "<spritename>." to names and
545 copy into old instances dict */
546 dictionary_clear(&instances);
548 currentframe = stack[stackpos].oldframe;
549 currentrect = stack[stackpos].oldrect;
550 currentdepth = stack[stackpos].olddepth;
551 instances = stack[stackpos].oldinstances;
553 tag = swf_InsertTag(tag, ST_END);
555 tag = stack[stackpos].tag;
558 syntaxerror("internal error(7)");
560 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
561 free(stack[stackpos].name);
564 static void s_endSWF()
570 if(stack[stackpos].cut)
571 tag = removeFromTo(stack[stackpos].cut, tag);
575 swf = stack[stackpos].swf;
576 filename = stack[stackpos].filename;
578 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
580 tag = swf_InsertTag(tag, ST_END);
582 swf_OptimizeTagOrder(swf);
584 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
585 swf->movieSize = currentrect; /* "autocrop" */
588 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
589 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
590 swf->movieSize.ymax += 20;
593 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
595 syntaxerror("couldn't create output file %s", filename);
598 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
600 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
604 dictionary_clear(&instances);
605 dictionary_clear(&characters);
606 dictionary_clear(&images);
607 dictionary_clear(&outlines);
608 dictionary_clear(&gradients);
609 dictionary_clear(&fonts);
610 dictionary_clear(&sounds);
620 if(stack[stackpos-1].type == 0)
621 syntaxerror("End of file encountered in .flash block");
622 if(stack[stackpos-1].type == 1)
623 syntaxerror("End of file encountered in .sprite block");
624 if(stack[stackpos-1].type == 2)
625 syntaxerror("End of file encountered in .clip block");
634 void s_frame(int nr, int cut)
639 for(t=currentframe;t<nr;t++) {
640 tag = swf_InsertTag(tag, ST_SHOWFRAME);
645 syntaxerror("Can't cut, frame empty");
647 stack[stackpos].cut = tag;
653 int parseColor2(char*str, RGBA*color);
655 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
660 if(texture[0] == '#') {
661 parseColor2(texture, &color);
662 return swf_ShapeAddSolidFillStyle(s, &color);
663 } else if((image = dictionary_lookup(&images, texture))) {
665 swf_GetMatrix(0, &m);
666 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
667 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
670 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
671 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
672 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
674 swf_GetMatrix(0, &m);
675 m.sx = (r->xmax - r->xmin)*2;
676 m.sy = (r->ymax - r->ymin)*2;
677 m.tx = r->xmin + (r->xmax - r->xmin)/2;
678 m.ty = r->ymin + (r->ymax - r->ymin)/2;
679 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
680 } else if (parseColor2(texture, &color)) {
681 return swf_ShapeAddSolidFillStyle(s, &color);
683 syntaxerror("not a color/fillstyle: %s", texture);
688 RGBA black={r:0,g:0,b:0,a:0};
689 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
698 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
700 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
703 fs1 = addFillStyle(s, &r2, texture);
706 r.xmin = r2.xmin-linewidth-linewidth/2;
707 r.ymin = r2.ymin-linewidth-linewidth/2;
708 r.xmax = r2.xmax+linewidth+linewidth/2;
709 r.ymax = r2.ymax+linewidth+linewidth/2;
711 swf_SetShapeHeader(tag,s);
712 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
713 swf_ShapeSetLine(tag,s,width,0);
714 swf_ShapeSetLine(tag,s,0,height);
715 swf_ShapeSetLine(tag,s,-width,0);
716 swf_ShapeSetLine(tag,s,0,-height);
717 swf_ShapeSetEnd(tag);
720 s_addcharacter(name, id, tag, r);
724 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
730 outline = dictionary_lookup(&outlines, outlinename);
732 syntaxerror("outline %s not defined", outlinename);
736 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
738 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
740 fs1 = addFillStyle(s, &r2, texture);
742 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
744 rect.xmin = r2.xmin-linewidth-linewidth/2;
745 rect.ymin = r2.ymin-linewidth-linewidth/2;
746 rect.xmax = r2.xmax+linewidth+linewidth/2;
747 rect.ymax = r2.ymax+linewidth+linewidth/2;
749 swf_SetRect(tag,&rect);
750 swf_SetShapeStyles(tag, s);
751 swf_SetShapeBits(tag, outline->shape); //does not count bits!
752 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
755 s_addcharacter(name, id, tag, rect);
759 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
764 r2.xmin = r2.ymin = 0;
768 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
770 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
772 fs1 = addFillStyle(s, &r2, texture);
774 rect.xmin = r2.xmin-linewidth-linewidth/2;
775 rect.ymin = r2.ymin-linewidth-linewidth/2;
776 rect.xmax = r2.xmax+linewidth+linewidth/2;
777 rect.ymax = r2.ymax+linewidth+linewidth/2;
779 swf_SetRect(tag,&rect);
780 swf_SetShapeHeader(tag,s);
781 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
782 swf_ShapeSetCircle(tag, s, r,r,r,r);
783 swf_ShapeSetEnd(tag);
786 s_addcharacter(name, id, tag, rect);
790 void s_textshape(char*name, char*fontname, char*_text)
793 U8*text = (U8*)_text;
797 font = dictionary_lookup(&fonts, fontname);
799 syntaxerror("font \"%s\" not known!", fontname);
801 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
802 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
803 s_box(name, 0, 0, black, 20, 0);
806 g = font->ascii2glyph[text[0]];
808 outline = malloc(sizeof(outline_t));
809 memset(outline, 0, sizeof(outline_t));
810 outline->shape = font->glyph[g].shape;
811 outline->bbox = font->layout->bounds[g];
815 swf_Shape11DrawerInit(&draw, 0);
816 swf_DrawText(&draw, font, _text);
818 outline->shape = swf_ShapeDrawerToShape(&draw);
819 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
823 if(dictionary_lookup(&outlines, name))
824 syntaxerror("outline %s defined twice", name);
825 dictionary_put2(&outlines, name, outline);
828 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
833 font = dictionary_lookup(&fonts, fontname);
835 syntaxerror("font \"%s\" not known!", fontname);
837 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
839 if(!font->numchars) {
840 s_box(name, 0, 0, black, 20, 0);
843 r = swf_SetDefineText(tag, font, &color, text, size);
845 s_addcharacter(name, id, tag, r);
849 /* type: either "jpeg" or "png"
851 void s_image(char*name, char*type, char*filename, int quality)
853 /* an image is actually two folded: 1st bitmap, 2nd character.
854 Both of them can be used separately */
856 /* step 1: the bitmap */
861 warning("image type \"png\" not supported yet!");
862 s_box(name, 0, 0, black, 20, 0);
867 warning("no jpeg support compiled in");
868 s_box(name, 0, 0, black, 20, 0);
871 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
872 swf_SetU16(tag, imageID);
874 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
875 syntaxerror("Image \"%s\" not found, or contains errors", filename);
878 swf_GetJPEGSize(filename, &width, &height);
885 s_addimage(name, id, tag, r);
890 /* step 2: the character */
891 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
893 swf_ShapeSetBitmapRect(tag, imageID, width, height);
895 s_addcharacter(name, id, tag, r);
899 void dumpSWF(SWF*swf)
901 TAG* tag = swf->firstTag;
902 printf("vvvvvvvvvvvvvvvvvvvvv\n");
904 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
907 printf("^^^^^^^^^^^^^^^^^^^^^\n");
910 void s_font(char*name, char*filename)
913 font = swf_LoadFont(filename);
916 warning("Couldn't open font file \"%s\"", filename);
917 font = (SWFFONT*)malloc(sizeof(SWFFONT));
918 memset(font, 0, sizeof(SWFFONT));
919 dictionary_put2(&fonts, name, font);
925 /* fix the layout. Only needed for old fonts */
927 for(t=0;t<font->numchars;t++) {
928 font->glyph[t].advance = 0;
931 swf_FontCreateLayout(font);
935 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
936 swf_FontSetDefine2(tag, font);
939 if(dictionary_lookup(&fonts, name))
940 syntaxerror("font %s defined twice", name);
941 dictionary_put2(&fonts, name, font);
946 typedef struct _sound_t
952 void s_sound(char*name, char*filename)
954 struct WAV wav, wav2;
959 if(!readWAV(filename, &wav)) {
960 warning("Couldn't read wav file \"%s\"", filename);
964 convertWAV2mono(&wav, &wav2, 44100);
965 samples = (U16*)wav2.data;
966 numsamples = wav2.size/2;
970 tag = swf_InsertTag(tag, ST_DEFINESOUND);
971 swf_SetU16(tag, id); //id
972 swf_SetSoundDefine(tag, samples, numsamples);
974 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
978 if(dictionary_lookup(&sounds, name))
979 syntaxerror("sound %s defined twice", name);
980 dictionary_put2(&sounds, name, sound);
988 static char* gradient_getToken(const char**p)
992 while(**p && strchr(" \t\n\r", **p)) {
996 while(**p && !strchr(" \t\n\r", **p)) {
999 result = malloc((*p)-start+1);
1000 memcpy(result,start,(*p)-start+1);
1001 result[(*p)-start] = 0;
1005 float parsePercent(char*str);
1006 RGBA parseColor(char*str);
1008 GRADIENT parseGradient(const char*str)
1011 const char* p = str;
1012 memset(&gradient, 0, sizeof(GRADIENT));
1014 char*posstr,*colorstr;
1017 posstr = gradient_getToken(&p);
1020 pos = parsePercent(posstr);
1021 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1022 colorstr = gradient_getToken(&p);
1023 color = parseColor(colorstr);
1024 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1025 warning("gradient record too big- max size is 8, rest ignored");
1028 gradient.ratios[gradient.num] = (int)(pos*255.0);
1029 gradient.rgba[gradient.num] = color;
1037 void s_gradient(char*name, const char*text, int radial)
1039 gradient_t* gradient;
1040 gradient = malloc(sizeof(gradient_t));
1041 memset(gradient, 0, sizeof(gradient_t));
1042 gradient->gradient = parseGradient(text);
1043 gradient->radial = radial;
1045 if(dictionary_lookup(&gradients, name))
1046 syntaxerror("gradient %s defined twice", name);
1047 dictionary_put2(&gradients, name, gradient);
1050 void s_action(const char*text)
1053 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1055 syntaxerror("Couldn't compile ActionScript");
1058 tag = swf_InsertTag(tag, ST_DOACTION);
1060 swf_ActionSet(tag, a);
1065 void s_outline(char*name, char*format, char*source)
1074 swf_Shape11DrawerInit(&draw, 0);
1075 draw_string(&draw, source);
1077 shape = swf_ShapeDrawerToShape(&draw);
1078 //shape2 = swf_ShapeToShape2(shape);
1079 //bounds = swf_GetShapeBoundingBox(shape2);
1080 //swf_Shape2Free(shape2);
1081 bounds = swf_ShapeDrawerGetBBox(&draw);
1082 draw.dealloc(&draw);
1084 outline = (outline_t*)malloc(sizeof(outline_t));
1085 memset(outline, 0, sizeof(outline_t));
1086 outline->shape = shape;
1087 outline->bbox = bounds;
1089 if(dictionary_lookup(&outlines, name))
1090 syntaxerror("outline %s defined twice", name);
1091 dictionary_put2(&outlines, name, outline);
1094 void s_playsound(char*name, int loops, int nomultiple, int stop)
1096 sound_t* sound = dictionary_lookup(&sounds, name);
1099 syntaxerror("Don't know anything about sound \"%s\"", name);
1101 tag = swf_InsertTag(tag, ST_STARTSOUND);
1102 swf_SetU16(tag, sound->id); //id
1103 memset(&info, 0, sizeof(info));
1106 info.nomultiple = nomultiple;
1107 swf_SetSoundInfo(tag, &info);
1110 void s_includeswf(char*name, char*filename)
1118 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1119 f = open(filename,O_RDONLY);
1121 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1122 s_box(name, 0, 0, black, 20, 0);
1125 if (swf_ReadSWF(f,&swf)<0) {
1126 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1127 s_box(name, 0, 0, black, 20, 0);
1132 /* FIXME: The following sets the bounding Box for the character.
1133 It is wrong for two reasons:
1134 a) It may be too small (in case objects in the movie clip at the borders)
1135 b) it may be too big (because the poor movie never got autocropped)
1139 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1140 swf_SetU16(tag, id);
1143 swf_Relocate(&swf, idmap);
1145 ftag = swf.firstTag;
1149 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1150 if(cutout[t] == ftag->id) {
1154 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1156 if(ftag->id == ST_END)
1160 /* We simply dump all tags right after the sprite
1161 header, relying on the fact that swf_OptimizeTagOrder() will
1162 sort things out for us later.
1163 We also rely on the fact that the imported SWF is well-formed.
1165 tag = swf_InsertTag(tag, ftag->id);
1166 swf_SetBlock(tag, ftag->data, ftag->len);
1170 syntaxerror("Included file %s contains errors", filename);
1171 tag = swf_InsertTag(tag, ST_END);
1175 s_addcharacter(name, id, tag, r);
1178 SRECT s_getCharBBox(char*name)
1180 character_t* c = dictionary_lookup(&characters, name);
1181 if(!c) syntaxerror("character '%s' unknown(2)", name);
1184 SRECT s_getInstanceBBox(char*name)
1186 instance_t * i = dictionary_lookup(&instances, name);
1188 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1190 if(!c) syntaxerror("internal error(5)");
1193 parameters_t s_getParameters(char*name)
1195 instance_t * i = dictionary_lookup(&instances, name);
1196 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1197 return i->parameters;
1199 void s_startclip(char*instance, char*character, parameters_t p)
1201 character_t* c = dictionary_lookup(&characters, character);
1205 syntaxerror("character %s not known", character);
1207 i = s_addinstance(instance, c, currentdepth);
1209 m = s_instancepos(i->character->size, &p);
1211 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1212 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1213 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1215 i->lastFrame= currentframe;
1217 stack[stackpos].tag = tag;
1218 stack[stackpos].type = 2;
1227 swf_SetTagPos(stack[stackpos].tag, 0);
1228 swf_GetPlaceObject(stack[stackpos].tag, &p);
1229 p.clipdepth = currentdepth;
1230 swf_ClearTag(stack[stackpos].tag);
1231 swf_SetPlaceObject(stack[stackpos].tag, &p);
1235 void s_put(char*instance, char*character, parameters_t p)
1237 character_t* c = dictionary_lookup(&characters, character);
1241 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1244 i = s_addinstance(instance, c, currentdepth);
1246 m = s_instancepos(i->character->size, &p);
1248 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1249 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1251 i->lastFrame = currentframe;
1255 void s_jump(char*instance, parameters_t p)
1257 instance_t* i = dictionary_lookup(&instances, instance);
1260 syntaxerror("instance %s not known", instance);
1264 m = s_instancepos(i->character->size, &p);
1266 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1267 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1269 i->lastFrame = currentframe;
1272 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1276 if(num==0 || num==1)
1278 ratio = (float)pos/(float)num;
1280 p.x = (p2->x-p1->x)*ratio + p1->x;
1281 p.y = (p2->y-p1->y)*ratio + p1->y;
1282 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1283 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1284 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1285 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1287 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1288 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1289 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1290 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1292 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1293 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1294 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1295 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1297 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1298 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1299 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1300 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1304 void s_change(char*instance, parameters_t p2)
1306 instance_t* i = dictionary_lookup(&instances, instance);
1310 int frame, allframes;
1312 syntaxerror("instance %s not known", instance);
1316 allframes = currentframe - i->lastFrame - 1;
1318 warning(".change ignored. can only .put/.change an object once per frame.");
1322 m = s_instancepos(i->character->size, &p2);
1323 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1324 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1327 /* o.k., we got the start and end point set. Now iterate though all the
1328 tags in between, inserting object changes after each new frame */
1331 if(!t) syntaxerror("internal error(6)");
1333 while(frame < allframes) {
1334 if(t->id == ST_SHOWFRAME) {
1339 p = s_interpolate(&p1, &p2, frame, allframes);
1340 m = s_instancepos(i->character->size, &p); //needed?
1341 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1342 i->lastFrame = currentframe;
1343 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1345 if(frame == allframes)
1350 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1354 void s_delinstance(char*instance)
1356 instance_t* i = dictionary_lookup(&instances, instance);
1358 syntaxerror("instance %s not known", instance);
1360 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1361 swf_SetU16(tag, i->depth);
1362 dictionary_del(&instances, instance);
1365 void s_qchange(char*instance, parameters_t p)
1372 syntaxerror(".end unexpected");
1373 if(stack[stackpos-1].type == 0)
1375 else if(stack[stackpos-1].type == 1)
1377 else if(stack[stackpos-1].type == 2)
1379 else if(stack[stackpos-1].type == 3)
1381 else syntaxerror("internal error 1");
1384 // ------------------------------------------------------------------------
1386 typedef int command_func_t(map_t*args);
1388 SRECT parseBox(char*str)
1391 float xmin, xmax, ymin, ymax;
1392 char*x = strchr(str, 'x');
1394 if(!strcmp(str, "autocrop")) {
1395 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1399 d1 = strchr(x+1, ':');
1401 d2 = strchr(d1+1, ':');
1403 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1407 else if(d1 && !d2) {
1408 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1414 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1419 r.xmin = (SCOORD)(xmin*20);
1420 r.ymin = (SCOORD)(ymin*20);
1421 r.xmax = (SCOORD)(xmax*20);
1422 r.ymax = (SCOORD)(ymax*20);
1425 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1428 float parseFloat(char*str)
1432 int parseInt(char*str)
1437 if(str[0]=='+' || str[0]=='-')
1441 if(str[t]<'0' || str[t]>'9')
1442 syntaxerror("Not an Integer: \"%s\"", str);
1445 int parseTwip(char*str)
1449 if(str[0]=='+' || str[0]=='-') {
1454 dot = strchr(str, '.');
1458 return sign*parseInt(str)*20;
1460 int l=strlen(++dot);
1462 for(s=str;s<dot-1;s++)
1463 if(*s<'0' || *s>'9')
1464 syntaxerror("Not a coordinate: \"%s\"", str);
1466 if(*s<'0' || *s>'9')
1467 syntaxerror("Not a coordinate: \"%s\"", str);
1469 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1470 warning("precision loss: %s converted to twip", str);
1475 return sign*atoi(str)*20;
1477 return sign*atoi(str)*20+atoi(dot)*2;
1479 return sign*atoi(str)*20+atoi(dot)/5;
1484 int isPoint(char*str)
1486 if(strchr(str, '('))
1492 SPOINT parsePoint(char*str)
1496 int l = strlen(str);
1497 char*comma = strchr(str, ',');
1498 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1499 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1500 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1501 p.x = parseTwip(tmp);
1502 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1503 p.y = parseTwip(tmp);
1507 int parseColor2(char*str, RGBA*color)
1509 int l = strlen(str);
1513 struct {unsigned char r,g,b;char*name;} colors[] =
1514 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1515 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1516 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1517 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1518 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1519 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1520 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1521 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1522 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1523 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1524 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1525 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1529 if(str[0]=='#' && (l==7 || l==9)) {
1530 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1532 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1534 color->r = r; color->g = g; color->b = b; color->a = a;
1537 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1538 if(!strcmp(str, colors[t].name)) {
1543 color->r = r; color->g = g; color->b = b; color->a = a;
1549 RGBA parseColor(char*str)
1552 if(!parseColor2(str, &c))
1553 syntaxerror("Expression '%s' is not a color", str);
1557 typedef struct _muladd {
1562 MULADD parseMulAdd(char*str)
1565 char* str2 = (char*)malloc(strlen(str)+5);
1572 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1573 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1574 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1575 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1576 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1577 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1578 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1579 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1580 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1581 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1583 syntaxerror("'%s' is not a valid color transform expression", str);
1585 m.add = (int)(add*256);
1586 m.mul = (int)(mul*256);
1591 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1593 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1594 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1596 if(a<-32768) a=-32768;
1597 if(a>32767) a=32767;
1598 if(m<-32768) m=-32768;
1599 if(m>32767) m=32767;
1605 float parsePercent(char*str)
1607 int l = strlen(str);
1611 return atoi(str)/100.0;
1613 syntaxerror("Expression '%s' is not a percentage", str);
1616 int isPercent(char*str)
1618 return str[strlen(str)-1]=='%';
1620 int parseNewSize(char*str, int size)
1623 return parsePercent(str)*size;
1625 return (int)(atof(str)*20);
1628 int isColor(char*str)
1631 return parseColor2(str, &c);
1634 static char* lu(map_t* args, char*name)
1636 char* value = map_lookup(args, name);
1638 map_dump(args, stdout, "");
1639 syntaxerror("internal error 2: value %s should be set", name);
1644 static int c_flash(map_t*args)
1646 char* name = lu(args, "name");
1647 char* compressstr = lu(args, "compress");
1648 SRECT bbox = parseBox(lu(args, "bbox"));
1649 int version = parseInt(lu(args, "version"));
1650 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1652 RGBA color = parseColor(lu(args, "background"));
1653 if(!strcmp(name, "!default!") || override_outputname)
1656 if(!strcmp(compressstr, "default"))
1657 compress = version==6;
1658 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1660 else if(!strcmp(compressstr, "no"))
1662 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1664 s_swf(name, bbox, version, fps, compress, color);
1667 int isRelative(char*str)
1669 return !strncmp(str, "<plus>", 6) ||
1670 !strncmp(str, "<minus>", 7);
1672 char* getOffset(char*str)
1674 if(!strncmp(str, "<plus>", 6))
1676 if(!strncmp(str, "<minus>", 7))
1678 syntaxerror("internal error (347)");
1681 int getSign(char*str)
1683 if(!strncmp(str, "<plus>", 6))
1685 if(!strncmp(str, "<minus>", 7))
1687 syntaxerror("internal error (348)");
1690 static dictionary_t points;
1691 static mem_t mpoints;
1692 int points_initialized = 0;
1694 SPOINT getPoint(SRECT r, char*name)
1697 if(!strcmp(name, "center")) {
1699 p.x = (r.xmin + r.xmax)/2;
1700 p.y = (r.ymin + r.ymax)/2;
1704 if(points_initialized)
1705 l = (int)dictionary_lookup(&points, name);
1707 syntaxerror("Invalid point: \"%s\".", name);
1710 return *(SPOINT*)&mpoints.buffer[l];
1712 static int c_gradient(map_t*args)
1714 char*name = lu(args, "name");
1715 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1719 syntaxerror("colon (:) expected");
1721 s_gradient(name, text, radial);
1724 static int c_point(map_t*args)
1726 char*name = lu(args, "name");
1730 if(!points_initialized) {
1731 dictionary_init(&points);
1733 points_initialized = 1;
1735 p.x = parseTwip(lu(args, "x"));
1736 p.y = parseTwip(lu(args, "y"));
1737 pos = mem_put(&mpoints, &p, sizeof(p));
1738 string_set(&s1, name);
1740 dictionary_put(&points, s1, (void*)pos);
1743 static int c_play(map_t*args)
1745 char*name = lu(args, "sound");
1746 char*loop = lu(args, "loop");
1747 char*nomultiple = lu(args, "nomultiple");
1749 if(!strcmp(nomultiple, "nomultiple"))
1752 nm = parseInt(nomultiple);
1754 s_playsound(name, parseInt(loop), nm, 0);
1758 static int c_stop(map_t*args)
1760 char*name = lu(args, "sound");
1761 s_playsound(name, 0,0,1);
1765 static int c_placement(map_t*args, int type)
1767 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1770 char* luminancestr = lu(args, "luminance");
1771 char* scalestr = lu(args, "scale");
1772 char* scalexstr = lu(args, "scalex");
1773 char* scaleystr = lu(args, "scaley");
1774 char* rotatestr = lu(args, "rotate");
1775 char* shearstr = lu(args, "shear");
1776 char* xstr="", *pivotstr="";
1777 char* ystr="", *anglestr="";
1778 char*above = lu(args, "above"); /*FIXME*/
1779 char*below = lu(args, "below");
1780 char* rstr = lu(args, "red");
1781 char* gstr = lu(args, "green");
1782 char* bstr = lu(args, "blue");
1783 char* astr = lu(args, "alpha");
1784 char* pinstr = lu(args, "pin");
1785 char* as = map_lookup(args, "as");
1793 if(type==9) { // (?) .rotate or .arcchange
1794 pivotstr = lu(args, "pivot");
1795 anglestr = lu(args, "angle");
1797 xstr = lu(args, "x");
1798 ystr = lu(args, "y");
1801 luminance = parseMulAdd(luminancestr);
1804 luminance.mul = 256;
1808 if(scalexstr[0]||scaleystr[0])
1809 syntaxerror("scalex/scaley and scale cannot both be set");
1810 scalexstr = scaleystr = scalestr;
1813 if(type == 0 || type == 4) {
1815 character = lu(args, "character");
1816 parameters_clear(&p);
1817 } else if (type == 5) {
1818 character = lu(args, "name");
1819 parameters_clear(&p);
1822 p = s_getParameters(instance);
1827 if(isRelative(xstr)) {
1828 if(type == 0 || type == 4)
1829 syntaxerror("relative x values not allowed for initial put or startclip");
1830 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1832 p.x = parseTwip(xstr);
1836 if(isRelative(ystr)) {
1837 if(type == 0 || type == 4)
1838 syntaxerror("relative y values not allowed for initial put or startclip");
1839 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1841 p.y = parseTwip(ystr);
1845 /* scale, scalex, scaley */
1847 oldbbox = s_getCharBBox(character);
1849 oldbbox = s_getInstanceBBox(instance);
1851 oldwidth = oldbbox.xmax - oldbbox.xmin;
1852 oldheight = oldbbox.ymax - oldbbox.ymin;
1854 if(oldwidth==0) p.scalex = 1.0;
1857 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1861 if(oldheight==0) p.scaley = 1.0;
1864 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1870 if(isRelative(rotatestr)) {
1871 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1873 p.rotate = parseFloat(rotatestr);
1879 if(isRelative(shearstr)) {
1880 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1882 p.shear = parseFloat(shearstr);
1887 if(isPoint(pivotstr))
1888 p.pivot = parsePoint(pivotstr);
1890 p.pivot = getPoint(oldbbox, pivotstr);
1894 p.pin = parsePoint(pinstr);
1896 p.pin = getPoint(oldbbox, pinstr);
1899 /* color transform */
1901 if(rstr[0] || luminancestr[0]) {
1904 r = parseMulAdd(rstr);
1906 r.add = p.cxform.r0;
1907 r.mul = p.cxform.r1;
1909 r = mergeMulAdd(r, luminance);
1910 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1912 if(gstr[0] || luminancestr[0]) {
1915 g = parseMulAdd(gstr);
1917 g.add = p.cxform.g0;
1918 g.mul = p.cxform.g1;
1920 g = mergeMulAdd(g, luminance);
1921 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1923 if(bstr[0] || luminancestr[0]) {
1926 b = parseMulAdd(bstr);
1928 b.add = p.cxform.b0;
1929 b.mul = p.cxform.b1;
1931 b = mergeMulAdd(b, luminance);
1932 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1935 MULADD a = parseMulAdd(astr);
1936 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1940 s_put(instance, character, p);
1942 s_change(instance, p);
1944 s_qchange(instance, p);
1946 s_jump(instance, p);
1948 s_startclip(instance, character, p);
1949 else if(type == 5) {
1951 s_buttonput(character, as, p);
1953 s_buttonput(character, "shape", p);
1958 static int c_put(map_t*args)
1960 c_placement(args, 0);
1963 static int c_change(map_t*args)
1965 c_placement(args, 1);
1968 static int c_qchange(map_t*args)
1970 c_placement(args, 2);
1973 static int c_arcchange(map_t*args)
1975 c_placement(args, 2);
1978 static int c_jump(map_t*args)
1980 c_placement(args, 3);
1983 static int c_startclip(map_t*args)
1985 c_placement(args, 4);
1988 static int c_show(map_t*args)
1990 c_placement(args, 5);
1993 static int c_del(map_t*args)
1995 char*instance = lu(args, "name");
1996 s_delinstance(instance);
1999 static int c_end(map_t*args)
2004 static int c_sprite(map_t*args)
2006 char* name = lu(args, "name");
2010 static int c_frame(map_t*args)
2012 char*framestr = lu(args, "n");
2013 char*cutstr = lu(args, "cut");
2016 if(strcmp(cutstr, "no"))
2018 if(isRelative(framestr)) {
2019 frame = s_getframe();
2020 if(getSign(framestr)<0)
2021 syntaxerror("relative frame expressions must be positive");
2022 frame += parseInt(getOffset(framestr));
2025 frame = parseInt(framestr);
2026 if(s_getframe() >= frame
2027 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2028 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2030 s_frame(frame, cut);
2033 static int c_primitive(map_t*args)
2035 char*name = lu(args, "name");
2036 char*command = lu(args, "commandname");
2037 int width=0, height=0, r=0;
2038 int linewidth = parseTwip(lu(args, "line"));
2039 char*colorstr = lu(args, "color");
2040 RGBA color = parseColor(colorstr);
2041 char*fillstr = lu(args, "fill");
2048 if(!strcmp(command, "circle"))
2050 else if(!strcmp(command, "filled"))
2054 width = parseTwip(lu(args, "width"));
2055 height = parseTwip(lu(args, "height"));
2056 } else if (type==1) {
2057 r = parseTwip(lu(args, "r"));
2058 } else if (type==2) {
2059 outline = lu(args, "outline");
2062 if(!strcmp(fillstr, "fill"))
2064 if(!strcmp(fillstr, "none"))
2066 if(width<0 || height<0 || linewidth<0 || r<0)
2067 syntaxerror("values width, height, line, r must be positive");
2069 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2070 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2071 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2075 static int c_textshape(map_t*args)
2077 char*name = lu(args, "name");
2078 char*text = lu(args, "text");
2079 char*font = lu(args, "font");
2081 s_textshape(name, font, text);
2085 static int c_swf(map_t*args)
2087 char*name = lu(args, "name");
2088 char*filename = lu(args, "filename");
2089 char*command = lu(args, "commandname");
2090 if(!strcmp(command, "shape"))
2091 warning("Please use .swf instead of .shape");
2092 s_includeswf(name, filename);
2096 static int c_font(map_t*args)
2098 char*name = lu(args, "name");
2099 char*filename = lu(args, "filename");
2100 s_font(name, filename);
2104 static int c_sound(map_t*args)
2106 char*name = lu(args, "name");
2107 char*filename = lu(args, "filename");
2108 s_sound(name, filename);
2112 static int c_text(map_t*args)
2114 char*name = lu(args, "name");
2115 char*text = lu(args, "text");
2116 char*font = lu(args, "font");
2117 float size = parsePercent(lu(args, "size"));
2118 RGBA color = parseColor(lu(args, "color"));
2119 s_text(name, font, text, (int)(size*100), color);
2123 static int c_soundtrack(map_t*args)
2128 static int c_image(map_t*args)
2130 char*command = lu(args, "commandname");
2131 char*name = lu(args, "name");
2132 char*filename = lu(args, "filename");
2133 if(!strcmp(command,"jpeg")) {
2134 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2135 s_image(name, "jpeg", filename, quality);
2137 s_image(name, "png", filename, 0);
2142 static int c_outline(map_t*args)
2144 char*name = lu(args, "name");
2145 char*format = lu(args, "format");
2149 syntaxerror("colon (:) expected");
2151 s_outline(name, format, text);
2155 int fakechar(map_t*args)
2157 char*name = lu(args, "name");
2158 s_box(name, 0, 0, black, 20, 0);
2162 static int c_egon(map_t*args) {return fakechar(args);}
2163 static int c_button(map_t*args) {
2164 char*name = lu(args, "name");
2168 static int current_button_flags = 0;
2169 static int c_on_press(map_t*args)
2171 char*position = lu(args, "position");
2172 if(!strcmp(position, "inside")) {
2173 current_button_flags |= BC_OVERUP_OVERDOWN;
2174 } else if(!strcmp(position, "outside")) {
2175 //current_button_flags |= BC_IDLE_OUTDOWN;
2176 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2177 } else if(!strcmp(position, "anywhere")) {
2178 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2182 if(type == RAWDATA) {
2184 s_buttonaction(current_button_flags, action);
2185 current_button_flags = 0;
2191 static int c_on_release(map_t*args)
2193 char*position = lu(args, "position");
2194 if(!strcmp(position, "inside")) {
2195 current_button_flags |= BC_OVERDOWN_OVERUP;
2196 } else if(!strcmp(position, "outside")) {
2197 current_button_flags |= BC_OUTDOWN_IDLE;
2198 } else if(!strcmp(position, "anywhere")) {
2199 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2203 if(type == RAWDATA) {
2205 s_buttonaction(current_button_flags, action);
2206 current_button_flags = 0;
2212 static int c_on_move_in(map_t*args)
2214 char*position = lu(args, "state");
2215 if(!strcmp(position, "pressed")) {
2216 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2217 } else if(!strcmp(position, "not_pressed")) {
2218 current_button_flags |= BC_IDLE_OVERUP;
2219 } else if(!strcmp(position, "any")) {
2220 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2224 if(type == RAWDATA) {
2226 s_buttonaction(current_button_flags, action);
2227 current_button_flags = 0;
2233 static int c_on_move_out(map_t*args)
2235 char*position = lu(args, "state");
2236 if(!strcmp(position, "pressed")) {
2237 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2238 } else if(!strcmp(position, "not_pressed")) {
2239 current_button_flags |= BC_OVERUP_IDLE;
2240 } else if(!strcmp(position, "any")) {
2241 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2245 if(type == RAWDATA) {
2247 s_buttonaction(current_button_flags, action);
2248 current_button_flags = 0;
2254 static int c_on_key(map_t*args)
2256 char*key = lu(args, "key");
2257 if(strlen(key)==1) {
2260 current_button_flags |= 0x4000 + (key[0]*0x200);
2262 syntaxerror("invalid character: %c"+key[0]);
2267 <ctrl-x> = 0x200*(x-'a')
2271 syntaxerror("invalid key: %s",key);
2275 if(type == RAWDATA) {
2277 s_buttonaction(current_button_flags, action);
2278 current_button_flags = 0;
2285 static int c_edittext(map_t*args) {return fakechar(args);}
2287 static int c_morphshape(map_t*args) {return fakechar(args);}
2288 static int c_movie(map_t*args) {return fakechar(args);}
2290 static int c_texture(map_t*args) {return 0;}
2292 static int c_action(map_t*args)
2295 if(type != RAWDATA) {
2296 syntaxerror("colon (:) expected");
2306 command_func_t* func;
2309 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2310 {"frame", c_frame, "n=<plus>1 @cut=no"},
2311 // "import" type stuff
2312 {"swf", c_swf, "name filename"},
2313 {"shape", c_swf, "name filename"},
2314 {"jpeg", c_image, "name filename quality=80%"},
2315 {"png", c_image, "name filename"},
2316 {"movie", c_movie, "name filename"},
2317 {"sound", c_sound, "name filename"},
2318 {"font", c_font, "name filename"},
2319 {"soundtrack", c_soundtrack, "filename"},
2321 // generators of primitives
2323 {"point", c_point, "name x=0 y=0"},
2324 {"gradient", c_gradient, "name @radial=0"},
2325 {"outline", c_outline, "name format=simple"},
2326 {"textshape", c_textshape, "name text font"},
2328 // character generators
2329 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2330 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2331 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2333 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2334 {"text", c_text, "name text font size=100% color=white"},
2335 {"edittext", c_edittext, "name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2336 {"morphshape", c_morphshape, "name start end"},
2337 {"button", c_button, "name"},
2338 {"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="},
2339 {"on_press", c_on_press, "position=inside"},
2340 {"on_release", c_on_release, "position=anywhere"},
2341 {"on_move_in", c_on_move_out, "state=not_pressed"},
2342 {"on_move_out", c_on_move_out, "state=not_pressed"},
2343 {"on_key", c_on_key, "key=any"},
2346 {"play", c_play, "sound loop=0 @nomultiple=0"},
2347 {"stop", c_stop, "sound"},
2349 // object placement tags
2350 {"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="},
2351 {"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="},
2352 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2353 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2354 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2355 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2356 {"del", c_del, "name"},
2357 // virtual object placement
2358 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2360 // commands which start a block
2361 //startclip (see above)
2362 {"sprite", c_sprite, "name"},
2363 {"action", c_action, ""},
2369 static map_t parseArguments(char*command, char*pattern)
2385 string_set(&t1, "commandname");
2386 string_set(&t2, command);
2387 map_put(&result, t1, t2);
2389 if(!pattern || !*pattern)
2396 if(!strncmp("<i> ", x, 3)) {
2398 if(type == COMMAND || type == RAWDATA) {
2400 syntaxerror("character name expected");
2402 name[pos].str = "instance";
2404 value[pos].str = text;
2405 value[pos].len = strlen(text);
2409 if(type == ASSIGNMENT)
2412 name[pos].str = "character";
2414 value[pos].str = text;
2415 value[pos].len = strlen(text);
2423 isboolean[pos] = (x[0] =='@');
2436 name[pos].len = d-x;
2441 name[pos].len = e-x;
2442 value[pos].str = e+1;
2443 value[pos].len = d-e-1;
2451 /* for(t=0;t<len;t++) {
2452 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2453 isboolean[t]?"(boolean)":"");
2458 if(type == RAWDATA || type == COMMAND) {
2463 // first, search for boolean arguments
2464 for(pos=0;pos<len;pos++)
2466 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2468 if(type == ASSIGNMENT)
2470 value[pos].str = text;
2471 value[pos].len = strlen(text);
2472 /*printf("setting boolean parameter %s (to %s)\n",
2473 strdup_n(name[pos], namelen[pos]),
2474 strdup_n(value[pos], valuelen[pos]));*/
2479 // second, search for normal arguments
2481 for(pos=0;pos<len;pos++)
2483 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2484 (type != ASSIGNMENT && !set[pos])) {
2486 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2488 if(type == ASSIGNMENT)
2491 value[pos].str = text;
2492 value[pos].len = strlen(text);
2494 printf("setting parameter %s (to %s)\n",
2495 strdup_n(name[pos].str, name[pos].len),
2496 strdup_n(value[pos].str, value[pos].len));
2502 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2506 for(t=0;t<len;t++) {
2507 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2510 for(t=0;t<len;t++) {
2511 if(value[t].str && value[t].str[0] == '*') {
2512 //relative default- take value from some other parameter
2514 for(s=0;s<len;s++) {
2515 if(value[s].len == value[t].len-1 &&
2516 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2517 value[t].str = value[s].str;
2520 if(value[t].str == 0) {
2522 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2526 /* ok, now construct the dictionary from the parameters */
2530 map_put(&result, name[t], value[t]);
2534 static void parseArgumentsForCommand(char*command)
2539 msg("<verbose> parse Command: %s (line %d)", command, line);
2541 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2542 if(!strcmp(arguments[t].command, command)) {
2544 /* ugly hack- will be removed soon (once documentation and .sc generating
2545 utilities have been changed) */
2546 if(!strcmp(command, "swf") && !stackpos) {
2547 warning("Please use .flash instead of .swf- this will be mandatory soon");
2552 args = parseArguments(command, arguments[t].arguments);
2558 syntaxerror("command %s not known", command);
2560 // catch missing .flash directives at the beginning of a file
2561 if(strcmp(command, "flash") && !stackpos)
2563 syntaxerror("No movie defined- use .flash first");
2567 printf(".%s\n", command);fflush(stdout);
2568 map_dump(&args, stdout, "\t");fflush(stdout);
2571 (*arguments[nr].func)(&args);
2573 /*if(!strcmp(command, "button") ||
2574 !strcmp(command, "action")) {
2577 if(type == COMMAND) {
2578 if(!strcmp(text, "end"))
2592 int main (int argc,char ** argv)
2595 processargs(argc, argv);
2596 initLog(0,-1,0,0,-1,verbose);
2599 args_callback_usage(argv[0]);
2603 file = generateTokens(filename);
2605 printf("parser returned error.\n");
2611 while(!noMoreTokens()) {
2614 syntaxerror("command expected");
2615 parseArgumentsForCommand(text);