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 _button
445 static button_t mybutton;
447 void s_button(char*name)
449 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
450 swf_SetU16(tag, id); //id
451 swf_ButtonSetFlags(tag, 0); //menu=no
453 mybutton.endofshapes = 0;
454 mybutton.shapes[0] = mybutton.shapes[1] = mybutton.shapes[2] = mybutton.shapes[3] = 0;
455 mybutton.nr_actions = 0;
457 memset(&stack[stackpos], 0, sizeof(stack[0]));
458 stack[stackpos].type = 3;
459 stack[stackpos].tag = tag;
460 stack[stackpos].id = id;
461 stack[stackpos].name = strdup(name);
462 stack[stackpos].oldrect = currentrect;
463 memset(¤trect, 0, sizeof(currentrect));
468 void s_buttonput(char*character, char*as, parameters_t p)
470 character_t* c = dictionary_lookup(&characters, character);
474 syntaxerror("character %s not known (in .shape %s)", character, character);
476 if(strstr(as, "idle")) {flags |= BS_UP;mybutton.shapes[0]=c->id;}
477 if(strstr(as, "hover")) {flags |= BS_OVER;mybutton.shapes[1]=c->id;}
478 if(strstr(as, "pressed")) {flags |= BS_DOWN;mybutton.shapes[2]=c->id;}
479 if(strstr(as, "area")) {flags |= BS_HIT;mybutton.shapes[3]=c->id;}
483 if(mybutton.endofshapes) {
484 syntaxerror("a .do may not precede a .show", character, character);
487 m = s_instancepos(c->size, &p);
489 swf_ButtonSetRecord(stack[stackpos-1].tag,flags,c->id,1,&m,&p.cxform);
491 void s_buttonaction(int flags, char*action)
497 if(!mybutton.endofshapes) {
498 swf_SetU8(stack[stackpos-1].tag,0); // end of button records
499 mybutton.endofshapes = 1;
503 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
505 syntaxerror("Couldn't compile ActionScript");
508 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
509 swf_ActionSet(stack[stackpos-1].tag, a);
510 mybutton.nr_actions++;
515 static void s_endButton()
518 if(!mybutton.endofshapes) {
519 swf_SetU8(stack[stackpos].tag,0); // end of button records
520 mybutton.endofshapes = 1;
524 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
526 SRECT r = currentrect;
528 tag = stack[stackpos].tag;
529 currentrect = stack[stackpos].oldrect;
531 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
532 free(stack[stackpos].name);
535 TAG* removeFromTo(TAG*from, TAG*to)
537 TAG*save = from->prev;
539 TAG*next = from->next;
547 static void s_endSprite()
549 SRECT r = currentrect;
551 if(stack[stackpos].cut)
552 tag = removeFromTo(stack[stackpos].cut, tag);
556 /* TODO: before clearing, prepend "<spritename>." to names and
557 copy into old instances dict */
558 dictionary_clear(&instances);
560 currentframe = stack[stackpos].oldframe;
561 currentrect = stack[stackpos].oldrect;
562 currentdepth = stack[stackpos].olddepth;
563 instances = stack[stackpos].oldinstances;
565 tag = swf_InsertTag(tag, ST_END);
567 tag = stack[stackpos].tag;
570 syntaxerror("internal error(7)");
572 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
573 free(stack[stackpos].name);
576 static void s_endSWF()
582 if(stack[stackpos].cut)
583 tag = removeFromTo(stack[stackpos].cut, tag);
587 swf = stack[stackpos].swf;
588 filename = stack[stackpos].filename;
590 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
592 tag = swf_InsertTag(tag, ST_END);
594 swf_OptimizeTagOrder(swf);
596 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
597 swf->movieSize = currentrect; /* "autocrop" */
600 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
601 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
602 swf->movieSize.ymax += 20;
605 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
607 syntaxerror("couldn't create output file %s", filename);
610 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
612 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
616 dictionary_clear(&instances);
617 dictionary_clear(&characters);
618 dictionary_clear(&images);
619 dictionary_clear(&outlines);
620 dictionary_clear(&gradients);
621 dictionary_clear(&fonts);
622 dictionary_clear(&sounds);
632 if(stack[stackpos-1].type == 0)
633 syntaxerror("End of file encountered in .flash block");
634 if(stack[stackpos-1].type == 1)
635 syntaxerror("End of file encountered in .sprite block");
636 if(stack[stackpos-1].type == 2)
637 syntaxerror("End of file encountered in .clip block");
646 void s_frame(int nr, int cut)
651 for(t=currentframe;t<nr;t++) {
652 tag = swf_InsertTag(tag, ST_SHOWFRAME);
657 syntaxerror("Can't cut, frame empty");
659 stack[stackpos].cut = tag;
665 int parseColor2(char*str, RGBA*color);
667 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
672 if(texture[0] == '#') {
673 parseColor2(texture, &color);
674 return swf_ShapeAddSolidFillStyle(s, &color);
675 } else if((image = dictionary_lookup(&images, texture))) {
677 swf_GetMatrix(0, &m);
678 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
679 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
682 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
683 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
684 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
686 swf_GetMatrix(0, &m);
687 m.sx = (r->xmax - r->xmin)*2;
688 m.sy = (r->ymax - r->ymin)*2;
689 m.tx = r->xmin + (r->xmax - r->xmin)/2;
690 m.ty = r->ymin + (r->ymax - r->ymin)/2;
691 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
692 } else if (parseColor2(texture, &color)) {
693 return swf_ShapeAddSolidFillStyle(s, &color);
695 syntaxerror("not a color/fillstyle: %s", texture);
700 RGBA black={r:0,g:0,b:0,a:0};
701 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
710 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
712 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
715 fs1 = addFillStyle(s, &r2, texture);
718 r.xmin = r2.xmin-linewidth-linewidth/2;
719 r.ymin = r2.ymin-linewidth-linewidth/2;
720 r.xmax = r2.xmax+linewidth+linewidth/2;
721 r.ymax = r2.ymax+linewidth+linewidth/2;
723 swf_SetShapeHeader(tag,s);
724 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
725 swf_ShapeSetLine(tag,s,width,0);
726 swf_ShapeSetLine(tag,s,0,height);
727 swf_ShapeSetLine(tag,s,-width,0);
728 swf_ShapeSetLine(tag,s,0,-height);
729 swf_ShapeSetEnd(tag);
732 s_addcharacter(name, id, tag, r);
736 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
742 outline = dictionary_lookup(&outlines, outlinename);
744 syntaxerror("outline %s not defined", outlinename);
748 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
750 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
752 fs1 = addFillStyle(s, &r2, texture);
754 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
756 rect.xmin = r2.xmin-linewidth-linewidth/2;
757 rect.ymin = r2.ymin-linewidth-linewidth/2;
758 rect.xmax = r2.xmax+linewidth+linewidth/2;
759 rect.ymax = r2.ymax+linewidth+linewidth/2;
761 swf_SetRect(tag,&rect);
762 swf_SetShapeStyles(tag, s);
763 swf_SetShapeBits(tag, outline->shape); //does not count bits!
764 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
767 s_addcharacter(name, id, tag, rect);
771 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
776 r2.xmin = r2.ymin = 0;
780 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
782 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
784 fs1 = addFillStyle(s, &r2, texture);
786 rect.xmin = r2.xmin-linewidth-linewidth/2;
787 rect.ymin = r2.ymin-linewidth-linewidth/2;
788 rect.xmax = r2.xmax+linewidth+linewidth/2;
789 rect.ymax = r2.ymax+linewidth+linewidth/2;
791 swf_SetRect(tag,&rect);
792 swf_SetShapeHeader(tag,s);
793 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
794 swf_ShapeSetCircle(tag, s, r,r,r,r);
795 swf_ShapeSetEnd(tag);
798 s_addcharacter(name, id, tag, rect);
802 void s_textshape(char*name, char*fontname, char*_text)
805 U8*text = (U8*)_text;
809 font = dictionary_lookup(&fonts, fontname);
811 syntaxerror("font \"%s\" not known!", fontname);
813 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
814 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
815 s_box(name, 0, 0, black, 20, 0);
818 g = font->ascii2glyph[text[0]];
820 outline = malloc(sizeof(outline_t));
821 memset(outline, 0, sizeof(outline_t));
822 outline->shape = font->glyph[g].shape;
823 outline->bbox = font->layout->bounds[g];
827 swf_Shape11DrawerInit(&draw, 0);
828 swf_DrawText(&draw, font, _text);
830 outline->shape = swf_ShapeDrawerToShape(&draw);
831 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
835 if(dictionary_lookup(&outlines, name))
836 syntaxerror("outline %s defined twice", name);
837 dictionary_put2(&outlines, name, outline);
840 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
845 font = dictionary_lookup(&fonts, fontname);
847 syntaxerror("font \"%s\" not known!", fontname);
849 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
851 if(!font->numchars) {
852 s_box(name, 0, 0, black, 20, 0);
855 r = swf_SetDefineText(tag, font, &color, text, size);
857 s_addcharacter(name, id, tag, r);
861 /* type: either "jpeg" or "png"
863 void s_image(char*name, char*type, char*filename, int quality)
865 /* an image is actually two folded: 1st bitmap, 2nd character.
866 Both of them can be used separately */
868 /* step 1: the bitmap */
873 warning("image type \"png\" not supported yet!");
874 s_box(name, 0, 0, black, 20, 0);
879 warning("no jpeg support compiled in");
880 s_box(name, 0, 0, black, 20, 0);
883 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
884 swf_SetU16(tag, imageID);
886 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
887 syntaxerror("Image \"%s\" not found, or contains errors", filename);
890 swf_GetJPEGSize(filename, &width, &height);
897 s_addimage(name, id, tag, r);
902 /* step 2: the character */
903 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
905 swf_ShapeSetBitmapRect(tag, imageID, width, height);
907 s_addcharacter(name, id, tag, r);
911 void dumpSWF(SWF*swf)
913 TAG* tag = swf->firstTag;
914 printf("vvvvvvvvvvvvvvvvvvvvv\n");
916 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
919 printf("^^^^^^^^^^^^^^^^^^^^^\n");
922 void s_font(char*name, char*filename)
925 font = swf_LoadFont(filename);
928 warning("Couldn't open font file \"%s\"", filename);
929 font = (SWFFONT*)malloc(sizeof(SWFFONT));
930 memset(font, 0, sizeof(SWFFONT));
931 dictionary_put2(&fonts, name, font);
937 /* fix the layout. Only needed for old fonts */
939 for(t=0;t<font->numchars;t++) {
940 font->glyph[t].advance = 0;
943 swf_FontCreateLayout(font);
947 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
948 swf_FontSetDefine2(tag, font);
951 if(dictionary_lookup(&fonts, name))
952 syntaxerror("font %s defined twice", name);
953 dictionary_put2(&fonts, name, font);
958 typedef struct _sound_t
964 void s_sound(char*name, char*filename)
966 struct WAV wav, wav2;
971 if(!readWAV(filename, &wav)) {
972 warning("Couldn't read wav file \"%s\"", filename);
976 convertWAV2mono(&wav, &wav2, 44100);
977 samples = (U16*)wav2.data;
978 numsamples = wav2.size/2;
982 tag = swf_InsertTag(tag, ST_DEFINESOUND);
983 swf_SetU16(tag, id); //id
984 swf_SetSoundDefine(tag, samples, numsamples);
986 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
990 if(dictionary_lookup(&sounds, name))
991 syntaxerror("sound %s defined twice", name);
992 dictionary_put2(&sounds, name, sound);
1000 static char* gradient_getToken(const char**p)
1004 while(**p && strchr(" \t\n\r", **p)) {
1008 while(**p && !strchr(" \t\n\r", **p)) {
1011 result = malloc((*p)-start+1);
1012 memcpy(result,start,(*p)-start+1);
1013 result[(*p)-start] = 0;
1017 float parsePercent(char*str);
1018 RGBA parseColor(char*str);
1020 GRADIENT parseGradient(const char*str)
1023 const char* p = str;
1024 memset(&gradient, 0, sizeof(GRADIENT));
1026 char*posstr,*colorstr;
1029 posstr = gradient_getToken(&p);
1032 pos = parsePercent(posstr);
1033 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1034 colorstr = gradient_getToken(&p);
1035 color = parseColor(colorstr);
1036 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1037 warning("gradient record too big- max size is 8, rest ignored");
1040 gradient.ratios[gradient.num] = (int)(pos*255.0);
1041 gradient.rgba[gradient.num] = color;
1049 void s_gradient(char*name, const char*text, int radial)
1051 gradient_t* gradient;
1052 gradient = malloc(sizeof(gradient_t));
1053 memset(gradient, 0, sizeof(gradient_t));
1054 gradient->gradient = parseGradient(text);
1055 gradient->radial = radial;
1057 if(dictionary_lookup(&gradients, name))
1058 syntaxerror("gradient %s defined twice", name);
1059 dictionary_put2(&gradients, name, gradient);
1062 void s_action(const char*text)
1065 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1067 syntaxerror("Couldn't compile ActionScript");
1070 tag = swf_InsertTag(tag, ST_DOACTION);
1072 swf_ActionSet(tag, a);
1077 void s_outline(char*name, char*format, char*source)
1086 swf_Shape11DrawerInit(&draw, 0);
1087 draw_string(&draw, source);
1089 shape = swf_ShapeDrawerToShape(&draw);
1090 //shape2 = swf_ShapeToShape2(shape);
1091 //bounds = swf_GetShapeBoundingBox(shape2);
1092 //swf_Shape2Free(shape2);
1093 bounds = swf_ShapeDrawerGetBBox(&draw);
1094 draw.dealloc(&draw);
1096 outline = (outline_t*)malloc(sizeof(outline_t));
1097 memset(outline, 0, sizeof(outline_t));
1098 outline->shape = shape;
1099 outline->bbox = bounds;
1101 if(dictionary_lookup(&outlines, name))
1102 syntaxerror("outline %s defined twice", name);
1103 dictionary_put2(&outlines, name, outline);
1106 void s_playsound(char*name, int loops, int nomultiple, int stop)
1108 sound_t* sound = dictionary_lookup(&sounds, name);
1111 syntaxerror("Don't know anything about sound \"%s\"", name);
1113 tag = swf_InsertTag(tag, ST_STARTSOUND);
1114 swf_SetU16(tag, sound->id); //id
1115 memset(&info, 0, sizeof(info));
1118 info.nomultiple = nomultiple;
1119 swf_SetSoundInfo(tag, &info);
1122 void s_includeswf(char*name, char*filename)
1130 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1131 f = open(filename,O_RDONLY);
1133 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1134 s_box(name, 0, 0, black, 20, 0);
1137 if (swf_ReadSWF(f,&swf)<0) {
1138 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1139 s_box(name, 0, 0, black, 20, 0);
1144 /* FIXME: The following sets the bounding Box for the character.
1145 It is wrong for two reasons:
1146 a) It may be too small (in case objects in the movie clip at the borders)
1147 b) it may be too big (because the poor movie never got autocropped)
1151 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1152 swf_SetU16(tag, id);
1155 swf_Relocate(&swf, idmap);
1157 ftag = swf.firstTag;
1161 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1162 if(cutout[t] == ftag->id) {
1166 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1168 if(ftag->id == ST_END)
1172 /* We simply dump all tags right after the sprite
1173 header, relying on the fact that swf_OptimizeTagOrder() will
1174 sort things out for us later.
1175 We also rely on the fact that the imported SWF is well-formed.
1177 tag = swf_InsertTag(tag, ftag->id);
1178 swf_SetBlock(tag, ftag->data, ftag->len);
1182 syntaxerror("Included file %s contains errors", filename);
1183 tag = swf_InsertTag(tag, ST_END);
1187 s_addcharacter(name, id, tag, r);
1190 SRECT s_getCharBBox(char*name)
1192 character_t* c = dictionary_lookup(&characters, name);
1193 if(!c) syntaxerror("character '%s' unknown(2)", name);
1196 SRECT s_getInstanceBBox(char*name)
1198 instance_t * i = dictionary_lookup(&instances, name);
1200 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1202 if(!c) syntaxerror("internal error(5)");
1205 parameters_t s_getParameters(char*name)
1207 instance_t * i = dictionary_lookup(&instances, name);
1208 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1209 return i->parameters;
1211 void s_startclip(char*instance, char*character, parameters_t p)
1213 character_t* c = dictionary_lookup(&characters, character);
1217 syntaxerror("character %s not known", character);
1219 i = s_addinstance(instance, c, currentdepth);
1221 m = s_instancepos(i->character->size, &p);
1223 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1224 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1225 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1227 i->lastFrame= currentframe;
1229 stack[stackpos].tag = tag;
1230 stack[stackpos].type = 2;
1239 swf_SetTagPos(stack[stackpos].tag, 0);
1240 swf_GetPlaceObject(stack[stackpos].tag, &p);
1241 p.clipdepth = currentdepth;
1242 swf_ClearTag(stack[stackpos].tag);
1243 swf_SetPlaceObject(stack[stackpos].tag, &p);
1247 void s_put(char*instance, char*character, parameters_t p)
1249 character_t* c = dictionary_lookup(&characters, character);
1253 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1256 i = s_addinstance(instance, c, currentdepth);
1258 m = s_instancepos(i->character->size, &p);
1260 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1261 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1263 i->lastFrame = currentframe;
1267 void s_jump(char*instance, parameters_t p)
1269 instance_t* i = dictionary_lookup(&instances, instance);
1272 syntaxerror("instance %s not known", instance);
1276 m = s_instancepos(i->character->size, &p);
1278 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1279 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1281 i->lastFrame = currentframe;
1284 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1288 if(num==0 || num==1)
1290 ratio = (float)pos/(float)num;
1292 p.x = (p2->x-p1->x)*ratio + p1->x;
1293 p.y = (p2->y-p1->y)*ratio + p1->y;
1294 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1295 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1296 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1297 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1299 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1300 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1301 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1302 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1304 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1305 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1306 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1307 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1309 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1310 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1311 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1312 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1316 void s_change(char*instance, parameters_t p2)
1318 instance_t* i = dictionary_lookup(&instances, instance);
1322 int frame, allframes;
1324 syntaxerror("instance %s not known", instance);
1328 allframes = currentframe - i->lastFrame - 1;
1330 warning(".change ignored. can only .put/.change an object once per frame.");
1334 m = s_instancepos(i->character->size, &p2);
1335 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1336 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1339 /* o.k., we got the start and end point set. Now iterate though all the
1340 tags in between, inserting object changes after each new frame */
1343 if(!t) syntaxerror("internal error(6)");
1345 while(frame < allframes) {
1346 if(t->id == ST_SHOWFRAME) {
1351 p = s_interpolate(&p1, &p2, frame, allframes);
1352 m = s_instancepos(i->character->size, &p); //needed?
1353 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1354 i->lastFrame = currentframe;
1355 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1357 if(frame == allframes)
1362 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1366 void s_delinstance(char*instance)
1368 instance_t* i = dictionary_lookup(&instances, instance);
1370 syntaxerror("instance %s not known", instance);
1372 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1373 swf_SetU16(tag, i->depth);
1374 dictionary_del(&instances, instance);
1377 void s_qchange(char*instance, parameters_t p)
1384 syntaxerror(".end unexpected");
1385 if(stack[stackpos-1].type == 0)
1387 else if(stack[stackpos-1].type == 1)
1389 else if(stack[stackpos-1].type == 2)
1391 else if(stack[stackpos-1].type == 3)
1393 else syntaxerror("internal error 1");
1396 // ------------------------------------------------------------------------
1398 typedef int command_func_t(map_t*args);
1400 SRECT parseBox(char*str)
1403 float xmin, xmax, ymin, ymax;
1404 char*x = strchr(str, 'x');
1406 if(!strcmp(str, "autocrop")) {
1407 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1411 d1 = strchr(x+1, ':');
1413 d2 = strchr(d1+1, ':');
1415 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1419 else if(d1 && !d2) {
1420 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1426 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1431 r.xmin = (SCOORD)(xmin*20);
1432 r.ymin = (SCOORD)(ymin*20);
1433 r.xmax = (SCOORD)(xmax*20);
1434 r.ymax = (SCOORD)(ymax*20);
1437 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1440 float parseFloat(char*str)
1444 int parseInt(char*str)
1449 if(str[0]=='+' || str[0]=='-')
1453 if(str[t]<'0' || str[t]>'9')
1454 syntaxerror("Not an Integer: \"%s\"", str);
1457 int parseTwip(char*str)
1461 if(str[0]=='+' || str[0]=='-') {
1466 dot = strchr(str, '.');
1470 return sign*parseInt(str)*20;
1472 int l=strlen(++dot);
1474 for(s=str;s<dot-1;s++)
1475 if(*s<'0' || *s>'9')
1476 syntaxerror("Not a coordinate: \"%s\"", str);
1478 if(*s<'0' || *s>'9')
1479 syntaxerror("Not a coordinate: \"%s\"", str);
1481 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1482 warning("precision loss: %s converted to twip", str);
1487 return sign*atoi(str)*20;
1489 return sign*atoi(str)*20+atoi(dot)*2;
1491 return sign*atoi(str)*20+atoi(dot)/5;
1496 int isPoint(char*str)
1498 if(strchr(str, '('))
1504 SPOINT parsePoint(char*str)
1508 int l = strlen(str);
1509 char*comma = strchr(str, ',');
1510 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1511 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1512 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1513 p.x = parseTwip(tmp);
1514 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1515 p.y = parseTwip(tmp);
1519 int parseColor2(char*str, RGBA*color)
1521 int l = strlen(str);
1525 struct {unsigned char r,g,b;char*name;} colors[] =
1526 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1527 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1528 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1529 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1530 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1531 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1532 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1533 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1534 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1535 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1536 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1537 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1541 if(str[0]=='#' && (l==7 || l==9)) {
1542 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1544 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1546 color->r = r; color->g = g; color->b = b; color->a = a;
1549 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1550 if(!strcmp(str, colors[t].name)) {
1555 color->r = r; color->g = g; color->b = b; color->a = a;
1561 RGBA parseColor(char*str)
1564 if(!parseColor2(str, &c))
1565 syntaxerror("Expression '%s' is not a color", str);
1569 typedef struct _muladd {
1574 MULADD parseMulAdd(char*str)
1577 char* str2 = (char*)malloc(strlen(str)+5);
1584 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1585 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1586 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1587 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1588 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1589 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1590 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1591 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1592 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1593 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1595 syntaxerror("'%s' is not a valid color transform expression", str);
1597 m.add = (int)(add*256);
1598 m.mul = (int)(mul*256);
1603 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1605 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1606 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1608 if(a<-32768) a=-32768;
1609 if(a>32767) a=32767;
1610 if(m<-32768) m=-32768;
1611 if(m>32767) m=32767;
1617 float parsePercent(char*str)
1619 int l = strlen(str);
1623 return atoi(str)/100.0;
1625 syntaxerror("Expression '%s' is not a percentage", str);
1628 int isPercent(char*str)
1630 return str[strlen(str)-1]=='%';
1632 int parseNewSize(char*str, int size)
1635 return parsePercent(str)*size;
1637 return (int)(atof(str)*20);
1640 int isColor(char*str)
1643 return parseColor2(str, &c);
1646 static char* lu(map_t* args, char*name)
1648 char* value = map_lookup(args, name);
1650 map_dump(args, stdout, "");
1651 syntaxerror("internal error 2: value %s should be set", name);
1656 static int c_flash(map_t*args)
1658 char* name = lu(args, "name");
1659 char* compressstr = lu(args, "compress");
1660 SRECT bbox = parseBox(lu(args, "bbox"));
1661 int version = parseInt(lu(args, "version"));
1662 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1664 RGBA color = parseColor(lu(args, "background"));
1665 if(!strcmp(name, "!default!") || override_outputname)
1668 if(!strcmp(compressstr, "default"))
1669 compress = version==6;
1670 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1672 else if(!strcmp(compressstr, "no"))
1674 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1676 s_swf(name, bbox, version, fps, compress, color);
1679 int isRelative(char*str)
1681 return !strncmp(str, "<plus>", 6) ||
1682 !strncmp(str, "<minus>", 7);
1684 char* getOffset(char*str)
1686 if(!strncmp(str, "<plus>", 6))
1688 if(!strncmp(str, "<minus>", 7))
1690 syntaxerror("internal error (347)");
1693 int getSign(char*str)
1695 if(!strncmp(str, "<plus>", 6))
1697 if(!strncmp(str, "<minus>", 7))
1699 syntaxerror("internal error (348)");
1702 static dictionary_t points;
1703 static mem_t mpoints;
1704 int points_initialized = 0;
1706 SPOINT getPoint(SRECT r, char*name)
1709 if(!strcmp(name, "center")) {
1711 p.x = (r.xmin + r.xmax)/2;
1712 p.y = (r.ymin + r.ymax)/2;
1716 if(points_initialized)
1717 l = (int)dictionary_lookup(&points, name);
1719 syntaxerror("Invalid point: \"%s\".", name);
1722 return *(SPOINT*)&mpoints.buffer[l];
1724 static int c_gradient(map_t*args)
1726 char*name = lu(args, "name");
1727 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1731 syntaxerror("colon (:) expected");
1733 s_gradient(name, text, radial);
1736 static int c_point(map_t*args)
1738 char*name = lu(args, "name");
1742 if(!points_initialized) {
1743 dictionary_init(&points);
1745 points_initialized = 1;
1747 p.x = parseTwip(lu(args, "x"));
1748 p.y = parseTwip(lu(args, "y"));
1749 pos = mem_put(&mpoints, &p, sizeof(p));
1750 string_set(&s1, name);
1752 dictionary_put(&points, s1, (void*)pos);
1755 static int c_play(map_t*args)
1757 char*name = lu(args, "sound");
1758 char*loop = lu(args, "loop");
1759 char*nomultiple = lu(args, "nomultiple");
1761 if(!strcmp(nomultiple, "nomultiple"))
1764 nm = parseInt(nomultiple);
1766 s_playsound(name, parseInt(loop), nm, 0);
1770 static int c_stop(map_t*args)
1772 char*name = lu(args, "sound");
1773 s_playsound(name, 0,0,1);
1777 static int c_placement(map_t*args, int type)
1779 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1782 char* luminancestr = lu(args, "luminance");
1783 char* scalestr = lu(args, "scale");
1784 char* scalexstr = lu(args, "scalex");
1785 char* scaleystr = lu(args, "scaley");
1786 char* rotatestr = lu(args, "rotate");
1787 char* shearstr = lu(args, "shear");
1788 char* xstr="", *pivotstr="";
1789 char* ystr="", *anglestr="";
1790 char*above = lu(args, "above"); /*FIXME*/
1791 char*below = lu(args, "below");
1792 char* rstr = lu(args, "red");
1793 char* gstr = lu(args, "green");
1794 char* bstr = lu(args, "blue");
1795 char* astr = lu(args, "alpha");
1796 char* pinstr = lu(args, "pin");
1797 char* as = map_lookup(args, "as");
1805 if(type==9) { // (?) .rotate or .arcchange
1806 pivotstr = lu(args, "pivot");
1807 anglestr = lu(args, "angle");
1809 xstr = lu(args, "x");
1810 ystr = lu(args, "y");
1813 luminance = parseMulAdd(luminancestr);
1816 luminance.mul = 256;
1820 if(scalexstr[0]||scaleystr[0])
1821 syntaxerror("scalex/scaley and scale cannot both be set");
1822 scalexstr = scaleystr = scalestr;
1825 if(type == 0 || type == 4) {
1827 character = lu(args, "character");
1828 parameters_clear(&p);
1829 } else if (type == 5) {
1830 character = lu(args, "name");
1831 parameters_clear(&p);
1834 p = s_getParameters(instance);
1839 if(isRelative(xstr)) {
1840 if(type == 0 || type == 4)
1841 syntaxerror("relative x values not allowed for initial put or startclip");
1842 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1844 p.x = parseTwip(xstr);
1848 if(isRelative(ystr)) {
1849 if(type == 0 || type == 4)
1850 syntaxerror("relative y values not allowed for initial put or startclip");
1851 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1853 p.y = parseTwip(ystr);
1857 /* scale, scalex, scaley */
1859 oldbbox = s_getCharBBox(character);
1861 oldbbox = s_getInstanceBBox(instance);
1863 oldwidth = oldbbox.xmax - oldbbox.xmin;
1864 oldheight = oldbbox.ymax - oldbbox.ymin;
1866 if(oldwidth==0) p.scalex = 1.0;
1869 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1873 if(oldheight==0) p.scaley = 1.0;
1876 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1882 if(isRelative(rotatestr)) {
1883 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1885 p.rotate = parseFloat(rotatestr);
1891 if(isRelative(shearstr)) {
1892 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1894 p.shear = parseFloat(shearstr);
1899 if(isPoint(pivotstr))
1900 p.pivot = parsePoint(pivotstr);
1902 p.pivot = getPoint(oldbbox, pivotstr);
1906 p.pin = parsePoint(pinstr);
1908 p.pin = getPoint(oldbbox, pinstr);
1911 /* color transform */
1913 if(rstr[0] || luminancestr[0]) {
1916 r = parseMulAdd(rstr);
1918 r.add = p.cxform.r0;
1919 r.mul = p.cxform.r1;
1921 r = mergeMulAdd(r, luminance);
1922 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1924 if(gstr[0] || luminancestr[0]) {
1927 g = parseMulAdd(gstr);
1929 g.add = p.cxform.g0;
1930 g.mul = p.cxform.g1;
1932 g = mergeMulAdd(g, luminance);
1933 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1935 if(bstr[0] || luminancestr[0]) {
1938 b = parseMulAdd(bstr);
1940 b.add = p.cxform.b0;
1941 b.mul = p.cxform.b1;
1943 b = mergeMulAdd(b, luminance);
1944 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1947 MULADD a = parseMulAdd(astr);
1948 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1952 s_put(instance, character, p);
1954 s_change(instance, p);
1956 s_qchange(instance, p);
1958 s_jump(instance, p);
1960 s_startclip(instance, character, p);
1961 else if(type == 5) {
1963 s_buttonput(character, as, p);
1965 s_buttonput(character, "shape", p);
1970 static int c_put(map_t*args)
1972 c_placement(args, 0);
1975 static int c_change(map_t*args)
1977 c_placement(args, 1);
1980 static int c_qchange(map_t*args)
1982 c_placement(args, 2);
1985 static int c_arcchange(map_t*args)
1987 c_placement(args, 2);
1990 static int c_jump(map_t*args)
1992 c_placement(args, 3);
1995 static int c_startclip(map_t*args)
1997 c_placement(args, 4);
2000 static int c_show(map_t*args)
2002 c_placement(args, 5);
2005 static int c_del(map_t*args)
2007 char*instance = lu(args, "name");
2008 s_delinstance(instance);
2011 static int c_end(map_t*args)
2016 static int c_sprite(map_t*args)
2018 char* name = lu(args, "name");
2022 static int c_frame(map_t*args)
2024 char*framestr = lu(args, "n");
2025 char*cutstr = lu(args, "cut");
2028 if(strcmp(cutstr, "no"))
2030 if(isRelative(framestr)) {
2031 frame = s_getframe();
2032 if(getSign(framestr)<0)
2033 syntaxerror("relative frame expressions must be positive");
2034 frame += parseInt(getOffset(framestr));
2037 frame = parseInt(framestr);
2038 if(s_getframe() >= frame
2039 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2040 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2042 s_frame(frame, cut);
2045 static int c_primitive(map_t*args)
2047 char*name = lu(args, "name");
2048 char*command = lu(args, "commandname");
2049 int width=0, height=0, r=0;
2050 int linewidth = parseTwip(lu(args, "line"));
2051 char*colorstr = lu(args, "color");
2052 RGBA color = parseColor(colorstr);
2053 char*fillstr = lu(args, "fill");
2060 if(!strcmp(command, "circle"))
2062 else if(!strcmp(command, "filled"))
2066 width = parseTwip(lu(args, "width"));
2067 height = parseTwip(lu(args, "height"));
2068 } else if (type==1) {
2069 r = parseTwip(lu(args, "r"));
2070 } else if (type==2) {
2071 outline = lu(args, "outline");
2074 if(!strcmp(fillstr, "fill"))
2076 if(!strcmp(fillstr, "none"))
2078 if(width<0 || height<0 || linewidth<0 || r<0)
2079 syntaxerror("values width, height, line, r must be positive");
2081 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2082 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2083 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2087 static int c_textshape(map_t*args)
2089 char*name = lu(args, "name");
2090 char*text = lu(args, "text");
2091 char*font = lu(args, "font");
2093 s_textshape(name, font, text);
2097 static int c_swf(map_t*args)
2099 char*name = lu(args, "name");
2100 char*filename = lu(args, "filename");
2101 char*command = lu(args, "commandname");
2102 if(!strcmp(command, "shape"))
2103 warning("Please use .swf instead of .shape");
2104 s_includeswf(name, filename);
2108 static int c_font(map_t*args)
2110 char*name = lu(args, "name");
2111 char*filename = lu(args, "filename");
2112 s_font(name, filename);
2116 static int c_sound(map_t*args)
2118 char*name = lu(args, "name");
2119 char*filename = lu(args, "filename");
2120 s_sound(name, filename);
2124 static int c_text(map_t*args)
2126 char*name = lu(args, "name");
2127 char*text = lu(args, "text");
2128 char*font = lu(args, "font");
2129 float size = parsePercent(lu(args, "size"));
2130 RGBA color = parseColor(lu(args, "color"));
2131 s_text(name, font, text, (int)(size*100), color);
2135 static int c_soundtrack(map_t*args)
2140 static int c_image(map_t*args)
2142 char*command = lu(args, "commandname");
2143 char*name = lu(args, "name");
2144 char*filename = lu(args, "filename");
2145 if(!strcmp(command,"jpeg")) {
2146 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2147 s_image(name, "jpeg", filename, quality);
2149 s_image(name, "png", filename, 0);
2154 static int c_outline(map_t*args)
2156 char*name = lu(args, "name");
2157 char*format = lu(args, "format");
2161 syntaxerror("colon (:) expected");
2163 s_outline(name, format, text);
2167 int fakechar(map_t*args)
2169 char*name = lu(args, "name");
2170 s_box(name, 0, 0, black, 20, 0);
2174 static int c_egon(map_t*args) {return fakechar(args);}
2175 static int c_button(map_t*args) {
2176 char*name = lu(args, "name");
2180 static int current_button_flags = 0;
2181 static int c_on_press(map_t*args)
2183 char*position = lu(args, "position");
2184 if(!strcmp(position, "inside")) {
2185 current_button_flags |= BC_OVERUP_OVERDOWN;
2186 } else if(!strcmp(position, "outside")) {
2187 //current_button_flags |= BC_IDLE_OUTDOWN;
2188 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2189 } else if(!strcmp(position, "anywhere")) {
2190 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2194 if(type == RAWDATA) {
2196 s_buttonaction(current_button_flags, action);
2197 current_button_flags = 0;
2203 static int c_on_release(map_t*args)
2205 char*position = lu(args, "position");
2206 if(!strcmp(position, "inside")) {
2207 current_button_flags |= BC_OVERDOWN_OVERUP;
2208 } else if(!strcmp(position, "outside")) {
2209 current_button_flags |= BC_OUTDOWN_IDLE;
2210 } else if(!strcmp(position, "anywhere")) {
2211 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2215 if(type == RAWDATA) {
2217 s_buttonaction(current_button_flags, action);
2218 current_button_flags = 0;
2224 static int c_on_move_in(map_t*args)
2226 char*position = lu(args, "state");
2227 if(!strcmp(position, "pressed")) {
2228 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2229 } else if(!strcmp(position, "not_pressed")) {
2230 current_button_flags |= BC_IDLE_OVERUP;
2231 } else if(!strcmp(position, "any")) {
2232 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2236 if(type == RAWDATA) {
2238 s_buttonaction(current_button_flags, action);
2239 current_button_flags = 0;
2245 static int c_on_move_out(map_t*args)
2247 char*position = lu(args, "state");
2248 if(!strcmp(position, "pressed")) {
2249 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2250 } else if(!strcmp(position, "not_pressed")) {
2251 current_button_flags |= BC_OVERUP_IDLE;
2252 } else if(!strcmp(position, "any")) {
2253 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2257 if(type == RAWDATA) {
2259 s_buttonaction(current_button_flags, action);
2260 current_button_flags = 0;
2266 static int c_on_key(map_t*args)
2268 char*key = lu(args, "key");
2269 if(strlen(key)==1) {
2272 current_button_flags |= 0x4000 + (key[0]*0x200);
2274 syntaxerror("invalid character: %c"+key[0]);
2279 <ctrl-x> = 0x200*(x-'a')
2283 syntaxerror("invalid key: %s",key);
2287 if(type == RAWDATA) {
2289 s_buttonaction(current_button_flags, action);
2290 current_button_flags = 0;
2297 static int c_edittext(map_t*args) {return fakechar(args);}
2299 static int c_morphshape(map_t*args) {return fakechar(args);}
2300 static int c_movie(map_t*args) {return fakechar(args);}
2302 static int c_texture(map_t*args) {return 0;}
2304 static int c_action(map_t*args)
2307 if(type != RAWDATA) {
2308 syntaxerror("colon (:) expected");
2318 command_func_t* func;
2321 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2322 {"frame", c_frame, "n=<plus>1 @cut=no"},
2323 // "import" type stuff
2324 {"swf", c_swf, "name filename"},
2325 {"shape", c_swf, "name filename"},
2326 {"jpeg", c_image, "name filename quality=80%"},
2327 {"png", c_image, "name filename"},
2328 {"movie", c_movie, "name filename"},
2329 {"sound", c_sound, "name filename"},
2330 {"font", c_font, "name filename"},
2331 {"soundtrack", c_soundtrack, "filename"},
2333 // generators of primitives
2335 {"point", c_point, "name x=0 y=0"},
2336 {"gradient", c_gradient, "name @radial=0"},
2337 {"outline", c_outline, "name format=simple"},
2338 {"textshape", c_textshape, "name text font"},
2340 // character generators
2341 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2342 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2343 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2345 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2346 {"text", c_text, "name text font size=100% color=white"},
2347 {"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"},
2348 {"morphshape", c_morphshape, "name start end"},
2349 {"button", c_button, "name"},
2350 {"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="},
2351 {"on_press", c_on_press, "position=inside"},
2352 {"on_release", c_on_release, "position=anywhere"},
2353 {"on_move_in", c_on_move_out, "state=not_pressed"},
2354 {"on_move_out", c_on_move_out, "state=not_pressed"},
2355 {"on_key", c_on_key, "key=any"},
2358 {"play", c_play, "sound loop=0 @nomultiple=0"},
2359 {"stop", c_stop, "sound"},
2361 // object placement tags
2362 {"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="},
2363 {"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="},
2364 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2365 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2366 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2367 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2368 {"del", c_del, "name"},
2369 // virtual object placement
2370 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2372 // commands which start a block
2373 //startclip (see above)
2374 {"sprite", c_sprite, "name"},
2375 {"action", c_action, ""},
2381 static map_t parseArguments(char*command, char*pattern)
2397 string_set(&t1, "commandname");
2398 string_set(&t2, command);
2399 map_put(&result, t1, t2);
2401 if(!pattern || !*pattern)
2408 if(!strncmp("<i> ", x, 3)) {
2410 if(type == COMMAND || type == RAWDATA) {
2412 syntaxerror("character name expected");
2414 name[pos].str = "instance";
2416 value[pos].str = text;
2417 value[pos].len = strlen(text);
2421 if(type == ASSIGNMENT)
2424 name[pos].str = "character";
2426 value[pos].str = text;
2427 value[pos].len = strlen(text);
2435 isboolean[pos] = (x[0] =='@');
2448 name[pos].len = d-x;
2453 name[pos].len = e-x;
2454 value[pos].str = e+1;
2455 value[pos].len = d-e-1;
2463 /* for(t=0;t<len;t++) {
2464 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2465 isboolean[t]?"(boolean)":"");
2470 if(type == RAWDATA || type == COMMAND) {
2475 // first, search for boolean arguments
2476 for(pos=0;pos<len;pos++)
2478 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2480 if(type == ASSIGNMENT)
2482 value[pos].str = text;
2483 value[pos].len = strlen(text);
2484 /*printf("setting boolean parameter %s (to %s)\n",
2485 strdup_n(name[pos], namelen[pos]),
2486 strdup_n(value[pos], valuelen[pos]));*/
2491 // second, search for normal arguments
2493 for(pos=0;pos<len;pos++)
2495 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2496 (type != ASSIGNMENT && !set[pos])) {
2498 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2500 if(type == ASSIGNMENT)
2503 value[pos].str = text;
2504 value[pos].len = strlen(text);
2506 printf("setting parameter %s (to %s)\n",
2507 strdup_n(name[pos].str, name[pos].len),
2508 strdup_n(value[pos].str, value[pos].len));
2514 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2518 for(t=0;t<len;t++) {
2519 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2522 for(t=0;t<len;t++) {
2523 if(value[t].str && value[t].str[0] == '*') {
2524 //relative default- take value from some other parameter
2526 for(s=0;s<len;s++) {
2527 if(value[s].len == value[t].len-1 &&
2528 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2529 value[t].str = value[s].str;
2532 if(value[t].str == 0) {
2534 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2538 /* ok, now construct the dictionary from the parameters */
2542 map_put(&result, name[t], value[t]);
2546 static void parseArgumentsForCommand(char*command)
2551 msg("<verbose> parse Command: %s (line %d)", command, line);
2553 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2554 if(!strcmp(arguments[t].command, command)) {
2556 /* ugly hack- will be removed soon (once documentation and .sc generating
2557 utilities have been changed) */
2558 if(!strcmp(command, "swf") && !stackpos) {
2559 warning("Please use .flash instead of .swf- this will be mandatory soon");
2564 args = parseArguments(command, arguments[t].arguments);
2570 syntaxerror("command %s not known", command);
2572 // catch missing .flash directives at the beginning of a file
2573 if(strcmp(command, "flash") && !stackpos)
2575 syntaxerror("No movie defined- use .flash first");
2579 printf(".%s\n", command);fflush(stdout);
2580 map_dump(&args, stdout, "\t");fflush(stdout);
2583 (*arguments[nr].func)(&args);
2585 /*if(!strcmp(command, "button") ||
2586 !strcmp(command, "action")) {
2589 if(type == COMMAND) {
2590 if(!strcmp(text, "end"))
2604 int main (int argc,char ** argv)
2607 processargs(argc, argv);
2608 initLog(0,-1,0,0,-1,verbose);
2611 args_callback_usage(argv[0]);
2615 file = generateTokens(filename);
2617 printf("parser returned error.\n");
2623 while(!noMoreTokens()) {
2626 syntaxerror("command expected");
2627 parseArgumentsForCommand(text);