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 int s_swf3action(char*name, char*action)
1099 instance_t* object = dictionary_lookup(&instances, name);
1103 a = action_SetTarget(0, name);
1104 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1105 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1106 else if(!strcmp(action, "stop")) a = action_Stop(a);
1107 else if(!strcmp(action, "play")) a = action_Play(a);
1108 a = action_SetTarget(a, "");
1111 tag = swf_InsertTag(tag, ST_DOACTION);
1112 swf_ActionSet(tag, a);
1117 void s_outline(char*name, char*format, char*source)
1126 swf_Shape11DrawerInit(&draw, 0);
1127 draw_string(&draw, source);
1129 shape = swf_ShapeDrawerToShape(&draw);
1130 //shape2 = swf_ShapeToShape2(shape);
1131 //bounds = swf_GetShapeBoundingBox(shape2);
1132 //swf_Shape2Free(shape2);
1133 bounds = swf_ShapeDrawerGetBBox(&draw);
1134 draw.dealloc(&draw);
1136 outline = (outline_t*)malloc(sizeof(outline_t));
1137 memset(outline, 0, sizeof(outline_t));
1138 outline->shape = shape;
1139 outline->bbox = bounds;
1141 if(dictionary_lookup(&outlines, name))
1142 syntaxerror("outline %s defined twice", name);
1143 dictionary_put2(&outlines, name, outline);
1146 int s_playsound(char*name, int loops, int nomultiple, int stop)
1148 sound_t* sound = dictionary_lookup(&sounds, name);
1153 tag = swf_InsertTag(tag, ST_STARTSOUND);
1154 swf_SetU16(tag, sound->id); //id
1155 memset(&info, 0, sizeof(info));
1158 info.nomultiple = nomultiple;
1159 swf_SetSoundInfo(tag, &info);
1163 void s_includeswf(char*name, char*filename)
1171 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1172 f = open(filename,O_RDONLY);
1174 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1175 s_box(name, 0, 0, black, 20, 0);
1178 if (swf_ReadSWF(f,&swf)<0) {
1179 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1180 s_box(name, 0, 0, black, 20, 0);
1185 /* FIXME: The following sets the bounding Box for the character.
1186 It is wrong for two reasons:
1187 a) It may be too small (in case objects in the movie clip at the borders)
1188 b) it may be too big (because the poor movie never got autocropped)
1192 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1193 swf_SetU16(tag, id);
1196 swf_Relocate(&swf, idmap);
1198 ftag = swf.firstTag;
1202 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1203 if(cutout[t] == ftag->id) {
1207 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1209 if(ftag->id == ST_END)
1213 /* We simply dump all tags right after the sprite
1214 header, relying on the fact that swf_OptimizeTagOrder() will
1215 sort things out for us later.
1216 We also rely on the fact that the imported SWF is well-formed.
1218 tag = swf_InsertTag(tag, ftag->id);
1219 swf_SetBlock(tag, ftag->data, ftag->len);
1223 syntaxerror("Included file %s contains errors", filename);
1224 tag = swf_InsertTag(tag, ST_END);
1228 s_addcharacter(name, id, tag, r);
1231 SRECT s_getCharBBox(char*name)
1233 character_t* c = dictionary_lookup(&characters, name);
1234 if(!c) syntaxerror("character '%s' unknown(2)", name);
1237 SRECT s_getInstanceBBox(char*name)
1239 instance_t * i = dictionary_lookup(&instances, name);
1241 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1243 if(!c) syntaxerror("internal error(5)");
1246 parameters_t s_getParameters(char*name)
1248 instance_t * i = dictionary_lookup(&instances, name);
1249 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1250 return i->parameters;
1252 void s_startclip(char*instance, char*character, parameters_t p)
1254 character_t* c = dictionary_lookup(&characters, character);
1258 syntaxerror("character %s not known", character);
1260 i = s_addinstance(instance, c, currentdepth);
1262 m = s_instancepos(i->character->size, &p);
1264 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1265 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1266 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1268 i->lastFrame= currentframe;
1270 stack[stackpos].tag = tag;
1271 stack[stackpos].type = 2;
1280 swf_SetTagPos(stack[stackpos].tag, 0);
1281 swf_GetPlaceObject(stack[stackpos].tag, &p);
1282 p.clipdepth = currentdepth;
1283 swf_ClearTag(stack[stackpos].tag);
1284 swf_SetPlaceObject(stack[stackpos].tag, &p);
1288 void s_put(char*instance, char*character, parameters_t p)
1290 character_t* c = dictionary_lookup(&characters, character);
1294 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1297 i = s_addinstance(instance, c, currentdepth);
1299 m = s_instancepos(i->character->size, &p);
1301 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1302 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1304 i->lastFrame = currentframe;
1308 void s_jump(char*instance, parameters_t p)
1310 instance_t* i = dictionary_lookup(&instances, instance);
1313 syntaxerror("instance %s not known", instance);
1317 m = s_instancepos(i->character->size, &p);
1319 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1320 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1322 i->lastFrame = currentframe;
1325 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1329 if(num==0 || num==1)
1331 ratio = (float)pos/(float)num;
1333 p.x = (p2->x-p1->x)*ratio + p1->x;
1334 p.y = (p2->y-p1->y)*ratio + p1->y;
1335 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1336 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1337 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1338 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1340 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1341 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1342 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1343 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1345 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1346 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1347 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1348 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1350 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1351 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1352 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1353 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1357 void s_change(char*instance, parameters_t p2)
1359 instance_t* i = dictionary_lookup(&instances, instance);
1363 int frame, allframes;
1365 syntaxerror("instance %s not known", instance);
1369 allframes = currentframe - i->lastFrame - 1;
1371 warning(".change ignored. can only .put/.change an object once per frame.");
1375 m = s_instancepos(i->character->size, &p2);
1376 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1377 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1380 /* o.k., we got the start and end point set. Now iterate though all the
1381 tags in between, inserting object changes after each new frame */
1384 if(!t) syntaxerror("internal error(6)");
1386 while(frame < allframes) {
1387 if(t->id == ST_SHOWFRAME) {
1392 p = s_interpolate(&p1, &p2, frame, allframes);
1393 m = s_instancepos(i->character->size, &p); //needed?
1394 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1395 i->lastFrame = currentframe;
1396 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1398 if(frame == allframes)
1403 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1407 void s_delinstance(char*instance)
1409 instance_t* i = dictionary_lookup(&instances, instance);
1411 syntaxerror("instance %s not known", instance);
1413 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1414 swf_SetU16(tag, i->depth);
1415 dictionary_del(&instances, instance);
1418 void s_qchange(char*instance, parameters_t p)
1425 syntaxerror(".end unexpected");
1426 if(stack[stackpos-1].type == 0)
1428 else if(stack[stackpos-1].type == 1)
1430 else if(stack[stackpos-1].type == 2)
1432 else if(stack[stackpos-1].type == 3)
1434 else syntaxerror("internal error 1");
1437 // ------------------------------------------------------------------------
1439 typedef int command_func_t(map_t*args);
1441 SRECT parseBox(char*str)
1444 float xmin, xmax, ymin, ymax;
1445 char*x = strchr(str, 'x');
1447 if(!strcmp(str, "autocrop")) {
1448 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1452 d1 = strchr(x+1, ':');
1454 d2 = strchr(d1+1, ':');
1456 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1460 else if(d1 && !d2) {
1461 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1467 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1472 r.xmin = (SCOORD)(xmin*20);
1473 r.ymin = (SCOORD)(ymin*20);
1474 r.xmax = (SCOORD)(xmax*20);
1475 r.ymax = (SCOORD)(ymax*20);
1478 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1481 float parseFloat(char*str)
1485 int parseInt(char*str)
1490 if(str[0]=='+' || str[0]=='-')
1494 if(str[t]<'0' || str[t]>'9')
1495 syntaxerror("Not an Integer: \"%s\"", str);
1498 int parseTwip(char*str)
1502 if(str[0]=='+' || str[0]=='-') {
1507 dot = strchr(str, '.');
1511 return sign*parseInt(str)*20;
1513 int l=strlen(++dot);
1515 for(s=str;s<dot-1;s++)
1516 if(*s<'0' || *s>'9')
1517 syntaxerror("Not a coordinate: \"%s\"", str);
1519 if(*s<'0' || *s>'9')
1520 syntaxerror("Not a coordinate: \"%s\"", str);
1522 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1523 warning("precision loss: %s converted to twip", str);
1528 return sign*atoi(str)*20;
1530 return sign*atoi(str)*20+atoi(dot)*2;
1532 return sign*atoi(str)*20+atoi(dot)/5;
1537 int isPoint(char*str)
1539 if(strchr(str, '('))
1545 SPOINT parsePoint(char*str)
1549 int l = strlen(str);
1550 char*comma = strchr(str, ',');
1551 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1552 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1553 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1554 p.x = parseTwip(tmp);
1555 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1556 p.y = parseTwip(tmp);
1560 int parseColor2(char*str, RGBA*color)
1562 int l = strlen(str);
1566 struct {unsigned char r,g,b;char*name;} colors[] =
1567 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1568 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1569 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1570 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1571 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1572 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1573 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1574 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1575 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1576 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1577 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1578 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1582 if(str[0]=='#' && (l==7 || l==9)) {
1583 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1585 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1587 color->r = r; color->g = g; color->b = b; color->a = a;
1590 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1591 if(!strcmp(str, colors[t].name)) {
1596 color->r = r; color->g = g; color->b = b; color->a = a;
1602 RGBA parseColor(char*str)
1605 if(!parseColor2(str, &c))
1606 syntaxerror("Expression '%s' is not a color", str);
1610 typedef struct _muladd {
1615 MULADD parseMulAdd(char*str)
1618 char* str2 = (char*)malloc(strlen(str)+5);
1625 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1626 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1627 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1628 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1629 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1630 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1631 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1632 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1633 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1634 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1636 syntaxerror("'%s' is not a valid color transform expression", str);
1638 m.add = (int)(add*256);
1639 m.mul = (int)(mul*256);
1644 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1646 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1647 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1649 if(a<-32768) a=-32768;
1650 if(a>32767) a=32767;
1651 if(m<-32768) m=-32768;
1652 if(m>32767) m=32767;
1658 float parsePercent(char*str)
1660 int l = strlen(str);
1664 return atoi(str)/100.0;
1666 syntaxerror("Expression '%s' is not a percentage", str);
1669 int isPercent(char*str)
1671 return str[strlen(str)-1]=='%';
1673 int parseNewSize(char*str, int size)
1676 return parsePercent(str)*size;
1678 return (int)(atof(str)*20);
1681 int isColor(char*str)
1684 return parseColor2(str, &c);
1687 static char* lu(map_t* args, char*name)
1689 char* value = map_lookup(args, name);
1691 map_dump(args, stdout, "");
1692 syntaxerror("internal error 2: value %s should be set", name);
1697 static int c_flash(map_t*args)
1699 char* name = lu(args, "name");
1700 char* compressstr = lu(args, "compress");
1701 SRECT bbox = parseBox(lu(args, "bbox"));
1702 int version = parseInt(lu(args, "version"));
1703 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1705 RGBA color = parseColor(lu(args, "background"));
1706 if(!strcmp(name, "!default!") || override_outputname)
1709 if(!strcmp(compressstr, "default"))
1710 compress = version==6;
1711 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1713 else if(!strcmp(compressstr, "no"))
1715 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1717 s_swf(name, bbox, version, fps, compress, color);
1720 int isRelative(char*str)
1722 return !strncmp(str, "<plus>", 6) ||
1723 !strncmp(str, "<minus>", 7);
1725 char* getOffset(char*str)
1727 if(!strncmp(str, "<plus>", 6))
1729 if(!strncmp(str, "<minus>", 7))
1731 syntaxerror("internal error (347)");
1734 int getSign(char*str)
1736 if(!strncmp(str, "<plus>", 6))
1738 if(!strncmp(str, "<minus>", 7))
1740 syntaxerror("internal error (348)");
1743 static dictionary_t points;
1744 static mem_t mpoints;
1745 int points_initialized = 0;
1747 SPOINT getPoint(SRECT r, char*name)
1750 if(!strcmp(name, "center")) {
1752 p.x = (r.xmin + r.xmax)/2;
1753 p.y = (r.ymin + r.ymax)/2;
1757 if(points_initialized)
1758 l = (int)dictionary_lookup(&points, name);
1760 syntaxerror("Invalid point: \"%s\".", name);
1763 return *(SPOINT*)&mpoints.buffer[l];
1765 static int c_gradient(map_t*args)
1767 char*name = lu(args, "name");
1768 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1772 syntaxerror("colon (:) expected");
1774 s_gradient(name, text, radial);
1777 static int c_point(map_t*args)
1779 char*name = lu(args, "name");
1783 if(!points_initialized) {
1784 dictionary_init(&points);
1786 points_initialized = 1;
1788 p.x = parseTwip(lu(args, "x"));
1789 p.y = parseTwip(lu(args, "y"));
1790 pos = mem_put(&mpoints, &p, sizeof(p));
1791 string_set(&s1, name);
1793 dictionary_put(&points, s1, (void*)pos);
1796 static int c_play(map_t*args)
1798 char*name = lu(args, "name");
1799 char*loop = lu(args, "loop");
1800 char*nomultiple = lu(args, "nomultiple");
1802 if(!strcmp(nomultiple, "nomultiple"))
1805 nm = parseInt(nomultiple);
1807 if(s_playsound(name, parseInt(loop), nm, 0)) {
1809 } else if(s_swf3action(name, "play")) {
1815 static int c_stop(map_t*args)
1817 char*name = lu(args, "name");
1819 if(s_playsound(name, 0,0,1)) {
1821 } else if(s_swf3action(name, "stop")) {
1824 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1828 static int c_nextframe(map_t*args)
1830 char*name = lu(args, "name");
1832 if(s_swf3action(name, "nextframe")) {
1835 syntaxerror("I don't know anything about movie \"%s\"", name);
1839 static int c_previousframe(map_t*args)
1841 char*name = lu(args, "name");
1843 if(s_swf3action(name, "previousframe")) {
1846 syntaxerror("I don't know anything about movie \"%s\"", name);
1850 static int c_placement(map_t*args, int type)
1852 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1855 char* luminancestr = lu(args, "luminance");
1856 char* scalestr = lu(args, "scale");
1857 char* scalexstr = lu(args, "scalex");
1858 char* scaleystr = lu(args, "scaley");
1859 char* rotatestr = lu(args, "rotate");
1860 char* shearstr = lu(args, "shear");
1861 char* xstr="", *pivotstr="";
1862 char* ystr="", *anglestr="";
1863 char*above = lu(args, "above"); /*FIXME*/
1864 char*below = lu(args, "below");
1865 char* rstr = lu(args, "red");
1866 char* gstr = lu(args, "green");
1867 char* bstr = lu(args, "blue");
1868 char* astr = lu(args, "alpha");
1869 char* pinstr = lu(args, "pin");
1870 char* as = map_lookup(args, "as");
1878 if(type==9) { // (?) .rotate or .arcchange
1879 pivotstr = lu(args, "pivot");
1880 anglestr = lu(args, "angle");
1882 xstr = lu(args, "x");
1883 ystr = lu(args, "y");
1886 luminance = parseMulAdd(luminancestr);
1889 luminance.mul = 256;
1893 if(scalexstr[0]||scaleystr[0])
1894 syntaxerror("scalex/scaley and scale cannot both be set");
1895 scalexstr = scaleystr = scalestr;
1898 if(type == 0 || type == 4) {
1900 character = lu(args, "character");
1901 parameters_clear(&p);
1902 } else if (type == 5) {
1903 character = lu(args, "name");
1904 parameters_clear(&p);
1907 p = s_getParameters(instance);
1912 if(isRelative(xstr)) {
1913 if(type == 0 || type == 4)
1914 syntaxerror("relative x values not allowed for initial put or startclip");
1915 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1917 p.x = parseTwip(xstr);
1921 if(isRelative(ystr)) {
1922 if(type == 0 || type == 4)
1923 syntaxerror("relative y values not allowed for initial put or startclip");
1924 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1926 p.y = parseTwip(ystr);
1930 /* scale, scalex, scaley */
1932 oldbbox = s_getCharBBox(character);
1934 oldbbox = s_getInstanceBBox(instance);
1936 oldwidth = oldbbox.xmax - oldbbox.xmin;
1937 oldheight = oldbbox.ymax - oldbbox.ymin;
1939 if(oldwidth==0) p.scalex = 1.0;
1942 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1946 if(oldheight==0) p.scaley = 1.0;
1949 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1955 if(isRelative(rotatestr)) {
1956 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1958 p.rotate = parseFloat(rotatestr);
1964 if(isRelative(shearstr)) {
1965 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1967 p.shear = parseFloat(shearstr);
1972 if(isPoint(pivotstr))
1973 p.pivot = parsePoint(pivotstr);
1975 p.pivot = getPoint(oldbbox, pivotstr);
1979 p.pin = parsePoint(pinstr);
1981 p.pin = getPoint(oldbbox, pinstr);
1984 /* color transform */
1986 if(rstr[0] || luminancestr[0]) {
1989 r = parseMulAdd(rstr);
1991 r.add = p.cxform.r0;
1992 r.mul = p.cxform.r1;
1994 r = mergeMulAdd(r, luminance);
1995 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1997 if(gstr[0] || luminancestr[0]) {
2000 g = parseMulAdd(gstr);
2002 g.add = p.cxform.g0;
2003 g.mul = p.cxform.g1;
2005 g = mergeMulAdd(g, luminance);
2006 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2008 if(bstr[0] || luminancestr[0]) {
2011 b = parseMulAdd(bstr);
2013 b.add = p.cxform.b0;
2014 b.mul = p.cxform.b1;
2016 b = mergeMulAdd(b, luminance);
2017 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2020 MULADD a = parseMulAdd(astr);
2021 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2025 s_put(instance, character, p);
2027 s_change(instance, p);
2029 s_qchange(instance, p);
2031 s_jump(instance, p);
2033 s_startclip(instance, character, p);
2034 else if(type == 5) {
2036 s_buttonput(character, as, p);
2038 s_buttonput(character, "shape", p);
2043 static int c_put(map_t*args)
2045 c_placement(args, 0);
2048 static int c_change(map_t*args)
2050 c_placement(args, 1);
2053 static int c_qchange(map_t*args)
2055 c_placement(args, 2);
2058 static int c_arcchange(map_t*args)
2060 c_placement(args, 2);
2063 static int c_jump(map_t*args)
2065 c_placement(args, 3);
2068 static int c_startclip(map_t*args)
2070 c_placement(args, 4);
2073 static int c_show(map_t*args)
2075 c_placement(args, 5);
2078 static int c_del(map_t*args)
2080 char*instance = lu(args, "name");
2081 s_delinstance(instance);
2084 static int c_end(map_t*args)
2089 static int c_sprite(map_t*args)
2091 char* name = lu(args, "name");
2095 static int c_frame(map_t*args)
2097 char*framestr = lu(args, "n");
2098 char*cutstr = lu(args, "cut");
2101 if(strcmp(cutstr, "no"))
2103 if(isRelative(framestr)) {
2104 frame = s_getframe();
2105 if(getSign(framestr)<0)
2106 syntaxerror("relative frame expressions must be positive");
2107 frame += parseInt(getOffset(framestr));
2110 frame = parseInt(framestr);
2111 if(s_getframe() >= frame
2112 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2113 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2115 s_frame(frame, cut);
2118 static int c_primitive(map_t*args)
2120 char*name = lu(args, "name");
2121 char*command = lu(args, "commandname");
2122 int width=0, height=0, r=0;
2123 int linewidth = parseTwip(lu(args, "line"));
2124 char*colorstr = lu(args, "color");
2125 RGBA color = parseColor(colorstr);
2126 char*fillstr = lu(args, "fill");
2133 if(!strcmp(command, "circle"))
2135 else if(!strcmp(command, "filled"))
2139 width = parseTwip(lu(args, "width"));
2140 height = parseTwip(lu(args, "height"));
2141 } else if (type==1) {
2142 r = parseTwip(lu(args, "r"));
2143 } else if (type==2) {
2144 outline = lu(args, "outline");
2147 if(!strcmp(fillstr, "fill"))
2149 if(!strcmp(fillstr, "none"))
2151 if(width<0 || height<0 || linewidth<0 || r<0)
2152 syntaxerror("values width, height, line, r must be positive");
2154 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2155 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2156 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2160 static int c_textshape(map_t*args)
2162 char*name = lu(args, "name");
2163 char*text = lu(args, "text");
2164 char*font = lu(args, "font");
2166 s_textshape(name, font, text);
2170 static int c_swf(map_t*args)
2172 char*name = lu(args, "name");
2173 char*filename = lu(args, "filename");
2174 char*command = lu(args, "commandname");
2175 if(!strcmp(command, "shape"))
2176 warning("Please use .swf instead of .shape");
2177 s_includeswf(name, filename);
2181 static int c_font(map_t*args)
2183 char*name = lu(args, "name");
2184 char*filename = lu(args, "filename");
2185 s_font(name, filename);
2189 static int c_sound(map_t*args)
2191 char*name = lu(args, "name");
2192 char*filename = lu(args, "filename");
2193 s_sound(name, filename);
2197 static int c_text(map_t*args)
2199 char*name = lu(args, "name");
2200 char*text = lu(args, "text");
2201 char*font = lu(args, "font");
2202 float size = parsePercent(lu(args, "size"));
2203 RGBA color = parseColor(lu(args, "color"));
2204 s_text(name, font, text, (int)(size*100), color);
2208 static int c_soundtrack(map_t*args)
2213 static int c_image(map_t*args)
2215 char*command = lu(args, "commandname");
2216 char*name = lu(args, "name");
2217 char*filename = lu(args, "filename");
2218 if(!strcmp(command,"jpeg")) {
2219 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2220 s_image(name, "jpeg", filename, quality);
2222 s_image(name, "png", filename, 0);
2227 static int c_outline(map_t*args)
2229 char*name = lu(args, "name");
2230 char*format = lu(args, "format");
2234 syntaxerror("colon (:) expected");
2236 s_outline(name, format, text);
2240 int fakechar(map_t*args)
2242 char*name = lu(args, "name");
2243 s_box(name, 0, 0, black, 20, 0);
2247 static int c_egon(map_t*args) {return fakechar(args);}
2248 static int c_button(map_t*args) {
2249 char*name = lu(args, "name");
2253 static int current_button_flags = 0;
2254 static int c_on_press(map_t*args)
2256 char*position = lu(args, "position");
2257 if(!strcmp(position, "inside")) {
2258 current_button_flags |= BC_OVERUP_OVERDOWN;
2259 } else if(!strcmp(position, "outside")) {
2260 //current_button_flags |= BC_IDLE_OUTDOWN;
2261 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2262 } else if(!strcmp(position, "anywhere")) {
2263 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2267 if(type == RAWDATA) {
2269 s_buttonaction(current_button_flags, action);
2270 current_button_flags = 0;
2276 static int c_on_release(map_t*args)
2278 char*position = lu(args, "position");
2279 if(!strcmp(position, "inside")) {
2280 current_button_flags |= BC_OVERDOWN_OVERUP;
2281 } else if(!strcmp(position, "outside")) {
2282 current_button_flags |= BC_OUTDOWN_IDLE;
2283 } else if(!strcmp(position, "anywhere")) {
2284 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2288 if(type == RAWDATA) {
2290 s_buttonaction(current_button_flags, action);
2291 current_button_flags = 0;
2297 static int c_on_move_in(map_t*args)
2299 char*position = lu(args, "state");
2300 if(!strcmp(position, "pressed")) {
2301 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2302 } else if(!strcmp(position, "not_pressed")) {
2303 current_button_flags |= BC_IDLE_OVERUP;
2304 } else if(!strcmp(position, "any")) {
2305 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2309 if(type == RAWDATA) {
2311 s_buttonaction(current_button_flags, action);
2312 current_button_flags = 0;
2318 static int c_on_move_out(map_t*args)
2320 char*position = lu(args, "state");
2321 if(!strcmp(position, "pressed")) {
2322 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2323 } else if(!strcmp(position, "not_pressed")) {
2324 current_button_flags |= BC_OVERUP_IDLE;
2325 } else if(!strcmp(position, "any")) {
2326 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2330 if(type == RAWDATA) {
2332 s_buttonaction(current_button_flags, action);
2333 current_button_flags = 0;
2339 static int c_on_key(map_t*args)
2341 char*key = lu(args, "key");
2342 if(strlen(key)==1) {
2345 current_button_flags |= 0x4000 + (key[0]*0x200);
2347 syntaxerror("invalid character: %c"+key[0]);
2352 <ctrl-x> = 0x200*(x-'a')
2356 syntaxerror("invalid key: %s",key);
2360 if(type == RAWDATA) {
2362 s_buttonaction(current_button_flags, action);
2363 current_button_flags = 0;
2370 static int c_edittext(map_t*args) {return fakechar(args);}
2372 static int c_morphshape(map_t*args) {return fakechar(args);}
2373 static int c_movie(map_t*args) {return fakechar(args);}
2375 static int c_texture(map_t*args) {return 0;}
2377 static int c_action(map_t*args)
2380 if(type != RAWDATA) {
2381 syntaxerror("colon (:) expected");
2391 command_func_t* func;
2394 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2395 {"frame", c_frame, "n=<plus>1 @cut=no"},
2396 // "import" type stuff
2397 {"swf", c_swf, "name filename"},
2398 {"shape", c_swf, "name filename"},
2399 {"jpeg", c_image, "name filename quality=80%"},
2400 {"png", c_image, "name filename"},
2401 {"movie", c_movie, "name filename"},
2402 {"sound", c_sound, "name filename"},
2403 {"font", c_font, "name filename"},
2404 {"soundtrack", c_soundtrack, "filename"},
2406 // generators of primitives
2408 {"point", c_point, "name x=0 y=0"},
2409 {"gradient", c_gradient, "name @radial=0"},
2410 {"outline", c_outline, "name format=simple"},
2411 {"textshape", c_textshape, "name text font"},
2413 // character generators
2414 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2415 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2416 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2418 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2419 {"text", c_text, "name text font size=100% color=white"},
2420 {"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"},
2421 {"morphshape", c_morphshape, "name start end"},
2422 {"button", c_button, "name"},
2423 {"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="},
2424 {"on_press", c_on_press, "position=inside"},
2425 {"on_release", c_on_release, "position=anywhere"},
2426 {"on_move_in", c_on_move_out, "state=not_pressed"},
2427 {"on_move_out", c_on_move_out, "state=not_pressed"},
2428 {"on_key", c_on_key, "key=any"},
2431 {"play", c_play, "name loop=0 @nomultiple=0"},
2432 {"stop", c_stop, "name"},
2433 {"nextframe", c_nextframe, "name"},
2434 {"previousframe", c_previousframe, "name"},
2436 // object placement tags
2437 {"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="},
2438 {"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="},
2439 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2440 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2441 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2442 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2443 {"del", c_del, "name"},
2444 // virtual object placement
2445 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2447 // commands which start a block
2448 //startclip (see above)
2449 {"sprite", c_sprite, "name"},
2450 {"action", c_action, ""},
2456 static map_t parseArguments(char*command, char*pattern)
2472 string_set(&t1, "commandname");
2473 string_set(&t2, command);
2474 map_put(&result, t1, t2);
2476 if(!pattern || !*pattern)
2483 if(!strncmp("<i> ", x, 3)) {
2485 if(type == COMMAND || type == RAWDATA) {
2487 syntaxerror("character name expected");
2489 name[pos].str = "instance";
2491 value[pos].str = text;
2492 value[pos].len = strlen(text);
2496 if(type == ASSIGNMENT)
2499 name[pos].str = "character";
2501 value[pos].str = text;
2502 value[pos].len = strlen(text);
2510 isboolean[pos] = (x[0] =='@');
2523 name[pos].len = d-x;
2528 name[pos].len = e-x;
2529 value[pos].str = e+1;
2530 value[pos].len = d-e-1;
2538 /* for(t=0;t<len;t++) {
2539 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2540 isboolean[t]?"(boolean)":"");
2545 if(type == RAWDATA || type == COMMAND) {
2550 // first, search for boolean arguments
2551 for(pos=0;pos<len;pos++)
2553 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2555 if(type == ASSIGNMENT)
2557 value[pos].str = text;
2558 value[pos].len = strlen(text);
2559 /*printf("setting boolean parameter %s (to %s)\n",
2560 strdup_n(name[pos], namelen[pos]),
2561 strdup_n(value[pos], valuelen[pos]));*/
2566 // second, search for normal arguments
2568 for(pos=0;pos<len;pos++)
2570 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2571 (type != ASSIGNMENT && !set[pos])) {
2573 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2575 if(type == ASSIGNMENT)
2578 value[pos].str = text;
2579 value[pos].len = strlen(text);
2581 printf("setting parameter %s (to %s)\n",
2582 strdup_n(name[pos].str, name[pos].len),
2583 strdup_n(value[pos].str, value[pos].len));
2589 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2593 for(t=0;t<len;t++) {
2594 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2597 for(t=0;t<len;t++) {
2598 if(value[t].str && value[t].str[0] == '*') {
2599 //relative default- take value from some other parameter
2601 for(s=0;s<len;s++) {
2602 if(value[s].len == value[t].len-1 &&
2603 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2604 value[t].str = value[s].str;
2607 if(value[t].str == 0) {
2609 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2613 /* ok, now construct the dictionary from the parameters */
2617 map_put(&result, name[t], value[t]);
2621 static void parseArgumentsForCommand(char*command)
2626 msg("<verbose> parse Command: %s (line %d)", command, line);
2628 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2629 if(!strcmp(arguments[t].command, command)) {
2631 /* ugly hack- will be removed soon (once documentation and .sc generating
2632 utilities have been changed) */
2633 if(!strcmp(command, "swf") && !stackpos) {
2634 warning("Please use .flash instead of .swf- this will be mandatory soon");
2639 args = parseArguments(command, arguments[t].arguments);
2645 syntaxerror("command %s not known", command);
2647 // catch missing .flash directives at the beginning of a file
2648 if(strcmp(command, "flash") && !stackpos)
2650 syntaxerror("No movie defined- use .flash first");
2654 printf(".%s\n", command);fflush(stdout);
2655 map_dump(&args, stdout, "\t");fflush(stdout);
2658 (*arguments[nr].func)(&args);
2660 /*if(!strcmp(command, "button") ||
2661 !strcmp(command, "action")) {
2664 if(type == COMMAND) {
2665 if(!strcmp(text, "end"))
2679 int main (int argc,char ** argv)
2682 processargs(argc, argv);
2683 initLog(0,-1,0,0,-1,verbose);
2686 args_callback_usage(argv[0]);
2690 file = generateTokens(filename);
2692 printf("parser returned error.\n");
2698 while(!noMoreTokens()) {
2701 syntaxerror("command expected");
2702 parseArgumentsForCommand(text);