2 Compiles swf code (.sc) files into .swf files.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/drawer.h"
32 #include "../lib/log.h"
33 #include "../lib/args.h"
40 static char * filename = 0;
41 static char * outputname = "output.swf";
42 static int verbose = 2;
43 static int override_outputname = 0;
45 static struct options_t options[] = {
53 int args_callback_option(char*name,char*val)
55 if(!strcmp(name, "V")) {
56 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
59 else if(!strcmp(name, "o")) {
61 override_outputname = 1;
64 else if(!strcmp(name, "v")) {
69 printf("Unknown option: -%s\n", name);
74 int args_callback_longoption(char*name,char*val)
76 return args_long2shortoption(options, name, val);
78 void args_callback_usage(char *name)
81 printf("Usage: %s [-o file.swf] file.sc\n", name);
83 printf("-h , --help Print short help message and exit\n");
84 printf("-V , --version Print version info and exit\n");
85 printf("-v , --verbose Increase verbosity. \n");
86 printf("-o , --output <filename> Set output file to <filename>.\n");
89 int args_callback_command(char*name,char*val)
92 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
99 static struct token_t* file;
108 static void syntaxerror(char*format, ...)
112 va_start(arglist, format);
113 vsprintf(buf, format, arglist);
115 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
119 static void warning(char*format, ...)
123 va_start(arglist, format);
124 vsprintf(buf, format, arglist);
126 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
129 static void readToken()
131 type = file[pos].type;
133 syntaxerror("unexpected end of file");
135 text = file[pos].text;
136 textlen = strlen(text);
137 line = file[pos].line;
138 column = file[pos].column;
140 //printf("---> %d(%s) %s\n", type, type_names[type], text);
143 static void pushBack()
146 if(!pos) syntaxerror("internal error 3");
151 textlen = strlen(text);
154 column = file[p].column;
157 static int noMoreTokens()
159 if(file[pos].type == END)
164 // ------------------------------ swf routines ----------------------------
168 int type; //0=swf, 1=sprite, 2=clip, 3=button
174 /* for sprites (1): */
180 dictionary_t oldinstances;
185 static int stackpos = 0;
187 static dictionary_t characters;
188 static dictionary_t images;
189 static dictionary_t outlines;
190 static dictionary_t gradients;
191 static char idmap[65536];
192 static TAG*tag = 0; //current tag
194 static int id; //current character id
195 static int currentframe; //current frame in current level
196 static SRECT currentrect; //current bounding box in current level
197 static U16 currentdepth;
198 static dictionary_t instances;
199 static dictionary_t fonts;
200 static dictionary_t sounds;
202 typedef struct _parameters {
204 float scalex, scaley;
212 typedef struct _character {
218 typedef struct _instance {
219 character_t*character;
221 parameters_t parameters;
222 TAG* lastTag; //last tag which set the object
223 U16 lastFrame; //frame lastTag is in
226 typedef struct _outline {
231 typedef struct _gradient {
236 static void character_init(character_t*c)
238 memset(c, 0, sizeof(character_t));
240 static character_t* character_new()
243 c = (character_t*)malloc(sizeof(character_t));
247 static void instance_init(instance_t*i)
249 memset(i, 0, sizeof(instance_t));
251 static instance_t* instance_new()
254 c = (instance_t*)malloc(sizeof(instance_t));
259 static void incrementid()
263 syntaxerror("Out of character ids.");
268 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
270 character_t* c = character_new();
272 c->definingTag = ctag;
275 if(dictionary_lookup(&characters, name))
276 syntaxerror("character %s defined twice", name);
277 dictionary_put2(&characters, name, c);
279 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
281 swf_SetString(tag, name);
282 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
285 swf_SetString(tag, name);
287 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
289 character_t* c = character_new();
290 c->definingTag = ctag;
294 if(dictionary_lookup(&images, name))
295 syntaxerror("image %s defined twice", name);
296 dictionary_put2(&images, name, c);
298 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
300 instance_t* i = instance_new();
303 //swf_GetMatrix(0, &i->matrix);
304 if(dictionary_lookup(&instances, name))
305 syntaxerror("object %s defined twice", name);
306 dictionary_put2(&instances, name, i);
310 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
313 p->scalex = scalex; p->scaley = scaley;
314 p->pin = pin; p->pivot = pivot;
315 p->rotate = rotate; p->cxform = cxform;
319 static void parameters_clear(parameters_t*p)
322 p->scalex = 1.0; p->scaley = 1.0;
323 p->pin.x = 1; p->pin.y = 0;
324 p->pivot.x = 0; p->pivot.y = 0;
327 swf_GetCXForm(0, &p->cxform, 1);
330 static void makeMatrix(MATRIX*m, parameters_t*p)
339 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
340 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
341 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
342 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
344 m->sx = (int)(sx*65536+0.5);
345 m->r1 = (int)(r1*65536+0.5);
346 m->r0 = (int)(r0*65536+0.5);
347 m->sy = (int)(sy*65536+0.5);
351 h = swf_TurnPoint(p->pin, m);
356 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
361 r = swf_TurnRect(rect, &m);
362 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
363 currentrect.xmax == 0 && currentrect.ymax == 0)
366 swf_ExpandRect2(¤trect, &r);
370 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
372 SWF*swf = (SWF*)malloc(sizeof(SWF));
375 syntaxerror(".swf blocks can't be nested");
377 memset(swf, 0, sizeof(swf));
378 swf->fileVersion = version;
380 swf->frameRate = fps;
381 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
382 swf->compressed = compress;
383 swf_SetRGB(tag,&background);
385 if(stackpos==sizeof(stack)/sizeof(stack[0]))
386 syntaxerror("too many levels of recursion");
388 dictionary_init(&characters);
389 dictionary_init(&images);
390 dictionary_init(&outlines);
391 dictionary_init(&gradients);
392 dictionary_init(&instances);
393 dictionary_init(&fonts);
394 dictionary_init(&sounds);
396 memset(&stack[stackpos], 0, sizeof(stack[0]));
397 stack[stackpos].type = 0;
398 stack[stackpos].filename = strdup(name);
399 stack[stackpos].swf = swf;
400 stack[stackpos].oldframe = -1;
405 memset(¤trect, 0, sizeof(currentrect));
408 memset(idmap, 0, sizeof(idmap));
412 void s_sprite(char*name)
414 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
415 swf_SetU16(tag, id); //id
416 swf_SetU16(tag, 0); //frames
418 memset(&stack[stackpos], 0, sizeof(stack[0]));
419 stack[stackpos].type = 1;
420 stack[stackpos].oldframe = currentframe;
421 stack[stackpos].olddepth = currentdepth;
422 stack[stackpos].oldrect = currentrect;
423 stack[stackpos].oldinstances = instances;
424 stack[stackpos].tag = tag;
425 stack[stackpos].id = id;
426 stack[stackpos].name = strdup(name);
428 /* FIXME: those four fields should be bundled together */
429 dictionary_init(&instances);
432 memset(¤trect, 0, sizeof(currentrect));
438 typedef struct _buttonrecord
446 typedef struct _button
450 buttonrecord_t records[4];
453 static button_t mybutton;
455 void s_button(char*name)
457 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
458 swf_SetU16(tag, id); //id
459 swf_ButtonSetFlags(tag, 0); //menu=no
461 memset(&mybutton, 0, sizeof(mybutton));
463 memset(&stack[stackpos], 0, sizeof(stack[0]));
464 stack[stackpos].type = 3;
465 stack[stackpos].tag = tag;
466 stack[stackpos].id = id;
467 stack[stackpos].name = strdup(name);
468 stack[stackpos].oldrect = currentrect;
469 memset(¤trect, 0, sizeof(currentrect));
474 void s_buttonput(char*character, char*as, parameters_t p)
476 character_t* c = dictionary_lookup(&characters, character);
480 syntaxerror("character %s not known (in .shape %s)", character, character);
482 if(mybutton.endofshapes) {
483 syntaxerror("a .do may not precede a .show", character, character);
486 m = s_instancepos(c->size, &p);
494 if(strstr(as, "idle")) mybutton.records[0]=r;
495 if(strstr(as, "hover")) mybutton.records[1]=r;
496 if(strstr(as, "pressed")) mybutton.records[2]=r;
497 if(strstr(as, "area")) mybutton.records[3]=r;
499 static void setbuttonrecords(TAG*tag)
501 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
502 if(!mybutton.endofshapes) {
505 if(!mybutton.records[3].set) {
506 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
510 if(mybutton.records[t].set)
511 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
513 swf_SetU8(tag,0); // end of button records
514 mybutton.endofshapes = 1;
518 void s_buttonaction(int flags, char*action)
524 setbuttonrecords(stack[stackpos-1].tag);
526 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
528 syntaxerror("Couldn't compile ActionScript");
531 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
532 swf_ActionSet(stack[stackpos-1].tag, a);
533 mybutton.nr_actions++;
538 static void s_endButton()
540 setbuttonrecords(stack[stackpos-1].tag);
543 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
545 SRECT r = currentrect;
547 tag = stack[stackpos].tag;
548 currentrect = stack[stackpos].oldrect;
550 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
551 free(stack[stackpos].name);
554 TAG* removeFromTo(TAG*from, TAG*to)
556 TAG*save = from->prev;
558 TAG*next = from->next;
566 static void s_endSprite()
568 SRECT r = currentrect;
570 if(stack[stackpos].cut)
571 tag = removeFromTo(stack[stackpos].cut, tag);
575 /* TODO: before clearing, prepend "<spritename>." to names and
576 copy into old instances dict */
577 dictionary_clear(&instances);
579 currentframe = stack[stackpos].oldframe;
580 currentrect = stack[stackpos].oldrect;
581 currentdepth = stack[stackpos].olddepth;
582 instances = stack[stackpos].oldinstances;
584 tag = swf_InsertTag(tag, ST_END);
586 tag = stack[stackpos].tag;
589 syntaxerror("internal error(7)");
591 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
592 free(stack[stackpos].name);
595 static void s_endSWF()
601 if(stack[stackpos].cut)
602 tag = removeFromTo(stack[stackpos].cut, tag);
606 swf = stack[stackpos].swf;
607 filename = stack[stackpos].filename;
609 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
611 tag = swf_InsertTag(tag, ST_END);
613 swf_OptimizeTagOrder(swf);
615 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
616 swf->movieSize = currentrect; /* "autocrop" */
619 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
620 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
621 swf->movieSize.ymax += 20;
624 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
626 syntaxerror("couldn't create output file %s", filename);
629 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
631 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
635 dictionary_clear(&instances);
636 dictionary_clear(&characters);
637 dictionary_clear(&images);
638 dictionary_clear(&outlines);
639 dictionary_clear(&gradients);
640 dictionary_clear(&fonts);
641 dictionary_clear(&sounds);
651 if(stack[stackpos-1].type == 0)
652 syntaxerror("End of file encountered in .flash block");
653 if(stack[stackpos-1].type == 1)
654 syntaxerror("End of file encountered in .sprite block");
655 if(stack[stackpos-1].type == 2)
656 syntaxerror("End of file encountered in .clip block");
665 void s_frame(int nr, int cut)
670 for(t=currentframe;t<nr;t++) {
671 tag = swf_InsertTag(tag, ST_SHOWFRAME);
676 syntaxerror("Can't cut, frame empty");
678 stack[stackpos].cut = tag;
684 int parseColor2(char*str, RGBA*color);
686 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
691 if(texture[0] == '#') {
692 parseColor2(texture, &color);
693 return swf_ShapeAddSolidFillStyle(s, &color);
694 } else if((image = dictionary_lookup(&images, texture))) {
696 swf_GetMatrix(0, &m);
697 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
698 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
701 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
702 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
703 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
705 swf_GetMatrix(0, &m);
706 m.sx = (r->xmax - r->xmin)*2;
707 m.sy = (r->ymax - r->ymin)*2;
708 m.tx = r->xmin + (r->xmax - r->xmin)/2;
709 m.ty = r->ymin + (r->ymax - r->ymin)/2;
710 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
711 } else if (parseColor2(texture, &color)) {
712 return swf_ShapeAddSolidFillStyle(s, &color);
714 syntaxerror("not a color/fillstyle: %s", texture);
719 RGBA black={r:0,g:0,b:0,a:0};
720 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
729 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
731 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
734 fs1 = addFillStyle(s, &r2, texture);
737 r.xmin = r2.xmin-linewidth-linewidth/2;
738 r.ymin = r2.ymin-linewidth-linewidth/2;
739 r.xmax = r2.xmax+linewidth+linewidth/2;
740 r.ymax = r2.ymax+linewidth+linewidth/2;
742 swf_SetShapeHeader(tag,s);
743 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
744 swf_ShapeSetLine(tag,s,width,0);
745 swf_ShapeSetLine(tag,s,0,height);
746 swf_ShapeSetLine(tag,s,-width,0);
747 swf_ShapeSetLine(tag,s,0,-height);
748 swf_ShapeSetEnd(tag);
751 s_addcharacter(name, id, tag, r);
755 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
761 outline = dictionary_lookup(&outlines, outlinename);
763 syntaxerror("outline %s not defined", outlinename);
767 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
769 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
771 fs1 = addFillStyle(s, &r2, texture);
773 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
775 rect.xmin = r2.xmin-linewidth-linewidth/2;
776 rect.ymin = r2.ymin-linewidth-linewidth/2;
777 rect.xmax = r2.xmax+linewidth+linewidth/2;
778 rect.ymax = r2.ymax+linewidth+linewidth/2;
780 swf_SetRect(tag,&rect);
781 swf_SetShapeStyles(tag, s);
782 swf_SetShapeBits(tag, outline->shape); //does not count bits!
783 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
786 s_addcharacter(name, id, tag, rect);
790 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
795 r2.xmin = r2.ymin = 0;
799 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
801 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
803 fs1 = addFillStyle(s, &r2, texture);
805 rect.xmin = r2.xmin-linewidth-linewidth/2;
806 rect.ymin = r2.ymin-linewidth-linewidth/2;
807 rect.xmax = r2.xmax+linewidth+linewidth/2;
808 rect.ymax = r2.ymax+linewidth+linewidth/2;
810 swf_SetRect(tag,&rect);
811 swf_SetShapeHeader(tag,s);
812 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
813 swf_ShapeSetCircle(tag, s, r,r,r,r);
814 swf_ShapeSetEnd(tag);
817 s_addcharacter(name, id, tag, rect);
821 void s_textshape(char*name, char*fontname, char*_text)
824 U8*text = (U8*)_text;
828 font = dictionary_lookup(&fonts, fontname);
830 syntaxerror("font \"%s\" not known!", fontname);
832 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
833 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
834 s_box(name, 0, 0, black, 20, 0);
837 g = font->ascii2glyph[text[0]];
839 outline = malloc(sizeof(outline_t));
840 memset(outline, 0, sizeof(outline_t));
841 outline->shape = font->glyph[g].shape;
842 outline->bbox = font->layout->bounds[g];
846 swf_Shape11DrawerInit(&draw, 0);
847 swf_DrawText(&draw, font, _text);
849 outline->shape = swf_ShapeDrawerToShape(&draw);
850 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
854 if(dictionary_lookup(&outlines, name))
855 syntaxerror("outline %s defined twice", name);
856 dictionary_put2(&outlines, name, outline);
859 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
864 font = dictionary_lookup(&fonts, fontname);
866 syntaxerror("font \"%s\" not known!", fontname);
868 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
870 if(!font->numchars) {
871 s_box(name, 0, 0, black, 20, 0);
874 r = swf_SetDefineText(tag, font, &color, text, size);
876 s_addcharacter(name, id, tag, r);
880 /* type: either "jpeg" or "png"
882 void s_image(char*name, char*type, char*filename, int quality)
884 /* an image is actually two folded: 1st bitmap, 2nd character.
885 Both of them can be used separately */
887 /* step 1: the bitmap */
892 warning("image type \"png\" not supported yet!");
893 s_box(name, 0, 0, black, 20, 0);
898 warning("no jpeg support compiled in");
899 s_box(name, 0, 0, black, 20, 0);
902 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
903 swf_SetU16(tag, imageID);
905 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
906 syntaxerror("Image \"%s\" not found, or contains errors", filename);
909 swf_GetJPEGSize(filename, &width, &height);
916 s_addimage(name, id, tag, r);
921 /* step 2: the character */
922 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
924 swf_ShapeSetBitmapRect(tag, imageID, width, height);
926 s_addcharacter(name, id, tag, r);
930 void dumpSWF(SWF*swf)
932 TAG* tag = swf->firstTag;
933 printf("vvvvvvvvvvvvvvvvvvvvv\n");
935 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
938 printf("^^^^^^^^^^^^^^^^^^^^^\n");
941 void s_font(char*name, char*filename)
944 font = swf_LoadFont(filename);
947 warning("Couldn't open font file \"%s\"", filename);
948 font = (SWFFONT*)malloc(sizeof(SWFFONT));
949 memset(font, 0, sizeof(SWFFONT));
950 dictionary_put2(&fonts, name, font);
956 /* fix the layout. Only needed for old fonts */
958 for(t=0;t<font->numchars;t++) {
959 font->glyph[t].advance = 0;
962 swf_FontCreateLayout(font);
966 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
967 swf_FontSetDefine2(tag, font);
970 if(dictionary_lookup(&fonts, name))
971 syntaxerror("font %s defined twice", name);
972 dictionary_put2(&fonts, name, font);
977 typedef struct _sound_t
983 void s_sound(char*name, char*filename)
985 struct WAV wav, wav2;
990 if(!readWAV(filename, &wav)) {
991 warning("Couldn't read wav file \"%s\"", filename);
995 convertWAV2mono(&wav, &wav2, 44100);
996 samples = (U16*)wav2.data;
997 numsamples = wav2.size/2;
1001 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1002 swf_SetU16(tag, id); //id
1003 swf_SetSoundDefine(tag, samples, numsamples);
1005 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1009 if(dictionary_lookup(&sounds, name))
1010 syntaxerror("sound %s defined twice", name);
1011 dictionary_put2(&sounds, name, sound);
1019 static char* gradient_getToken(const char**p)
1023 while(**p && strchr(" \t\n\r", **p)) {
1027 while(**p && !strchr(" \t\n\r", **p)) {
1030 result = malloc((*p)-start+1);
1031 memcpy(result,start,(*p)-start+1);
1032 result[(*p)-start] = 0;
1036 float parsePercent(char*str);
1037 RGBA parseColor(char*str);
1039 GRADIENT parseGradient(const char*str)
1042 const char* p = str;
1043 memset(&gradient, 0, sizeof(GRADIENT));
1045 char*posstr,*colorstr;
1048 posstr = gradient_getToken(&p);
1051 pos = parsePercent(posstr);
1052 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1053 colorstr = gradient_getToken(&p);
1054 color = parseColor(colorstr);
1055 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1056 warning("gradient record too big- max size is 8, rest ignored");
1059 gradient.ratios[gradient.num] = (int)(pos*255.0);
1060 gradient.rgba[gradient.num] = color;
1068 void s_gradient(char*name, const char*text, int radial)
1070 gradient_t* gradient;
1071 gradient = malloc(sizeof(gradient_t));
1072 memset(gradient, 0, sizeof(gradient_t));
1073 gradient->gradient = parseGradient(text);
1074 gradient->radial = radial;
1076 if(dictionary_lookup(&gradients, name))
1077 syntaxerror("gradient %s defined twice", name);
1078 dictionary_put2(&gradients, name, gradient);
1081 void s_action(const char*text)
1084 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1086 syntaxerror("Couldn't compile ActionScript");
1089 tag = swf_InsertTag(tag, ST_DOACTION);
1091 swf_ActionSet(tag, a);
1096 void s_outline(char*name, char*format, char*source)
1105 swf_Shape11DrawerInit(&draw, 0);
1106 draw_string(&draw, source);
1108 shape = swf_ShapeDrawerToShape(&draw);
1109 //shape2 = swf_ShapeToShape2(shape);
1110 //bounds = swf_GetShapeBoundingBox(shape2);
1111 //swf_Shape2Free(shape2);
1112 bounds = swf_ShapeDrawerGetBBox(&draw);
1113 draw.dealloc(&draw);
1115 outline = (outline_t*)malloc(sizeof(outline_t));
1116 memset(outline, 0, sizeof(outline_t));
1117 outline->shape = shape;
1118 outline->bbox = bounds;
1120 if(dictionary_lookup(&outlines, name))
1121 syntaxerror("outline %s defined twice", name);
1122 dictionary_put2(&outlines, name, outline);
1125 void s_playsound(char*name, int loops, int nomultiple, int stop)
1127 sound_t* sound = dictionary_lookup(&sounds, name);
1130 syntaxerror("Don't know anything about sound \"%s\"", name);
1132 tag = swf_InsertTag(tag, ST_STARTSOUND);
1133 swf_SetU16(tag, sound->id); //id
1134 memset(&info, 0, sizeof(info));
1137 info.nomultiple = nomultiple;
1138 swf_SetSoundInfo(tag, &info);
1141 void s_includeswf(char*name, char*filename)
1149 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1150 f = open(filename,O_RDONLY);
1152 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1153 s_box(name, 0, 0, black, 20, 0);
1156 if (swf_ReadSWF(f,&swf)<0) {
1157 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1158 s_box(name, 0, 0, black, 20, 0);
1163 /* FIXME: The following sets the bounding Box for the character.
1164 It is wrong for two reasons:
1165 a) It may be too small (in case objects in the movie clip at the borders)
1166 b) it may be too big (because the poor movie never got autocropped)
1170 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1171 swf_SetU16(tag, id);
1174 swf_Relocate(&swf, idmap);
1176 ftag = swf.firstTag;
1180 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1181 if(cutout[t] == ftag->id) {
1185 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1187 if(ftag->id == ST_END)
1191 /* We simply dump all tags right after the sprite
1192 header, relying on the fact that swf_OptimizeTagOrder() will
1193 sort things out for us later.
1194 We also rely on the fact that the imported SWF is well-formed.
1196 tag = swf_InsertTag(tag, ftag->id);
1197 swf_SetBlock(tag, ftag->data, ftag->len);
1201 syntaxerror("Included file %s contains errors", filename);
1202 tag = swf_InsertTag(tag, ST_END);
1206 s_addcharacter(name, id, tag, r);
1209 SRECT s_getCharBBox(char*name)
1211 character_t* c = dictionary_lookup(&characters, name);
1212 if(!c) syntaxerror("character '%s' unknown(2)", name);
1215 SRECT s_getInstanceBBox(char*name)
1217 instance_t * i = dictionary_lookup(&instances, name);
1219 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1221 if(!c) syntaxerror("internal error(5)");
1224 parameters_t s_getParameters(char*name)
1226 instance_t * i = dictionary_lookup(&instances, name);
1227 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1228 return i->parameters;
1230 void s_startclip(char*instance, char*character, parameters_t p)
1232 character_t* c = dictionary_lookup(&characters, character);
1236 syntaxerror("character %s not known", character);
1238 i = s_addinstance(instance, c, currentdepth);
1240 m = s_instancepos(i->character->size, &p);
1242 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1243 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1244 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1246 i->lastFrame= currentframe;
1248 stack[stackpos].tag = tag;
1249 stack[stackpos].type = 2;
1258 swf_SetTagPos(stack[stackpos].tag, 0);
1259 swf_GetPlaceObject(stack[stackpos].tag, &p);
1260 p.clipdepth = currentdepth;
1261 swf_ClearTag(stack[stackpos].tag);
1262 swf_SetPlaceObject(stack[stackpos].tag, &p);
1266 void s_put(char*instance, char*character, parameters_t p)
1268 character_t* c = dictionary_lookup(&characters, character);
1272 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1275 i = s_addinstance(instance, c, currentdepth);
1277 m = s_instancepos(i->character->size, &p);
1279 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1280 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1282 i->lastFrame = currentframe;
1286 void s_jump(char*instance, parameters_t p)
1288 instance_t* i = dictionary_lookup(&instances, instance);
1291 syntaxerror("instance %s not known", instance);
1295 m = s_instancepos(i->character->size, &p);
1297 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1298 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1300 i->lastFrame = currentframe;
1303 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1307 if(num==0 || num==1)
1309 ratio = (float)pos/(float)num;
1311 p.x = (p2->x-p1->x)*ratio + p1->x;
1312 p.y = (p2->y-p1->y)*ratio + p1->y;
1313 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1314 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1315 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1316 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1318 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1319 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1320 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1321 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1323 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1324 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1325 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1326 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1328 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1329 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1330 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1331 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1335 void s_change(char*instance, parameters_t p2)
1337 instance_t* i = dictionary_lookup(&instances, instance);
1341 int frame, allframes;
1343 syntaxerror("instance %s not known", instance);
1347 allframes = currentframe - i->lastFrame - 1;
1349 warning(".change ignored. can only .put/.change an object once per frame.");
1353 m = s_instancepos(i->character->size, &p2);
1354 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1355 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1358 /* o.k., we got the start and end point set. Now iterate though all the
1359 tags in between, inserting object changes after each new frame */
1362 if(!t) syntaxerror("internal error(6)");
1364 while(frame < allframes) {
1365 if(t->id == ST_SHOWFRAME) {
1370 p = s_interpolate(&p1, &p2, frame, allframes);
1371 m = s_instancepos(i->character->size, &p); //needed?
1372 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1373 i->lastFrame = currentframe;
1374 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1376 if(frame == allframes)
1381 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1385 void s_delinstance(char*instance)
1387 instance_t* i = dictionary_lookup(&instances, instance);
1389 syntaxerror("instance %s not known", instance);
1391 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1392 swf_SetU16(tag, i->depth);
1393 dictionary_del(&instances, instance);
1396 void s_qchange(char*instance, parameters_t p)
1403 syntaxerror(".end unexpected");
1404 if(stack[stackpos-1].type == 0)
1406 else if(stack[stackpos-1].type == 1)
1408 else if(stack[stackpos-1].type == 2)
1410 else if(stack[stackpos-1].type == 3)
1412 else syntaxerror("internal error 1");
1415 // ------------------------------------------------------------------------
1417 typedef int command_func_t(map_t*args);
1419 SRECT parseBox(char*str)
1422 float xmin, xmax, ymin, ymax;
1423 char*x = strchr(str, 'x');
1425 if(!strcmp(str, "autocrop")) {
1426 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1430 d1 = strchr(x+1, ':');
1432 d2 = strchr(d1+1, ':');
1434 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1438 else if(d1 && !d2) {
1439 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1445 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1450 r.xmin = (SCOORD)(xmin*20);
1451 r.ymin = (SCOORD)(ymin*20);
1452 r.xmax = (SCOORD)(xmax*20);
1453 r.ymax = (SCOORD)(ymax*20);
1456 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1459 float parseFloat(char*str)
1463 int parseInt(char*str)
1468 if(str[0]=='+' || str[0]=='-')
1472 if(str[t]<'0' || str[t]>'9')
1473 syntaxerror("Not an Integer: \"%s\"", str);
1476 int parseTwip(char*str)
1480 if(str[0]=='+' || str[0]=='-') {
1485 dot = strchr(str, '.');
1489 return sign*parseInt(str)*20;
1491 int l=strlen(++dot);
1493 for(s=str;s<dot-1;s++)
1494 if(*s<'0' || *s>'9')
1495 syntaxerror("Not a coordinate: \"%s\"", str);
1497 if(*s<'0' || *s>'9')
1498 syntaxerror("Not a coordinate: \"%s\"", str);
1500 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1501 warning("precision loss: %s converted to twip", str);
1506 return sign*atoi(str)*20;
1508 return sign*atoi(str)*20+atoi(dot)*2;
1510 return sign*atoi(str)*20+atoi(dot)/5;
1515 int isPoint(char*str)
1517 if(strchr(str, '('))
1523 SPOINT parsePoint(char*str)
1527 int l = strlen(str);
1528 char*comma = strchr(str, ',');
1529 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1530 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1531 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1532 p.x = parseTwip(tmp);
1533 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1534 p.y = parseTwip(tmp);
1538 int parseColor2(char*str, RGBA*color)
1540 int l = strlen(str);
1544 struct {unsigned char r,g,b;char*name;} colors[] =
1545 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1546 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1547 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1548 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1549 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1550 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1551 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1552 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1553 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1554 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1555 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1556 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1560 if(str[0]=='#' && (l==7 || l==9)) {
1561 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1563 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1565 color->r = r; color->g = g; color->b = b; color->a = a;
1568 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1569 if(!strcmp(str, colors[t].name)) {
1574 color->r = r; color->g = g; color->b = b; color->a = a;
1580 RGBA parseColor(char*str)
1583 if(!parseColor2(str, &c))
1584 syntaxerror("Expression '%s' is not a color", str);
1588 typedef struct _muladd {
1593 MULADD parseMulAdd(char*str)
1596 char* str2 = (char*)malloc(strlen(str)+5);
1603 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1604 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1605 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1606 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1607 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1608 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1609 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1610 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1611 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1612 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1614 syntaxerror("'%s' is not a valid color transform expression", str);
1616 m.add = (int)(add*256);
1617 m.mul = (int)(mul*256);
1622 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1624 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1625 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1627 if(a<-32768) a=-32768;
1628 if(a>32767) a=32767;
1629 if(m<-32768) m=-32768;
1630 if(m>32767) m=32767;
1636 float parsePercent(char*str)
1638 int l = strlen(str);
1642 return atoi(str)/100.0;
1644 syntaxerror("Expression '%s' is not a percentage", str);
1647 int isPercent(char*str)
1649 return str[strlen(str)-1]=='%';
1651 int parseNewSize(char*str, int size)
1654 return parsePercent(str)*size;
1656 return (int)(atof(str)*20);
1659 int isColor(char*str)
1662 return parseColor2(str, &c);
1665 static char* lu(map_t* args, char*name)
1667 char* value = map_lookup(args, name);
1669 map_dump(args, stdout, "");
1670 syntaxerror("internal error 2: value %s should be set", name);
1675 static int c_flash(map_t*args)
1677 char* name = lu(args, "name");
1678 char* compressstr = lu(args, "compress");
1679 SRECT bbox = parseBox(lu(args, "bbox"));
1680 int version = parseInt(lu(args, "version"));
1681 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1683 RGBA color = parseColor(lu(args, "background"));
1684 if(!strcmp(name, "!default!") || override_outputname)
1687 if(!strcmp(compressstr, "default"))
1688 compress = version==6;
1689 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1691 else if(!strcmp(compressstr, "no"))
1693 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1695 s_swf(name, bbox, version, fps, compress, color);
1698 int isRelative(char*str)
1700 return !strncmp(str, "<plus>", 6) ||
1701 !strncmp(str, "<minus>", 7);
1703 char* getOffset(char*str)
1705 if(!strncmp(str, "<plus>", 6))
1707 if(!strncmp(str, "<minus>", 7))
1709 syntaxerror("internal error (347)");
1712 int getSign(char*str)
1714 if(!strncmp(str, "<plus>", 6))
1716 if(!strncmp(str, "<minus>", 7))
1718 syntaxerror("internal error (348)");
1721 static dictionary_t points;
1722 static mem_t mpoints;
1723 int points_initialized = 0;
1725 SPOINT getPoint(SRECT r, char*name)
1728 if(!strcmp(name, "center")) {
1730 p.x = (r.xmin + r.xmax)/2;
1731 p.y = (r.ymin + r.ymax)/2;
1735 if(points_initialized)
1736 l = (int)dictionary_lookup(&points, name);
1738 syntaxerror("Invalid point: \"%s\".", name);
1741 return *(SPOINT*)&mpoints.buffer[l];
1743 static int c_gradient(map_t*args)
1745 char*name = lu(args, "name");
1746 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1750 syntaxerror("colon (:) expected");
1752 s_gradient(name, text, radial);
1755 static int c_point(map_t*args)
1757 char*name = lu(args, "name");
1761 if(!points_initialized) {
1762 dictionary_init(&points);
1764 points_initialized = 1;
1766 p.x = parseTwip(lu(args, "x"));
1767 p.y = parseTwip(lu(args, "y"));
1768 pos = mem_put(&mpoints, &p, sizeof(p));
1769 string_set(&s1, name);
1771 dictionary_put(&points, s1, (void*)pos);
1774 static int c_play(map_t*args)
1776 char*name = lu(args, "sound");
1777 char*loop = lu(args, "loop");
1778 char*nomultiple = lu(args, "nomultiple");
1780 if(!strcmp(nomultiple, "nomultiple"))
1783 nm = parseInt(nomultiple);
1785 s_playsound(name, parseInt(loop), nm, 0);
1789 static int c_stop(map_t*args)
1791 char*name = lu(args, "sound");
1792 s_playsound(name, 0,0,1);
1796 static int c_placement(map_t*args, int type)
1798 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1801 char* luminancestr = lu(args, "luminance");
1802 char* scalestr = lu(args, "scale");
1803 char* scalexstr = lu(args, "scalex");
1804 char* scaleystr = lu(args, "scaley");
1805 char* rotatestr = lu(args, "rotate");
1806 char* shearstr = lu(args, "shear");
1807 char* xstr="", *pivotstr="";
1808 char* ystr="", *anglestr="";
1809 char*above = lu(args, "above"); /*FIXME*/
1810 char*below = lu(args, "below");
1811 char* rstr = lu(args, "red");
1812 char* gstr = lu(args, "green");
1813 char* bstr = lu(args, "blue");
1814 char* astr = lu(args, "alpha");
1815 char* pinstr = lu(args, "pin");
1816 char* as = map_lookup(args, "as");
1824 if(type==9) { // (?) .rotate or .arcchange
1825 pivotstr = lu(args, "pivot");
1826 anglestr = lu(args, "angle");
1828 xstr = lu(args, "x");
1829 ystr = lu(args, "y");
1832 luminance = parseMulAdd(luminancestr);
1835 luminance.mul = 256;
1839 if(scalexstr[0]||scaleystr[0])
1840 syntaxerror("scalex/scaley and scale cannot both be set");
1841 scalexstr = scaleystr = scalestr;
1844 if(type == 0 || type == 4) {
1846 character = lu(args, "character");
1847 parameters_clear(&p);
1848 } else if (type == 5) {
1849 character = lu(args, "name");
1850 parameters_clear(&p);
1853 p = s_getParameters(instance);
1858 if(isRelative(xstr)) {
1859 if(type == 0 || type == 4)
1860 syntaxerror("relative x values not allowed for initial put or startclip");
1861 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1863 p.x = parseTwip(xstr);
1867 if(isRelative(ystr)) {
1868 if(type == 0 || type == 4)
1869 syntaxerror("relative y values not allowed for initial put or startclip");
1870 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1872 p.y = parseTwip(ystr);
1876 /* scale, scalex, scaley */
1878 oldbbox = s_getCharBBox(character);
1880 oldbbox = s_getInstanceBBox(instance);
1882 oldwidth = oldbbox.xmax - oldbbox.xmin;
1883 oldheight = oldbbox.ymax - oldbbox.ymin;
1885 if(oldwidth==0) p.scalex = 1.0;
1888 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1892 if(oldheight==0) p.scaley = 1.0;
1895 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1901 if(isRelative(rotatestr)) {
1902 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1904 p.rotate = parseFloat(rotatestr);
1910 if(isRelative(shearstr)) {
1911 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1913 p.shear = parseFloat(shearstr);
1918 if(isPoint(pivotstr))
1919 p.pivot = parsePoint(pivotstr);
1921 p.pivot = getPoint(oldbbox, pivotstr);
1925 p.pin = parsePoint(pinstr);
1927 p.pin = getPoint(oldbbox, pinstr);
1930 /* color transform */
1932 if(rstr[0] || luminancestr[0]) {
1935 r = parseMulAdd(rstr);
1937 r.add = p.cxform.r0;
1938 r.mul = p.cxform.r1;
1940 r = mergeMulAdd(r, luminance);
1941 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1943 if(gstr[0] || luminancestr[0]) {
1946 g = parseMulAdd(gstr);
1948 g.add = p.cxform.g0;
1949 g.mul = p.cxform.g1;
1951 g = mergeMulAdd(g, luminance);
1952 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1954 if(bstr[0] || luminancestr[0]) {
1957 b = parseMulAdd(bstr);
1959 b.add = p.cxform.b0;
1960 b.mul = p.cxform.b1;
1962 b = mergeMulAdd(b, luminance);
1963 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1966 MULADD a = parseMulAdd(astr);
1967 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1971 s_put(instance, character, p);
1973 s_change(instance, p);
1975 s_qchange(instance, p);
1977 s_jump(instance, p);
1979 s_startclip(instance, character, p);
1980 else if(type == 5) {
1982 s_buttonput(character, as, p);
1984 s_buttonput(character, "shape", p);
1989 static int c_put(map_t*args)
1991 c_placement(args, 0);
1994 static int c_change(map_t*args)
1996 c_placement(args, 1);
1999 static int c_qchange(map_t*args)
2001 c_placement(args, 2);
2004 static int c_arcchange(map_t*args)
2006 c_placement(args, 2);
2009 static int c_jump(map_t*args)
2011 c_placement(args, 3);
2014 static int c_startclip(map_t*args)
2016 c_placement(args, 4);
2019 static int c_show(map_t*args)
2021 c_placement(args, 5);
2024 static int c_del(map_t*args)
2026 char*instance = lu(args, "name");
2027 s_delinstance(instance);
2030 static int c_end(map_t*args)
2035 static int c_sprite(map_t*args)
2037 char* name = lu(args, "name");
2041 static int c_frame(map_t*args)
2043 char*framestr = lu(args, "n");
2044 char*cutstr = lu(args, "cut");
2047 if(strcmp(cutstr, "no"))
2049 if(isRelative(framestr)) {
2050 frame = s_getframe();
2051 if(getSign(framestr)<0)
2052 syntaxerror("relative frame expressions must be positive");
2053 frame += parseInt(getOffset(framestr));
2056 frame = parseInt(framestr);
2057 if(s_getframe() >= frame
2058 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2059 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2061 s_frame(frame, cut);
2064 static int c_primitive(map_t*args)
2066 char*name = lu(args, "name");
2067 char*command = lu(args, "commandname");
2068 int width=0, height=0, r=0;
2069 int linewidth = parseTwip(lu(args, "line"));
2070 char*colorstr = lu(args, "color");
2071 RGBA color = parseColor(colorstr);
2072 char*fillstr = lu(args, "fill");
2079 if(!strcmp(command, "circle"))
2081 else if(!strcmp(command, "filled"))
2085 width = parseTwip(lu(args, "width"));
2086 height = parseTwip(lu(args, "height"));
2087 } else if (type==1) {
2088 r = parseTwip(lu(args, "r"));
2089 } else if (type==2) {
2090 outline = lu(args, "outline");
2093 if(!strcmp(fillstr, "fill"))
2095 if(!strcmp(fillstr, "none"))
2097 if(width<0 || height<0 || linewidth<0 || r<0)
2098 syntaxerror("values width, height, line, r must be positive");
2100 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2101 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2102 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2106 static int c_textshape(map_t*args)
2108 char*name = lu(args, "name");
2109 char*text = lu(args, "text");
2110 char*font = lu(args, "font");
2112 s_textshape(name, font, text);
2116 static int c_swf(map_t*args)
2118 char*name = lu(args, "name");
2119 char*filename = lu(args, "filename");
2120 char*command = lu(args, "commandname");
2121 if(!strcmp(command, "shape"))
2122 warning("Please use .swf instead of .shape");
2123 s_includeswf(name, filename);
2127 static int c_font(map_t*args)
2129 char*name = lu(args, "name");
2130 char*filename = lu(args, "filename");
2131 s_font(name, filename);
2135 static int c_sound(map_t*args)
2137 char*name = lu(args, "name");
2138 char*filename = lu(args, "filename");
2139 s_sound(name, filename);
2143 static int c_text(map_t*args)
2145 char*name = lu(args, "name");
2146 char*text = lu(args, "text");
2147 char*font = lu(args, "font");
2148 float size = parsePercent(lu(args, "size"));
2149 RGBA color = parseColor(lu(args, "color"));
2150 s_text(name, font, text, (int)(size*100), color);
2154 static int c_soundtrack(map_t*args)
2159 static int c_image(map_t*args)
2161 char*command = lu(args, "commandname");
2162 char*name = lu(args, "name");
2163 char*filename = lu(args, "filename");
2164 if(!strcmp(command,"jpeg")) {
2165 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2166 s_image(name, "jpeg", filename, quality);
2168 s_image(name, "png", filename, 0);
2173 static int c_outline(map_t*args)
2175 char*name = lu(args, "name");
2176 char*format = lu(args, "format");
2180 syntaxerror("colon (:) expected");
2182 s_outline(name, format, text);
2186 int fakechar(map_t*args)
2188 char*name = lu(args, "name");
2189 s_box(name, 0, 0, black, 20, 0);
2193 static int c_egon(map_t*args) {return fakechar(args);}
2194 static int c_button(map_t*args) {
2195 char*name = lu(args, "name");
2199 static int current_button_flags = 0;
2200 static int c_on_press(map_t*args)
2202 char*position = lu(args, "position");
2203 if(!strcmp(position, "inside")) {
2204 current_button_flags |= BC_OVERUP_OVERDOWN;
2205 } else if(!strcmp(position, "outside")) {
2206 //current_button_flags |= BC_IDLE_OUTDOWN;
2207 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2208 } else if(!strcmp(position, "anywhere")) {
2209 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2213 if(type == RAWDATA) {
2215 s_buttonaction(current_button_flags, action);
2216 current_button_flags = 0;
2222 static int c_on_release(map_t*args)
2224 char*position = lu(args, "position");
2225 if(!strcmp(position, "inside")) {
2226 current_button_flags |= BC_OVERDOWN_OVERUP;
2227 } else if(!strcmp(position, "outside")) {
2228 current_button_flags |= BC_OUTDOWN_IDLE;
2229 } else if(!strcmp(position, "anywhere")) {
2230 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2234 if(type == RAWDATA) {
2236 s_buttonaction(current_button_flags, action);
2237 current_button_flags = 0;
2243 static int c_on_move_in(map_t*args)
2245 char*position = lu(args, "state");
2246 if(!strcmp(position, "pressed")) {
2247 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2248 } else if(!strcmp(position, "not_pressed")) {
2249 current_button_flags |= BC_IDLE_OVERUP;
2250 } else if(!strcmp(position, "any")) {
2251 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2255 if(type == RAWDATA) {
2257 s_buttonaction(current_button_flags, action);
2258 current_button_flags = 0;
2264 static int c_on_move_out(map_t*args)
2266 char*position = lu(args, "state");
2267 if(!strcmp(position, "pressed")) {
2268 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2269 } else if(!strcmp(position, "not_pressed")) {
2270 current_button_flags |= BC_OVERUP_IDLE;
2271 } else if(!strcmp(position, "any")) {
2272 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2276 if(type == RAWDATA) {
2278 s_buttonaction(current_button_flags, action);
2279 current_button_flags = 0;
2285 static int c_on_key(map_t*args)
2287 char*key = lu(args, "key");
2288 if(strlen(key)==1) {
2291 current_button_flags |= 0x4000 + (key[0]*0x200);
2293 syntaxerror("invalid character: %c"+key[0]);
2298 <ctrl-x> = 0x200*(x-'a')
2302 syntaxerror("invalid key: %s",key);
2306 if(type == RAWDATA) {
2308 s_buttonaction(current_button_flags, action);
2309 current_button_flags = 0;
2316 static int c_edittext(map_t*args) {return fakechar(args);}
2318 static int c_morphshape(map_t*args) {return fakechar(args);}
2319 static int c_movie(map_t*args) {return fakechar(args);}
2321 static int c_texture(map_t*args) {return 0;}
2323 static int c_action(map_t*args)
2326 if(type != RAWDATA) {
2327 syntaxerror("colon (:) expected");
2337 command_func_t* func;
2340 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2341 {"frame", c_frame, "n=<plus>1 @cut=no"},
2342 // "import" type stuff
2343 {"swf", c_swf, "name filename"},
2344 {"shape", c_swf, "name filename"},
2345 {"jpeg", c_image, "name filename quality=80%"},
2346 {"png", c_image, "name filename"},
2347 {"movie", c_movie, "name filename"},
2348 {"sound", c_sound, "name filename"},
2349 {"font", c_font, "name filename"},
2350 {"soundtrack", c_soundtrack, "filename"},
2352 // generators of primitives
2354 {"point", c_point, "name x=0 y=0"},
2355 {"gradient", c_gradient, "name @radial=0"},
2356 {"outline", c_outline, "name format=simple"},
2357 {"textshape", c_textshape, "name text font"},
2359 // character generators
2360 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2361 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2362 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2364 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2365 {"text", c_text, "name text font size=100% color=white"},
2366 {"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"},
2367 {"morphshape", c_morphshape, "name start end"},
2368 {"button", c_button, "name"},
2369 {"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="},
2370 {"on_press", c_on_press, "position=inside"},
2371 {"on_release", c_on_release, "position=anywhere"},
2372 {"on_move_in", c_on_move_out, "state=not_pressed"},
2373 {"on_move_out", c_on_move_out, "state=not_pressed"},
2374 {"on_key", c_on_key, "key=any"},
2377 {"play", c_play, "sound loop=0 @nomultiple=0"},
2378 {"stop", c_stop, "sound"},
2380 // object placement tags
2381 {"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="},
2382 {"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="},
2383 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2384 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2385 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2386 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2387 {"del", c_del, "name"},
2388 // virtual object placement
2389 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2391 // commands which start a block
2392 //startclip (see above)
2393 {"sprite", c_sprite, "name"},
2394 {"action", c_action, ""},
2400 static map_t parseArguments(char*command, char*pattern)
2416 string_set(&t1, "commandname");
2417 string_set(&t2, command);
2418 map_put(&result, t1, t2);
2420 if(!pattern || !*pattern)
2427 if(!strncmp("<i> ", x, 3)) {
2429 if(type == COMMAND || type == RAWDATA) {
2431 syntaxerror("character name expected");
2433 name[pos].str = "instance";
2435 value[pos].str = text;
2436 value[pos].len = strlen(text);
2440 if(type == ASSIGNMENT)
2443 name[pos].str = "character";
2445 value[pos].str = text;
2446 value[pos].len = strlen(text);
2454 isboolean[pos] = (x[0] =='@');
2467 name[pos].len = d-x;
2472 name[pos].len = e-x;
2473 value[pos].str = e+1;
2474 value[pos].len = d-e-1;
2482 /* for(t=0;t<len;t++) {
2483 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2484 isboolean[t]?"(boolean)":"");
2489 if(type == RAWDATA || type == COMMAND) {
2494 // first, search for boolean arguments
2495 for(pos=0;pos<len;pos++)
2497 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2499 if(type == ASSIGNMENT)
2501 value[pos].str = text;
2502 value[pos].len = strlen(text);
2503 /*printf("setting boolean parameter %s (to %s)\n",
2504 strdup_n(name[pos], namelen[pos]),
2505 strdup_n(value[pos], valuelen[pos]));*/
2510 // second, search for normal arguments
2512 for(pos=0;pos<len;pos++)
2514 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2515 (type != ASSIGNMENT && !set[pos])) {
2517 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2519 if(type == ASSIGNMENT)
2522 value[pos].str = text;
2523 value[pos].len = strlen(text);
2525 printf("setting parameter %s (to %s)\n",
2526 strdup_n(name[pos].str, name[pos].len),
2527 strdup_n(value[pos].str, value[pos].len));
2533 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2537 for(t=0;t<len;t++) {
2538 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2541 for(t=0;t<len;t++) {
2542 if(value[t].str && value[t].str[0] == '*') {
2543 //relative default- take value from some other parameter
2545 for(s=0;s<len;s++) {
2546 if(value[s].len == value[t].len-1 &&
2547 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2548 value[t].str = value[s].str;
2551 if(value[t].str == 0) {
2553 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2557 /* ok, now construct the dictionary from the parameters */
2561 map_put(&result, name[t], value[t]);
2565 static void parseArgumentsForCommand(char*command)
2570 msg("<verbose> parse Command: %s (line %d)", command, line);
2572 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2573 if(!strcmp(arguments[t].command, command)) {
2575 /* ugly hack- will be removed soon (once documentation and .sc generating
2576 utilities have been changed) */
2577 if(!strcmp(command, "swf") && !stackpos) {
2578 warning("Please use .flash instead of .swf- this will be mandatory soon");
2583 args = parseArguments(command, arguments[t].arguments);
2589 syntaxerror("command %s not known", command);
2591 // catch missing .flash directives at the beginning of a file
2592 if(strcmp(command, "flash") && !stackpos)
2594 syntaxerror("No movie defined- use .flash first");
2598 printf(".%s\n", command);fflush(stdout);
2599 map_dump(&args, stdout, "\t");fflush(stdout);
2602 (*arguments[nr].func)(&args);
2604 /*if(!strcmp(command, "button") ||
2605 !strcmp(command, "action")) {
2608 if(type == COMMAND) {
2609 if(!strcmp(text, "end"))
2623 int main (int argc,char ** argv)
2626 processargs(argc, argv);
2627 initLog(0,-1,0,0,-1,verbose);
2630 args_callback_usage(argv[0]);
2634 file = generateTokens(filename);
2636 printf("parser returned error.\n");
2642 while(!noMoreTokens()) {
2645 syntaxerror("command expected");
2646 parseArgumentsForCommand(text);