2 Compiles swf code (.sc) files into .swf files.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/drawer.h"
32 #include "../lib/log.h"
33 #include "../lib/args.h"
40 static char * filename = 0;
41 static char * outputname = "output.swf";
42 static int verbose = 2;
43 static int override_outputname = 0;
45 static struct options_t options[] = {
53 int args_callback_option(char*name,char*val)
55 if(!strcmp(name, "V")) {
56 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
59 else if(!strcmp(name, "o")) {
61 override_outputname = 1;
64 else if(!strcmp(name, "v")) {
69 printf("Unknown option: -%s\n", name);
74 int args_callback_longoption(char*name,char*val)
76 return args_long2shortoption(options, name, val);
78 void args_callback_usage(char *name)
81 printf("Usage: %s [-o file.swf] file.sc\n", name);
83 printf("-h , --help Print short help message and exit\n");
84 printf("-V , --version Print version info and exit\n");
85 printf("-v , --verbose Increase verbosity. \n");
86 printf("-o , --output <filename> Set output file to <filename>.\n");
89 int args_callback_command(char*name,char*val)
92 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
99 static struct token_t* file;
108 static void syntaxerror(char*format, ...)
112 va_start(arglist, format);
113 vsprintf(buf, format, arglist);
115 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
119 static void warning(char*format, ...)
123 va_start(arglist, format);
124 vsprintf(buf, format, arglist);
126 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
129 static void readToken()
131 type = file[pos].type;
133 syntaxerror("unexpected end of file");
135 text = file[pos].text;
136 textlen = strlen(text);
137 line = file[pos].line;
138 column = file[pos].column;
140 //printf("---> %d(%s) %s\n", type, type_names[type], text);
143 static void pushBack()
146 if(!pos) syntaxerror("internal error 3");
151 textlen = strlen(text);
154 column = file[p].column;
157 static int noMoreTokens()
159 if(file[pos].type == END)
164 // ------------------------------ swf routines ----------------------------
168 int type; //0=swf, 1=sprite, 2=clip, 3=button
174 /* for sprites (1): */
180 dictionary_t oldinstances;
185 static int stackpos = 0;
187 static dictionary_t characters;
188 static dictionary_t images;
189 static dictionary_t outlines;
190 static dictionary_t gradients;
191 static char idmap[65536];
192 static TAG*tag = 0; //current tag
194 static int id; //current character id
195 static int currentframe; //current frame in current level
196 static SRECT currentrect; //current bounding box in current level
197 static U16 currentdepth;
198 static dictionary_t instances;
199 static dictionary_t fonts;
200 static dictionary_t sounds;
202 typedef struct _parameters {
204 float scalex, scaley;
212 typedef struct _character {
218 typedef struct _instance {
219 character_t*character;
221 parameters_t parameters;
222 TAG* lastTag; //last tag which set the object
223 U16 lastFrame; //frame lastTag is in
226 typedef struct _outline {
231 typedef struct _gradient {
236 static void character_init(character_t*c)
238 memset(c, 0, sizeof(character_t));
240 static character_t* character_new()
243 c = (character_t*)malloc(sizeof(character_t));
247 static void instance_init(instance_t*i)
249 memset(i, 0, sizeof(instance_t));
251 static instance_t* instance_new()
254 c = (instance_t*)malloc(sizeof(instance_t));
259 static void incrementid()
263 syntaxerror("Out of character ids.");
268 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
270 character_t* c = character_new();
272 c->definingTag = ctag;
275 if(dictionary_lookup(&characters, name))
276 syntaxerror("character %s defined twice", name);
277 dictionary_put2(&characters, name, c);
279 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
281 swf_SetString(tag, name);
282 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
285 swf_SetString(tag, name);
287 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
289 character_t* c = character_new();
290 c->definingTag = ctag;
294 if(dictionary_lookup(&images, name))
295 syntaxerror("image %s defined twice", name);
296 dictionary_put2(&images, name, c);
298 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
300 instance_t* i = instance_new();
303 //swf_GetMatrix(0, &i->matrix);
304 if(dictionary_lookup(&instances, name))
305 syntaxerror("object %s defined twice", name);
306 dictionary_put2(&instances, name, i);
310 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
313 p->scalex = scalex; p->scaley = scaley;
314 p->pin = pin; p->pivot = pivot;
315 p->rotate = rotate; p->cxform = cxform;
319 static void parameters_clear(parameters_t*p)
322 p->scalex = 1.0; p->scaley = 1.0;
323 p->pin.x = 1; p->pin.y = 0;
324 p->pivot.x = 0; p->pivot.y = 0;
327 swf_GetCXForm(0, &p->cxform, 1);
330 static void makeMatrix(MATRIX*m, parameters_t*p)
339 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
340 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
341 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
342 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
344 m->sx = (int)(sx*65536+0.5);
345 m->r1 = (int)(r1*65536+0.5);
346 m->r0 = (int)(r0*65536+0.5);
347 m->sy = (int)(sy*65536+0.5);
351 h = swf_TurnPoint(p->pin, m);
356 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
361 r = swf_TurnRect(rect, &m);
362 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
363 currentrect.xmax == 0 && currentrect.ymax == 0)
366 swf_ExpandRect2(¤trect, &r);
370 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
372 SWF*swf = (SWF*)malloc(sizeof(SWF));
375 syntaxerror(".swf blocks can't be nested");
377 memset(swf, 0, sizeof(swf));
378 swf->fileVersion = version;
380 swf->frameRate = fps;
381 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
382 swf->compressed = compress;
383 swf_SetRGB(tag,&background);
385 if(stackpos==sizeof(stack)/sizeof(stack[0]))
386 syntaxerror("too many levels of recursion");
388 dictionary_init(&characters);
389 dictionary_init(&images);
390 dictionary_init(&outlines);
391 dictionary_init(&gradients);
392 dictionary_init(&instances);
393 dictionary_init(&fonts);
394 dictionary_init(&sounds);
396 memset(&stack[stackpos], 0, sizeof(stack[0]));
397 stack[stackpos].type = 0;
398 stack[stackpos].filename = strdup(name);
399 stack[stackpos].swf = swf;
400 stack[stackpos].oldframe = -1;
405 memset(¤trect, 0, sizeof(currentrect));
408 memset(idmap, 0, sizeof(idmap));
412 void s_sprite(char*name)
414 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
415 swf_SetU16(tag, id); //id
416 swf_SetU16(tag, 0); //frames
418 memset(&stack[stackpos], 0, sizeof(stack[0]));
419 stack[stackpos].type = 1;
420 stack[stackpos].oldframe = currentframe;
421 stack[stackpos].olddepth = currentdepth;
422 stack[stackpos].oldrect = currentrect;
423 stack[stackpos].oldinstances = instances;
424 stack[stackpos].tag = tag;
425 stack[stackpos].id = id;
426 stack[stackpos].name = strdup(name);
428 /* FIXME: those four fields should be bundled together */
429 dictionary_init(&instances);
432 memset(¤trect, 0, sizeof(currentrect));
438 typedef struct _buttonrecord
446 typedef struct _button
450 buttonrecord_t records[4];
453 static button_t mybutton;
455 void s_button(char*name)
457 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
458 swf_SetU16(tag, id); //id
459 swf_ButtonSetFlags(tag, 0); //menu=no
461 memset(&mybutton, 0, sizeof(mybutton));
463 memset(&stack[stackpos], 0, sizeof(stack[0]));
464 stack[stackpos].type = 3;
465 stack[stackpos].tag = tag;
466 stack[stackpos].id = id;
467 stack[stackpos].name = strdup(name);
468 stack[stackpos].oldrect = currentrect;
469 memset(¤trect, 0, sizeof(currentrect));
474 void s_buttonput(char*character, char*as, parameters_t p)
476 character_t* c = dictionary_lookup(&characters, character);
481 syntaxerror("character %s not known (in .shape %s)", character, character);
483 if(mybutton.endofshapes) {
484 syntaxerror("a .do may not precede a .show", character, character);
487 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()
541 setbuttonrecords(stack[stackpos-1].tag);
544 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
548 tag = stack[stackpos].tag;
549 currentrect = stack[stackpos].oldrect;
551 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
552 free(stack[stackpos].name);
555 TAG* removeFromTo(TAG*from, TAG*to)
557 TAG*save = from->prev;
559 TAG*next = from->next;
567 static void s_endSprite()
569 SRECT r = currentrect;
571 if(stack[stackpos].cut)
572 tag = removeFromTo(stack[stackpos].cut, tag);
576 /* TODO: before clearing, prepend "<spritename>." to names and
577 copy into old instances dict */
578 dictionary_clear(&instances);
580 currentframe = stack[stackpos].oldframe;
581 currentrect = stack[stackpos].oldrect;
582 currentdepth = stack[stackpos].olddepth;
583 instances = stack[stackpos].oldinstances;
585 tag = swf_InsertTag(tag, ST_END);
587 tag = stack[stackpos].tag;
590 syntaxerror("internal error(7)");
592 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
593 free(stack[stackpos].name);
596 static void s_endSWF()
602 if(stack[stackpos].cut)
603 tag = removeFromTo(stack[stackpos].cut, tag);
607 swf = stack[stackpos].swf;
608 filename = stack[stackpos].filename;
610 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
612 tag = swf_InsertTag(tag, ST_END);
614 swf_OptimizeTagOrder(swf);
616 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
617 swf->movieSize = currentrect; /* "autocrop" */
620 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
621 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
622 swf->movieSize.ymax += 20;
625 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
627 syntaxerror("couldn't create output file %s", filename);
630 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
632 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
636 dictionary_clear(&instances);
637 dictionary_clear(&characters);
638 dictionary_clear(&images);
639 dictionary_clear(&outlines);
640 dictionary_clear(&gradients);
641 dictionary_clear(&fonts);
642 dictionary_clear(&sounds);
652 if(stack[stackpos-1].type == 0)
653 syntaxerror("End of file encountered in .flash block");
654 if(stack[stackpos-1].type == 1)
655 syntaxerror("End of file encountered in .sprite block");
656 if(stack[stackpos-1].type == 2)
657 syntaxerror("End of file encountered in .clip block");
666 void s_frame(int nr, int cut)
671 for(t=currentframe;t<nr;t++) {
672 tag = swf_InsertTag(tag, ST_SHOWFRAME);
677 syntaxerror("Can't cut, frame empty");
679 stack[stackpos].cut = tag;
685 int parseColor2(char*str, RGBA*color);
687 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
692 if(texture[0] == '#') {
693 parseColor2(texture, &color);
694 return swf_ShapeAddSolidFillStyle(s, &color);
695 } else if((image = dictionary_lookup(&images, texture))) {
697 swf_GetMatrix(0, &m);
698 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
699 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
702 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
703 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
704 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
706 swf_GetMatrix(0, &m);
707 m.sx = (r->xmax - r->xmin)*2;
708 m.sy = (r->ymax - r->ymin)*2;
709 m.tx = r->xmin + (r->xmax - r->xmin)/2;
710 m.ty = r->ymin + (r->ymax - r->ymin)/2;
711 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
712 } else if (parseColor2(texture, &color)) {
713 return swf_ShapeAddSolidFillStyle(s, &color);
715 syntaxerror("not a color/fillstyle: %s", texture);
720 RGBA black={r:0,g:0,b:0,a:0};
721 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
730 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
732 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
735 fs1 = addFillStyle(s, &r2, texture);
738 r.xmin = r2.xmin-linewidth-linewidth/2;
739 r.ymin = r2.ymin-linewidth-linewidth/2;
740 r.xmax = r2.xmax+linewidth+linewidth/2;
741 r.ymax = r2.ymax+linewidth+linewidth/2;
743 swf_SetShapeHeader(tag,s);
744 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
745 swf_ShapeSetLine(tag,s,width,0);
746 swf_ShapeSetLine(tag,s,0,height);
747 swf_ShapeSetLine(tag,s,-width,0);
748 swf_ShapeSetLine(tag,s,0,-height);
749 swf_ShapeSetEnd(tag);
752 s_addcharacter(name, id, tag, r);
756 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
762 outline = dictionary_lookup(&outlines, outlinename);
764 syntaxerror("outline %s not defined", outlinename);
768 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
770 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
772 fs1 = addFillStyle(s, &r2, texture);
774 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
776 rect.xmin = r2.xmin-linewidth-linewidth/2;
777 rect.ymin = r2.ymin-linewidth-linewidth/2;
778 rect.xmax = r2.xmax+linewidth+linewidth/2;
779 rect.ymax = r2.ymax+linewidth+linewidth/2;
781 swf_SetRect(tag,&rect);
782 swf_SetShapeStyles(tag, s);
783 swf_SetShapeBits(tag, outline->shape); //does not count bits!
784 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
787 s_addcharacter(name, id, tag, rect);
791 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
796 r2.xmin = r2.ymin = 0;
800 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
802 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
804 fs1 = addFillStyle(s, &r2, texture);
806 rect.xmin = r2.xmin-linewidth-linewidth/2;
807 rect.ymin = r2.ymin-linewidth-linewidth/2;
808 rect.xmax = r2.xmax+linewidth+linewidth/2;
809 rect.ymax = r2.ymax+linewidth+linewidth/2;
811 swf_SetRect(tag,&rect);
812 swf_SetShapeHeader(tag,s);
813 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
814 swf_ShapeSetCircle(tag, s, r,r,r,r);
815 swf_ShapeSetEnd(tag);
818 s_addcharacter(name, id, tag, rect);
822 void s_textshape(char*name, char*fontname, char*_text)
825 U8*text = (U8*)_text;
829 font = dictionary_lookup(&fonts, fontname);
831 syntaxerror("font \"%s\" not known!", fontname);
833 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
834 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
835 s_box(name, 0, 0, black, 20, 0);
838 g = font->ascii2glyph[text[0]];
840 outline = malloc(sizeof(outline_t));
841 memset(outline, 0, sizeof(outline_t));
842 outline->shape = font->glyph[g].shape;
843 outline->bbox = font->layout->bounds[g];
847 swf_Shape11DrawerInit(&draw, 0);
848 swf_DrawText(&draw, font, _text);
850 outline->shape = swf_ShapeDrawerToShape(&draw);
851 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
855 if(dictionary_lookup(&outlines, name))
856 syntaxerror("outline %s defined twice", name);
857 dictionary_put2(&outlines, name, outline);
860 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
865 font = dictionary_lookup(&fonts, fontname);
867 syntaxerror("font \"%s\" not known!", fontname);
869 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
871 if(!font->numchars) {
872 s_box(name, 0, 0, black, 20, 0);
875 r = swf_SetDefineText(tag, font, &color, text, size);
877 s_addcharacter(name, id, tag, r);
881 /* type: either "jpeg" or "png"
883 void s_image(char*name, char*type, char*filename, int quality)
885 /* an image is actually two folded: 1st bitmap, 2nd character.
886 Both of them can be used separately */
888 /* step 1: the bitmap */
893 warning("image type \"png\" not supported yet!");
894 s_box(name, 0, 0, black, 20, 0);
899 warning("no jpeg support compiled in");
900 s_box(name, 0, 0, black, 20, 0);
903 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
904 swf_SetU16(tag, imageID);
906 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
907 syntaxerror("Image \"%s\" not found, or contains errors", filename);
910 swf_GetJPEGSize(filename, &width, &height);
917 s_addimage(name, id, tag, r);
922 /* step 2: the character */
923 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
925 swf_ShapeSetBitmapRect(tag, imageID, width, height);
927 s_addcharacter(name, id, tag, r);
931 void dumpSWF(SWF*swf)
933 TAG* tag = swf->firstTag;
934 printf("vvvvvvvvvvvvvvvvvvvvv\n");
936 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
939 printf("^^^^^^^^^^^^^^^^^^^^^\n");
942 void s_font(char*name, char*filename)
945 font = swf_LoadFont(filename);
948 warning("Couldn't open font file \"%s\"", filename);
949 font = (SWFFONT*)malloc(sizeof(SWFFONT));
950 memset(font, 0, sizeof(SWFFONT));
951 dictionary_put2(&fonts, name, font);
957 /* fix the layout. Only needed for old fonts */
959 for(t=0;t<font->numchars;t++) {
960 font->glyph[t].advance = 0;
963 swf_FontCreateLayout(font);
967 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
968 swf_FontSetDefine2(tag, font);
971 if(dictionary_lookup(&fonts, name))
972 syntaxerror("font %s defined twice", name);
973 dictionary_put2(&fonts, name, font);
978 typedef struct _sound_t
984 void s_sound(char*name, char*filename)
986 struct WAV wav, wav2;
991 if(!readWAV(filename, &wav)) {
992 warning("Couldn't read wav file \"%s\"", filename);
996 convertWAV2mono(&wav, &wav2, 44100);
997 samples = (U16*)wav2.data;
998 numsamples = wav2.size/2;
1002 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1003 swf_SetU16(tag, id); //id
1004 swf_SetSoundDefine(tag, samples, numsamples);
1006 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1010 if(dictionary_lookup(&sounds, name))
1011 syntaxerror("sound %s defined twice", name);
1012 dictionary_put2(&sounds, name, sound);
1020 static char* gradient_getToken(const char**p)
1024 while(**p && strchr(" \t\n\r", **p)) {
1028 while(**p && !strchr(" \t\n\r", **p)) {
1031 result = malloc((*p)-start+1);
1032 memcpy(result,start,(*p)-start+1);
1033 result[(*p)-start] = 0;
1037 float parsePercent(char*str);
1038 RGBA parseColor(char*str);
1040 GRADIENT parseGradient(const char*str)
1043 const char* p = str;
1044 memset(&gradient, 0, sizeof(GRADIENT));
1046 char*posstr,*colorstr;
1049 posstr = gradient_getToken(&p);
1052 pos = parsePercent(posstr);
1053 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1054 colorstr = gradient_getToken(&p);
1055 color = parseColor(colorstr);
1056 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1057 warning("gradient record too big- max size is 8, rest ignored");
1060 gradient.ratios[gradient.num] = (int)(pos*255.0);
1061 gradient.rgba[gradient.num] = color;
1069 void s_gradient(char*name, const char*text, int radial)
1071 gradient_t* gradient;
1072 gradient = malloc(sizeof(gradient_t));
1073 memset(gradient, 0, sizeof(gradient_t));
1074 gradient->gradient = parseGradient(text);
1075 gradient->radial = radial;
1077 if(dictionary_lookup(&gradients, name))
1078 syntaxerror("gradient %s defined twice", name);
1079 dictionary_put2(&gradients, name, gradient);
1082 void s_action(const char*text)
1085 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1087 syntaxerror("Couldn't compile ActionScript");
1090 tag = swf_InsertTag(tag, ST_DOACTION);
1092 swf_ActionSet(tag, a);
1097 int s_swf3action(char*name, char*action)
1100 instance_t* object = dictionary_lookup(&instances, name);
1104 a = action_SetTarget(0, name);
1105 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1106 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1107 else if(!strcmp(action, "stop")) a = action_Stop(a);
1108 else if(!strcmp(action, "play")) a = action_Play(a);
1109 a = action_SetTarget(a, "");
1112 tag = swf_InsertTag(tag, ST_DOACTION);
1113 swf_ActionSet(tag, a);
1118 void s_outline(char*name, char*format, char*source)
1127 swf_Shape11DrawerInit(&draw, 0);
1128 draw_string(&draw, source);
1130 shape = swf_ShapeDrawerToShape(&draw);
1131 //shape2 = swf_ShapeToShape2(shape);
1132 //bounds = swf_GetShapeBoundingBox(shape2);
1133 //swf_Shape2Free(shape2);
1134 bounds = swf_ShapeDrawerGetBBox(&draw);
1135 draw.dealloc(&draw);
1137 outline = (outline_t*)malloc(sizeof(outline_t));
1138 memset(outline, 0, sizeof(outline_t));
1139 outline->shape = shape;
1140 outline->bbox = bounds;
1142 if(dictionary_lookup(&outlines, name))
1143 syntaxerror("outline %s defined twice", name);
1144 dictionary_put2(&outlines, name, outline);
1147 int s_playsound(char*name, int loops, int nomultiple, int stop)
1149 sound_t* sound = dictionary_lookup(&sounds, name);
1154 tag = swf_InsertTag(tag, ST_STARTSOUND);
1155 swf_SetU16(tag, sound->id); //id
1156 memset(&info, 0, sizeof(info));
1159 info.nomultiple = nomultiple;
1160 swf_SetSoundInfo(tag, &info);
1164 void s_includeswf(char*name, char*filename)
1172 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1173 f = open(filename,O_RDONLY);
1175 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1176 s_box(name, 0, 0, black, 20, 0);
1179 if (swf_ReadSWF(f,&swf)<0) {
1180 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1181 s_box(name, 0, 0, black, 20, 0);
1186 /* FIXME: The following sets the bounding Box for the character.
1187 It is wrong for two reasons:
1188 a) It may be too small (in case objects in the movie clip at the borders)
1189 b) it may be too big (because the poor movie never got autocropped)
1193 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1194 swf_SetU16(tag, id);
1197 swf_Relocate(&swf, idmap);
1199 ftag = swf.firstTag;
1203 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1204 if(cutout[t] == ftag->id) {
1208 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1210 if(ftag->id == ST_END)
1214 /* We simply dump all tags right after the sprite
1215 header, relying on the fact that swf_OptimizeTagOrder() will
1216 sort things out for us later.
1217 We also rely on the fact that the imported SWF is well-formed.
1219 tag = swf_InsertTag(tag, ftag->id);
1220 swf_SetBlock(tag, ftag->data, ftag->len);
1224 syntaxerror("Included file %s contains errors", filename);
1225 tag = swf_InsertTag(tag, ST_END);
1229 s_addcharacter(name, id, tag, r);
1232 SRECT s_getCharBBox(char*name)
1234 character_t* c = dictionary_lookup(&characters, name);
1235 if(!c) syntaxerror("character '%s' unknown(2)", name);
1238 SRECT s_getInstanceBBox(char*name)
1240 instance_t * i = dictionary_lookup(&instances, name);
1242 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1244 if(!c) syntaxerror("internal error(5)");
1247 parameters_t s_getParameters(char*name)
1249 instance_t * i = dictionary_lookup(&instances, name);
1250 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1251 return i->parameters;
1253 void s_startclip(char*instance, char*character, parameters_t p)
1255 character_t* c = dictionary_lookup(&characters, character);
1259 syntaxerror("character %s not known", character);
1261 i = s_addinstance(instance, c, currentdepth);
1263 m = s_instancepos(i->character->size, &p);
1265 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1266 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1267 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1269 i->lastFrame= currentframe;
1271 stack[stackpos].tag = tag;
1272 stack[stackpos].type = 2;
1281 swf_SetTagPos(stack[stackpos].tag, 0);
1282 swf_GetPlaceObject(stack[stackpos].tag, &p);
1283 p.clipdepth = currentdepth;
1284 swf_ClearTag(stack[stackpos].tag);
1285 swf_SetPlaceObject(stack[stackpos].tag, &p);
1289 void s_put(char*instance, char*character, parameters_t p)
1291 character_t* c = dictionary_lookup(&characters, character);
1295 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1298 i = s_addinstance(instance, c, currentdepth);
1300 m = s_instancepos(i->character->size, &p);
1302 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1303 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1305 i->lastFrame = currentframe;
1309 void s_jump(char*instance, parameters_t p)
1311 instance_t* i = dictionary_lookup(&instances, instance);
1314 syntaxerror("instance %s not known", instance);
1318 m = s_instancepos(i->character->size, &p);
1320 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1321 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1323 i->lastFrame = currentframe;
1326 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1330 if(num==0 || num==1)
1332 ratio = (float)pos/(float)num;
1334 p.x = (p2->x-p1->x)*ratio + p1->x;
1335 p.y = (p2->y-p1->y)*ratio + p1->y;
1336 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1337 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1338 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1339 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1341 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1342 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1343 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1344 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1346 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1347 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1348 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1349 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1351 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1352 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1353 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1354 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1358 void s_change(char*instance, parameters_t p2)
1360 instance_t* i = dictionary_lookup(&instances, instance);
1364 int frame, allframes;
1366 syntaxerror("instance %s not known", instance);
1370 allframes = currentframe - i->lastFrame - 1;
1372 warning(".change ignored. can only .put/.change an object once per frame.");
1376 m = s_instancepos(i->character->size, &p2);
1377 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1378 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1381 /* o.k., we got the start and end point set. Now iterate though all the
1382 tags in between, inserting object changes after each new frame */
1385 if(!t) syntaxerror("internal error(6)");
1387 while(frame < allframes) {
1388 if(t->id == ST_SHOWFRAME) {
1393 p = s_interpolate(&p1, &p2, frame, allframes);
1394 m = s_instancepos(i->character->size, &p); //needed?
1395 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1396 i->lastFrame = currentframe;
1397 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1399 if(frame == allframes)
1404 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1408 void s_delinstance(char*instance)
1410 instance_t* i = dictionary_lookup(&instances, instance);
1412 syntaxerror("instance %s not known", instance);
1414 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1415 swf_SetU16(tag, i->depth);
1416 dictionary_del(&instances, instance);
1419 void s_qchange(char*instance, parameters_t p)
1426 syntaxerror(".end unexpected");
1427 if(stack[stackpos-1].type == 0)
1429 else if(stack[stackpos-1].type == 1)
1431 else if(stack[stackpos-1].type == 2)
1433 else if(stack[stackpos-1].type == 3)
1435 else syntaxerror("internal error 1");
1438 // ------------------------------------------------------------------------
1440 typedef int command_func_t(map_t*args);
1442 SRECT parseBox(char*str)
1445 float xmin, xmax, ymin, ymax;
1446 char*x = strchr(str, 'x');
1448 if(!strcmp(str, "autocrop")) {
1449 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1453 d1 = strchr(x+1, ':');
1455 d2 = strchr(d1+1, ':');
1457 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1461 else if(d1 && !d2) {
1462 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1468 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1473 r.xmin = (SCOORD)(xmin*20);
1474 r.ymin = (SCOORD)(ymin*20);
1475 r.xmax = (SCOORD)(xmax*20);
1476 r.ymax = (SCOORD)(ymax*20);
1479 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1482 float parseFloat(char*str)
1486 int parseInt(char*str)
1491 if(str[0]=='+' || str[0]=='-')
1495 if(str[t]<'0' || str[t]>'9')
1496 syntaxerror("Not an Integer: \"%s\"", str);
1499 int parseTwip(char*str)
1503 if(str[0]=='+' || str[0]=='-') {
1508 dot = strchr(str, '.');
1512 return sign*parseInt(str)*20;
1514 int l=strlen(++dot);
1516 for(s=str;s<dot-1;s++)
1517 if(*s<'0' || *s>'9')
1518 syntaxerror("Not a coordinate: \"%s\"", str);
1520 if(*s<'0' || *s>'9')
1521 syntaxerror("Not a coordinate: \"%s\"", str);
1523 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1524 warning("precision loss: %s converted to twip", str);
1529 return sign*atoi(str)*20;
1531 return sign*atoi(str)*20+atoi(dot)*2;
1533 return sign*atoi(str)*20+atoi(dot)/5;
1538 int isPoint(char*str)
1540 if(strchr(str, '('))
1546 SPOINT parsePoint(char*str)
1550 int l = strlen(str);
1551 char*comma = strchr(str, ',');
1552 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1553 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1554 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1555 p.x = parseTwip(tmp);
1556 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1557 p.y = parseTwip(tmp);
1561 int parseColor2(char*str, RGBA*color)
1563 int l = strlen(str);
1567 struct {unsigned char r,g,b;char*name;} colors[] =
1568 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1569 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1570 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1571 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1572 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1573 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1574 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1575 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1576 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1577 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1578 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1579 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1583 if(str[0]=='#' && (l==7 || l==9)) {
1584 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1586 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1588 color->r = r; color->g = g; color->b = b; color->a = a;
1591 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1592 if(!strcmp(str, colors[t].name)) {
1597 color->r = r; color->g = g; color->b = b; color->a = a;
1603 RGBA parseColor(char*str)
1606 if(!parseColor2(str, &c))
1607 syntaxerror("Expression '%s' is not a color", str);
1611 typedef struct _muladd {
1616 MULADD parseMulAdd(char*str)
1619 char* str2 = (char*)malloc(strlen(str)+5);
1626 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1627 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1628 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1629 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1630 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1631 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1632 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1633 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1634 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1635 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1637 syntaxerror("'%s' is not a valid color transform expression", str);
1639 m.add = (int)(add*256);
1640 m.mul = (int)(mul*256);
1645 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1647 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1648 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1650 if(a<-32768) a=-32768;
1651 if(a>32767) a=32767;
1652 if(m<-32768) m=-32768;
1653 if(m>32767) m=32767;
1659 float parsePercent(char*str)
1661 int l = strlen(str);
1665 return atoi(str)/100.0;
1667 syntaxerror("Expression '%s' is not a percentage", str);
1670 int isPercent(char*str)
1672 return str[strlen(str)-1]=='%';
1674 int parseNewSize(char*str, int size)
1677 return parsePercent(str)*size;
1679 return (int)(atof(str)*20);
1682 int isColor(char*str)
1685 return parseColor2(str, &c);
1688 static char* lu(map_t* args, char*name)
1690 char* value = map_lookup(args, name);
1692 map_dump(args, stdout, "");
1693 syntaxerror("internal error 2: value %s should be set", name);
1698 static int c_flash(map_t*args)
1700 char* name = lu(args, "name");
1701 char* compressstr = lu(args, "compress");
1702 SRECT bbox = parseBox(lu(args, "bbox"));
1703 int version = parseInt(lu(args, "version"));
1704 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1706 RGBA color = parseColor(lu(args, "background"));
1707 if(!strcmp(name, "!default!") || override_outputname)
1710 if(!strcmp(compressstr, "default"))
1711 compress = version==6;
1712 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1714 else if(!strcmp(compressstr, "no"))
1716 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1718 s_swf(name, bbox, version, fps, compress, color);
1721 int isRelative(char*str)
1723 return !strncmp(str, "<plus>", 6) ||
1724 !strncmp(str, "<minus>", 7);
1726 char* getOffset(char*str)
1728 if(!strncmp(str, "<plus>", 6))
1730 if(!strncmp(str, "<minus>", 7))
1732 syntaxerror("internal error (347)");
1735 int getSign(char*str)
1737 if(!strncmp(str, "<plus>", 6))
1739 if(!strncmp(str, "<minus>", 7))
1741 syntaxerror("internal error (348)");
1744 static dictionary_t points;
1745 static mem_t mpoints;
1746 int points_initialized = 0;
1748 SPOINT getPoint(SRECT r, char*name)
1751 if(!strcmp(name, "center")) {
1753 p.x = (r.xmin + r.xmax)/2;
1754 p.y = (r.ymin + r.ymax)/2;
1758 if(points_initialized)
1759 l = (int)dictionary_lookup(&points, name);
1761 syntaxerror("Invalid point: \"%s\".", name);
1764 return *(SPOINT*)&mpoints.buffer[l];
1766 static int c_gradient(map_t*args)
1768 char*name = lu(args, "name");
1769 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1773 syntaxerror("colon (:) expected");
1775 s_gradient(name, text, radial);
1778 static int c_point(map_t*args)
1780 char*name = lu(args, "name");
1784 if(!points_initialized) {
1785 dictionary_init(&points);
1787 points_initialized = 1;
1789 p.x = parseTwip(lu(args, "x"));
1790 p.y = parseTwip(lu(args, "y"));
1791 pos = mem_put(&mpoints, &p, sizeof(p));
1792 string_set(&s1, name);
1794 dictionary_put(&points, s1, (void*)pos);
1797 static int c_play(map_t*args)
1799 char*name = lu(args, "name");
1800 char*loop = lu(args, "loop");
1801 char*nomultiple = lu(args, "nomultiple");
1803 if(!strcmp(nomultiple, "nomultiple"))
1806 nm = parseInt(nomultiple);
1808 if(s_playsound(name, parseInt(loop), nm, 0)) {
1810 } else if(s_swf3action(name, "play")) {
1816 static int c_stop(map_t*args)
1818 char*name = lu(args, "name");
1820 if(s_playsound(name, 0,0,1)) {
1822 } else if(s_swf3action(name, "stop")) {
1825 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1829 static int c_nextframe(map_t*args)
1831 char*name = lu(args, "name");
1833 if(s_swf3action(name, "nextframe")) {
1836 syntaxerror("I don't know anything about movie \"%s\"", name);
1840 static int c_previousframe(map_t*args)
1842 char*name = lu(args, "name");
1844 if(s_swf3action(name, "previousframe")) {
1847 syntaxerror("I don't know anything about movie \"%s\"", name);
1851 static int c_placement(map_t*args, int type)
1853 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1856 char* luminancestr = lu(args, "luminance");
1857 char* scalestr = lu(args, "scale");
1858 char* scalexstr = lu(args, "scalex");
1859 char* scaleystr = lu(args, "scaley");
1860 char* rotatestr = lu(args, "rotate");
1861 char* shearstr = lu(args, "shear");
1862 char* xstr="", *pivotstr="";
1863 char* ystr="", *anglestr="";
1864 char*above = lu(args, "above"); /*FIXME*/
1865 char*below = lu(args, "below");
1866 char* rstr = lu(args, "red");
1867 char* gstr = lu(args, "green");
1868 char* bstr = lu(args, "blue");
1869 char* astr = lu(args, "alpha");
1870 char* pinstr = lu(args, "pin");
1871 char* as = map_lookup(args, "as");
1879 if(type==9) { // (?) .rotate or .arcchange
1880 pivotstr = lu(args, "pivot");
1881 anglestr = lu(args, "angle");
1883 xstr = lu(args, "x");
1884 ystr = lu(args, "y");
1887 luminance = parseMulAdd(luminancestr);
1890 luminance.mul = 256;
1894 if(scalexstr[0]||scaleystr[0])
1895 syntaxerror("scalex/scaley and scale cannot both be set");
1896 scalexstr = scaleystr = scalestr;
1899 if(type == 0 || type == 4) {
1901 character = lu(args, "character");
1902 parameters_clear(&p);
1903 } else if (type == 5) {
1904 character = lu(args, "name");
1905 parameters_clear(&p);
1908 p = s_getParameters(instance);
1913 if(isRelative(xstr)) {
1914 if(type == 0 || type == 4)
1915 syntaxerror("relative x values not allowed for initial put or startclip");
1916 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1918 p.x = parseTwip(xstr);
1922 if(isRelative(ystr)) {
1923 if(type == 0 || type == 4)
1924 syntaxerror("relative y values not allowed for initial put or startclip");
1925 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1927 p.y = parseTwip(ystr);
1931 /* scale, scalex, scaley */
1933 oldbbox = s_getCharBBox(character);
1935 oldbbox = s_getInstanceBBox(instance);
1937 oldwidth = oldbbox.xmax - oldbbox.xmin;
1938 oldheight = oldbbox.ymax - oldbbox.ymin;
1940 if(oldwidth==0) p.scalex = 1.0;
1943 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1947 if(oldheight==0) p.scaley = 1.0;
1950 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1956 if(isRelative(rotatestr)) {
1957 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1959 p.rotate = parseFloat(rotatestr);
1965 if(isRelative(shearstr)) {
1966 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1968 p.shear = parseFloat(shearstr);
1973 if(isPoint(pivotstr))
1974 p.pivot = parsePoint(pivotstr);
1976 p.pivot = getPoint(oldbbox, pivotstr);
1980 p.pin = parsePoint(pinstr);
1982 p.pin = getPoint(oldbbox, pinstr);
1985 /* color transform */
1987 if(rstr[0] || luminancestr[0]) {
1990 r = parseMulAdd(rstr);
1992 r.add = p.cxform.r0;
1993 r.mul = p.cxform.r1;
1995 r = mergeMulAdd(r, luminance);
1996 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1998 if(gstr[0] || luminancestr[0]) {
2001 g = parseMulAdd(gstr);
2003 g.add = p.cxform.g0;
2004 g.mul = p.cxform.g1;
2006 g = mergeMulAdd(g, luminance);
2007 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2009 if(bstr[0] || luminancestr[0]) {
2012 b = parseMulAdd(bstr);
2014 b.add = p.cxform.b0;
2015 b.mul = p.cxform.b1;
2017 b = mergeMulAdd(b, luminance);
2018 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2021 MULADD a = parseMulAdd(astr);
2022 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2026 s_put(instance, character, p);
2028 s_change(instance, p);
2030 s_qchange(instance, p);
2032 s_jump(instance, p);
2034 s_startclip(instance, character, p);
2035 else if(type == 5) {
2037 s_buttonput(character, as, p);
2039 s_buttonput(character, "shape", p);
2044 static int c_put(map_t*args)
2046 c_placement(args, 0);
2049 static int c_change(map_t*args)
2051 c_placement(args, 1);
2054 static int c_qchange(map_t*args)
2056 c_placement(args, 2);
2059 static int c_arcchange(map_t*args)
2061 c_placement(args, 2);
2064 static int c_jump(map_t*args)
2066 c_placement(args, 3);
2069 static int c_startclip(map_t*args)
2071 c_placement(args, 4);
2074 static int c_show(map_t*args)
2076 c_placement(args, 5);
2079 static int c_del(map_t*args)
2081 char*instance = lu(args, "name");
2082 s_delinstance(instance);
2085 static int c_end(map_t*args)
2090 static int c_sprite(map_t*args)
2092 char* name = lu(args, "name");
2096 static int c_frame(map_t*args)
2098 char*framestr = lu(args, "n");
2099 char*cutstr = lu(args, "cut");
2102 if(strcmp(cutstr, "no"))
2104 if(isRelative(framestr)) {
2105 frame = s_getframe();
2106 if(getSign(framestr)<0)
2107 syntaxerror("relative frame expressions must be positive");
2108 frame += parseInt(getOffset(framestr));
2111 frame = parseInt(framestr);
2112 if(s_getframe() >= frame
2113 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2114 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2116 s_frame(frame, cut);
2119 static int c_primitive(map_t*args)
2121 char*name = lu(args, "name");
2122 char*command = lu(args, "commandname");
2123 int width=0, height=0, r=0;
2124 int linewidth = parseTwip(lu(args, "line"));
2125 char*colorstr = lu(args, "color");
2126 RGBA color = parseColor(colorstr);
2127 char*fillstr = lu(args, "fill");
2134 if(!strcmp(command, "circle"))
2136 else if(!strcmp(command, "filled"))
2140 width = parseTwip(lu(args, "width"));
2141 height = parseTwip(lu(args, "height"));
2142 } else if (type==1) {
2143 r = parseTwip(lu(args, "r"));
2144 } else if (type==2) {
2145 outline = lu(args, "outline");
2148 if(!strcmp(fillstr, "fill"))
2150 if(!strcmp(fillstr, "none"))
2152 if(width<0 || height<0 || linewidth<0 || r<0)
2153 syntaxerror("values width, height, line, r must be positive");
2155 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2156 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2157 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2161 static int c_textshape(map_t*args)
2163 char*name = lu(args, "name");
2164 char*text = lu(args, "text");
2165 char*font = lu(args, "font");
2167 s_textshape(name, font, text);
2171 static int c_swf(map_t*args)
2173 char*name = lu(args, "name");
2174 char*filename = lu(args, "filename");
2175 char*command = lu(args, "commandname");
2176 if(!strcmp(command, "shape"))
2177 warning("Please use .swf instead of .shape");
2178 s_includeswf(name, filename);
2182 static int c_font(map_t*args)
2184 char*name = lu(args, "name");
2185 char*filename = lu(args, "filename");
2186 s_font(name, filename);
2190 static int c_sound(map_t*args)
2192 char*name = lu(args, "name");
2193 char*filename = lu(args, "filename");
2194 s_sound(name, filename);
2198 static int c_text(map_t*args)
2200 char*name = lu(args, "name");
2201 char*text = lu(args, "text");
2202 char*font = lu(args, "font");
2203 float size = parsePercent(lu(args, "size"));
2204 RGBA color = parseColor(lu(args, "color"));
2205 s_text(name, font, text, (int)(size*100), color);
2209 static int c_soundtrack(map_t*args)
2214 static int c_image(map_t*args)
2216 char*command = lu(args, "commandname");
2217 char*name = lu(args, "name");
2218 char*filename = lu(args, "filename");
2219 if(!strcmp(command,"jpeg")) {
2220 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2221 s_image(name, "jpeg", filename, quality);
2223 s_image(name, "png", filename, 0);
2228 static int c_outline(map_t*args)
2230 char*name = lu(args, "name");
2231 char*format = lu(args, "format");
2235 syntaxerror("colon (:) expected");
2237 s_outline(name, format, text);
2241 int fakechar(map_t*args)
2243 char*name = lu(args, "name");
2244 s_box(name, 0, 0, black, 20, 0);
2248 static int c_egon(map_t*args) {return fakechar(args);}
2249 static int c_button(map_t*args) {
2250 char*name = lu(args, "name");
2254 static int current_button_flags = 0;
2255 static int c_on_press(map_t*args)
2257 char*position = lu(args, "position");
2259 if(!strcmp(position, "inside")) {
2260 current_button_flags |= BC_OVERUP_OVERDOWN;
2261 } else if(!strcmp(position, "outside")) {
2262 //current_button_flags |= BC_IDLE_OUTDOWN;
2263 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2264 } else if(!strcmp(position, "anywhere")) {
2265 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2268 if(type == RAWDATA) {
2270 s_buttonaction(current_button_flags, action);
2271 current_button_flags = 0;
2277 static int c_on_release(map_t*args)
2279 char*position = lu(args, "position");
2281 if(!strcmp(position, "inside")) {
2282 current_button_flags |= BC_OVERDOWN_OVERUP;
2283 } else if(!strcmp(position, "outside")) {
2284 current_button_flags |= BC_OUTDOWN_IDLE;
2285 } else if(!strcmp(position, "anywhere")) {
2286 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2289 if(type == RAWDATA) {
2291 s_buttonaction(current_button_flags, action);
2292 current_button_flags = 0;
2298 static int c_on_move_in(map_t*args)
2300 char*position = lu(args, "state");
2302 if(!strcmp(position, "pressed")) {
2303 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2304 } else if(!strcmp(position, "not_pressed")) {
2305 current_button_flags |= BC_IDLE_OVERUP;
2306 } else if(!strcmp(position, "any")) {
2307 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2310 if(type == RAWDATA) {
2312 s_buttonaction(current_button_flags, action);
2313 current_button_flags = 0;
2319 static int c_on_move_out(map_t*args)
2321 char*position = lu(args, "state");
2323 if(!strcmp(position, "pressed")) {
2324 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2325 } else if(!strcmp(position, "not_pressed")) {
2326 current_button_flags |= BC_OVERUP_IDLE;
2327 } else if(!strcmp(position, "any")) {
2328 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2331 if(type == RAWDATA) {
2333 s_buttonaction(current_button_flags, action);
2334 current_button_flags = 0;
2340 static int c_on_key(map_t*args)
2342 char*key = lu(args, "key");
2344 if(strlen(key)==1) {
2347 current_button_flags |= 0x4000 + (key[0]*0x200);
2349 syntaxerror("invalid character: %c"+key[0]);
2354 <ctrl-x> = 0x200*(x-'a')
2358 syntaxerror("invalid key: %s",key);
2361 if(type == RAWDATA) {
2363 s_buttonaction(current_button_flags, action);
2364 current_button_flags = 0;
2371 static int c_edittext(map_t*args) {return fakechar(args);}
2373 static int c_morphshape(map_t*args) {return fakechar(args);}
2374 static int c_movie(map_t*args) {return fakechar(args);}
2376 static int c_texture(map_t*args) {return 0;}
2378 static int c_action(map_t*args)
2381 if(type != RAWDATA) {
2382 syntaxerror("colon (:) expected");
2392 command_func_t* func;
2395 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2396 {"frame", c_frame, "n=<plus>1 @cut=no"},
2397 // "import" type stuff
2398 {"swf", c_swf, "name filename"},
2399 {"shape", c_swf, "name filename"},
2400 {"jpeg", c_image, "name filename quality=80%"},
2401 {"png", c_image, "name filename"},
2402 {"movie", c_movie, "name filename"},
2403 {"sound", c_sound, "name filename"},
2404 {"font", c_font, "name filename"},
2405 {"soundtrack", c_soundtrack, "filename"},
2407 // generators of primitives
2409 {"point", c_point, "name x=0 y=0"},
2410 {"gradient", c_gradient, "name @radial=0"},
2411 {"outline", c_outline, "name format=simple"},
2412 {"textshape", c_textshape, "name text font"},
2414 // character generators
2415 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2416 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2417 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2419 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2420 {"text", c_text, "name text font size=100% color=white"},
2421 {"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"},
2422 {"morphshape", c_morphshape, "name start end"},
2423 {"button", c_button, "name"},
2424 {"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="},
2425 {"on_press", c_on_press, "position=inside"},
2426 {"on_release", c_on_release, "position=anywhere"},
2427 {"on_move_in", c_on_move_out, "state=not_pressed"},
2428 {"on_move_out", c_on_move_out, "state=not_pressed"},
2429 {"on_key", c_on_key, "key=any"},
2432 {"play", c_play, "name loop=0 @nomultiple=0"},
2433 {"stop", c_stop, "name"},
2434 {"nextframe", c_nextframe, "name"},
2435 {"previousframe", c_previousframe, "name"},
2437 // object placement tags
2438 {"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="},
2439 {"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="},
2440 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2441 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2442 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2443 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2444 {"del", c_del, "name"},
2445 // virtual object placement
2446 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2448 // commands which start a block
2449 //startclip (see above)
2450 {"sprite", c_sprite, "name"},
2451 {"action", c_action, ""},
2457 static map_t parseArguments(char*command, char*pattern)
2473 string_set(&t1, "commandname");
2474 string_set(&t2, command);
2475 map_put(&result, t1, t2);
2477 if(!pattern || !*pattern)
2484 if(!strncmp("<i> ", x, 3)) {
2486 if(type == COMMAND || type == RAWDATA) {
2488 syntaxerror("character name expected");
2490 name[pos].str = "instance";
2492 value[pos].str = text;
2493 value[pos].len = strlen(text);
2497 if(type == ASSIGNMENT)
2500 name[pos].str = "character";
2502 value[pos].str = text;
2503 value[pos].len = strlen(text);
2511 isboolean[pos] = (x[0] =='@');
2524 name[pos].len = d-x;
2529 name[pos].len = e-x;
2530 value[pos].str = e+1;
2531 value[pos].len = d-e-1;
2539 /* for(t=0;t<len;t++) {
2540 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2541 isboolean[t]?"(boolean)":"");
2546 if(type == RAWDATA || type == COMMAND) {
2551 // first, search for boolean arguments
2552 for(pos=0;pos<len;pos++)
2554 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2556 if(type == ASSIGNMENT)
2558 value[pos].str = text;
2559 value[pos].len = strlen(text);
2560 /*printf("setting boolean parameter %s (to %s)\n",
2561 strdup_n(name[pos], namelen[pos]),
2562 strdup_n(value[pos], valuelen[pos]));*/
2567 // second, search for normal arguments
2569 for(pos=0;pos<len;pos++)
2571 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2572 (type != ASSIGNMENT && !set[pos])) {
2574 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2576 if(type == ASSIGNMENT)
2579 value[pos].str = text;
2580 value[pos].len = strlen(text);
2582 printf("setting parameter %s (to %s)\n",
2583 strdup_n(name[pos].str, name[pos].len),
2584 strdup_n(value[pos].str, value[pos].len));
2590 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2594 for(t=0;t<len;t++) {
2595 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2598 for(t=0;t<len;t++) {
2599 if(value[t].str && value[t].str[0] == '*') {
2600 //relative default- take value from some other parameter
2602 for(s=0;s<len;s++) {
2603 if(value[s].len == value[t].len-1 &&
2604 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2605 value[t].str = value[s].str;
2608 if(value[t].str == 0) {
2610 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2614 /* ok, now construct the dictionary from the parameters */
2618 map_put(&result, name[t], value[t]);
2622 static void parseArgumentsForCommand(char*command)
2627 msg("<verbose> parse Command: %s (line %d)", command, line);
2629 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2630 if(!strcmp(arguments[t].command, command)) {
2632 /* ugly hack- will be removed soon (once documentation and .sc generating
2633 utilities have been changed) */
2634 if(!strcmp(command, "swf") && !stackpos) {
2635 warning("Please use .flash instead of .swf- this will be mandatory soon");
2640 args = parseArguments(command, arguments[t].arguments);
2646 syntaxerror("command %s not known", command);
2648 // catch missing .flash directives at the beginning of a file
2649 if(strcmp(command, "flash") && !stackpos)
2651 syntaxerror("No movie defined- use .flash first");
2655 printf(".%s\n", command);fflush(stdout);
2656 map_dump(&args, stdout, "\t");fflush(stdout);
2659 (*arguments[nr].func)(&args);
2661 /*if(!strcmp(command, "button") ||
2662 !strcmp(command, "action")) {
2665 if(type == COMMAND) {
2666 if(!strcmp(text, "end"))
2680 int main (int argc,char ** argv)
2683 processargs(argc, argv);
2684 initLog(0,-1,0,0,-1,verbose);
2687 args_callback_usage(argv[0]);
2691 file = generateTokens(filename);
2693 printf("parser returned error.\n");
2699 while(!noMoreTokens()) {
2702 syntaxerror("command expected");
2703 parseArgumentsForCommand(text);