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 if(!stackpos || (stack[stackpos-1].type != 3)) {
482 syntaxerror(".show may only appear in .button");
485 syntaxerror("character %s not known (in .shape %s)", character, character);
487 if(mybutton.endofshapes) {
488 syntaxerror("a .do may not precede a .show", character, character);
491 m = s_instancepos(c->size, &p);
499 if(*s==',' || *s==0) {
500 if(!strncmp(o,"idle",s-o)) mybutton.records[0]=r;
501 else if(!strncmp(o,"shape",s-o)) mybutton.records[0]=r;
502 else if(!strncmp(o,"hover",s-o)) mybutton.records[1]=r;
503 else if(!strncmp(o,"pressed",s-o)) mybutton.records[2]=r;
504 else if(!strncmp(o,"area",s-o)) mybutton.records[3]=r;
505 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
512 static void setbuttonrecords(TAG*tag)
514 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
515 if(!mybutton.endofshapes) {
518 if(!mybutton.records[3].set) {
519 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
523 if(mybutton.records[t].set) {
524 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
527 swf_SetU8(tag,0); // end of button records
528 mybutton.endofshapes = 1;
532 void s_buttonaction(int flags, char*action)
538 setbuttonrecords(stack[stackpos-1].tag);
540 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
542 syntaxerror("Couldn't compile ActionScript");
545 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
546 swf_ActionSet(stack[stackpos-1].tag, a);
547 mybutton.nr_actions++;
552 static void setactionend(TAG*tag)
554 if(!mybutton.nr_actions) {
555 /* no actions means we didn't have an actionoffset,
556 which means we can't signal the end of the
557 buttonaction records, so, *sigh*, we have
558 to insert a dummy record */
559 swf_SetU16(tag, 0); //offset
560 swf_SetU16(tag, 0); //condition
561 swf_SetU8(tag, 0); //action
565 static void s_endButton()
568 setbuttonrecords(stack[stackpos-1].tag);
569 setactionend(stack[stackpos-1].tag);
572 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
576 tag = stack[stackpos].tag;
577 currentrect = stack[stackpos].oldrect;
579 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
580 free(stack[stackpos].name);
583 TAG* removeFromTo(TAG*from, TAG*to)
585 TAG*save = from->prev;
587 TAG*next = from->next;
595 static void s_endSprite()
597 SRECT r = currentrect;
599 if(stack[stackpos].cut)
600 tag = removeFromTo(stack[stackpos].cut, tag);
604 /* TODO: before clearing, prepend "<spritename>." to names and
605 copy into old instances dict */
606 dictionary_clear(&instances);
608 currentframe = stack[stackpos].oldframe;
609 currentrect = stack[stackpos].oldrect;
610 currentdepth = stack[stackpos].olddepth;
611 instances = stack[stackpos].oldinstances;
613 tag = swf_InsertTag(tag, ST_END);
615 tag = stack[stackpos].tag;
618 syntaxerror("internal error(7)");
620 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
621 free(stack[stackpos].name);
624 static void s_endSWF()
630 if(stack[stackpos].cut)
631 tag = removeFromTo(stack[stackpos].cut, tag);
635 swf = stack[stackpos].swf;
636 filename = stack[stackpos].filename;
638 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
640 tag = swf_InsertTag(tag, ST_END);
642 swf_OptimizeTagOrder(swf);
644 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
645 swf->movieSize = currentrect; /* "autocrop" */
648 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
649 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
650 swf->movieSize.ymax += 20;
653 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
655 syntaxerror("couldn't create output file %s", filename);
658 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
660 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
664 dictionary_clear(&instances);
665 dictionary_clear(&characters);
666 dictionary_clear(&images);
667 dictionary_clear(&outlines);
668 dictionary_clear(&gradients);
669 dictionary_clear(&fonts);
670 dictionary_clear(&sounds);
680 if(stack[stackpos-1].type == 0)
681 syntaxerror("End of file encountered in .flash block");
682 if(stack[stackpos-1].type == 1)
683 syntaxerror("End of file encountered in .sprite block");
684 if(stack[stackpos-1].type == 2)
685 syntaxerror("End of file encountered in .clip block");
694 void s_frame(int nr, int cut)
699 for(t=currentframe;t<nr;t++) {
700 tag = swf_InsertTag(tag, ST_SHOWFRAME);
705 syntaxerror("Can't cut, frame empty");
707 stack[stackpos].cut = tag;
713 int parseColor2(char*str, RGBA*color);
715 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
720 if(texture[0] == '#') {
721 parseColor2(texture, &color);
722 return swf_ShapeAddSolidFillStyle(s, &color);
723 } else if((image = dictionary_lookup(&images, texture))) {
725 swf_GetMatrix(0, &m);
726 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
727 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
730 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
731 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
732 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
734 swf_GetMatrix(0, &m);
735 m.sx = (r->xmax - r->xmin)*2;
736 m.sy = (r->ymax - r->ymin)*2;
737 m.tx = r->xmin + (r->xmax - r->xmin)/2;
738 m.ty = r->ymin + (r->ymax - r->ymin)/2;
739 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
740 } else if (parseColor2(texture, &color)) {
741 return swf_ShapeAddSolidFillStyle(s, &color);
743 syntaxerror("not a color/fillstyle: %s", texture);
748 RGBA black={r:0,g:0,b:0,a:0};
749 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
758 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
760 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
763 fs1 = addFillStyle(s, &r2, texture);
766 r.xmin = r2.xmin-linewidth-linewidth/2;
767 r.ymin = r2.ymin-linewidth-linewidth/2;
768 r.xmax = r2.xmax+linewidth+linewidth/2;
769 r.ymax = r2.ymax+linewidth+linewidth/2;
771 swf_SetShapeHeader(tag,s);
772 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
773 swf_ShapeSetLine(tag,s,width,0);
774 swf_ShapeSetLine(tag,s,0,height);
775 swf_ShapeSetLine(tag,s,-width,0);
776 swf_ShapeSetLine(tag,s,0,-height);
777 swf_ShapeSetEnd(tag);
780 s_addcharacter(name, id, tag, r);
784 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
790 outline = dictionary_lookup(&outlines, outlinename);
792 syntaxerror("outline %s not defined", outlinename);
796 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
798 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
800 fs1 = addFillStyle(s, &r2, texture);
802 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
804 rect.xmin = r2.xmin-linewidth-linewidth/2;
805 rect.ymin = r2.ymin-linewidth-linewidth/2;
806 rect.xmax = r2.xmax+linewidth+linewidth/2;
807 rect.ymax = r2.ymax+linewidth+linewidth/2;
809 swf_SetRect(tag,&rect);
810 swf_SetShapeStyles(tag, s);
811 swf_SetShapeBits(tag, outline->shape); //does not count bits!
812 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
815 s_addcharacter(name, id, tag, rect);
819 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
824 r2.xmin = r2.ymin = 0;
828 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
830 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
832 fs1 = addFillStyle(s, &r2, texture);
834 rect.xmin = r2.xmin-linewidth-linewidth/2;
835 rect.ymin = r2.ymin-linewidth-linewidth/2;
836 rect.xmax = r2.xmax+linewidth+linewidth/2;
837 rect.ymax = r2.ymax+linewidth+linewidth/2;
839 swf_SetRect(tag,&rect);
840 swf_SetShapeHeader(tag,s);
841 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
842 swf_ShapeSetCircle(tag, s, r,r,r,r);
843 swf_ShapeSetEnd(tag);
846 s_addcharacter(name, id, tag, rect);
850 void s_textshape(char*name, char*fontname, float size, char*_text)
853 U8*text = (U8*)_text;
857 font = dictionary_lookup(&fonts, fontname);
859 syntaxerror("font \"%s\" not known!", fontname);
861 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
862 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
863 s_box(name, 0, 0, black, 20, 0);
866 g = font->ascii2glyph[text[0]];
868 outline = malloc(sizeof(outline_t));
869 memset(outline, 0, sizeof(outline_t));
870 outline->shape = font->glyph[g].shape;
871 outline->bbox = font->layout->bounds[g];
875 swf_Shape11DrawerInit(&draw, 0);
876 swf_DrawText(&draw, font, (int)(size*100), _text);
878 outline->shape = swf_ShapeDrawerToShape(&draw);
879 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
883 if(dictionary_lookup(&outlines, name))
884 syntaxerror("outline %s defined twice", name);
885 dictionary_put2(&outlines, name, outline);
888 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
893 font = dictionary_lookup(&fonts, fontname);
895 syntaxerror("font \"%s\" not known!", fontname);
897 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
899 if(!font->numchars) {
900 s_box(name, 0, 0, black, 20, 0);
903 r = swf_SetDefineText(tag, font, &color, text, size);
905 s_addcharacter(name, id, tag, r);
909 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
912 EditTextLayout layout;
915 font = dictionary_lookup(&fonts, fontname);
917 syntaxerror("font \"%s\" not known!", fontname);
918 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
921 layout.leftmargin = 0;
922 layout.rightmargin = 0;
929 swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
931 s_addcharacter(name, id, tag, r);
935 /* type: either "jpeg" or "png"
937 void s_image(char*name, char*type, char*filename, int quality)
939 /* an image is actually two folded: 1st bitmap, 2nd character.
940 Both of them can be used separately */
942 /* step 1: the bitmap */
947 warning("image type \"png\" not supported yet!");
948 s_box(name, 0, 0, black, 20, 0);
953 warning("no jpeg support compiled in");
954 s_box(name, 0, 0, black, 20, 0);
957 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
958 swf_SetU16(tag, imageID);
960 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
961 syntaxerror("Image \"%s\" not found, or contains errors", filename);
964 swf_GetJPEGSize(filename, &width, &height);
971 s_addimage(name, id, tag, r);
976 /* step 2: the character */
977 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
979 swf_ShapeSetBitmapRect(tag, imageID, width, height);
981 s_addcharacter(name, id, tag, r);
985 void dumpSWF(SWF*swf)
987 TAG* tag = swf->firstTag;
988 printf("vvvvvvvvvvvvvvvvvvvvv\n");
990 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
993 printf("^^^^^^^^^^^^^^^^^^^^^\n");
996 void s_font(char*name, char*filename)
999 font = swf_LoadFont(filename);
1002 warning("Couldn't open font file \"%s\"", filename);
1003 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1004 memset(font, 0, sizeof(SWFFONT));
1005 dictionary_put2(&fonts, name, font);
1011 /* fix the layout. Only needed for old fonts */
1013 for(t=0;t<font->numchars;t++) {
1014 font->glyph[t].advance = 0;
1017 swf_FontCreateLayout(font);
1021 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1022 swf_FontSetDefine2(tag, font);
1025 if(dictionary_lookup(&fonts, name))
1026 syntaxerror("font %s defined twice", name);
1027 dictionary_put2(&fonts, name, font);
1032 typedef struct _sound_t
1038 void s_sound(char*name, char*filename)
1040 struct WAV wav, wav2;
1045 if(!readWAV(filename, &wav)) {
1046 warning("Couldn't read wav file \"%s\"", filename);
1050 convertWAV2mono(&wav, &wav2, 44100);
1051 samples = (U16*)wav2.data;
1052 numsamples = wav2.size/2;
1056 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1057 swf_SetU16(tag, id); //id
1058 swf_SetSoundDefine(tag, samples, numsamples);
1060 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1064 if(dictionary_lookup(&sounds, name))
1065 syntaxerror("sound %s defined twice", name);
1066 dictionary_put2(&sounds, name, sound);
1074 static char* gradient_getToken(const char**p)
1078 while(**p && strchr(" \t\n\r", **p)) {
1082 while(**p && !strchr(" \t\n\r", **p)) {
1085 result = malloc((*p)-start+1);
1086 memcpy(result,start,(*p)-start+1);
1087 result[(*p)-start] = 0;
1091 float parsePercent(char*str);
1092 RGBA parseColor(char*str);
1094 GRADIENT parseGradient(const char*str)
1097 const char* p = str;
1098 memset(&gradient, 0, sizeof(GRADIENT));
1100 char*posstr,*colorstr;
1103 posstr = gradient_getToken(&p);
1106 pos = parsePercent(posstr);
1107 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1108 colorstr = gradient_getToken(&p);
1109 color = parseColor(colorstr);
1110 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1111 warning("gradient record too big- max size is 8, rest ignored");
1114 gradient.ratios[gradient.num] = (int)(pos*255.0);
1115 gradient.rgba[gradient.num] = color;
1123 void s_gradient(char*name, const char*text, int radial)
1125 gradient_t* gradient;
1126 gradient = malloc(sizeof(gradient_t));
1127 memset(gradient, 0, sizeof(gradient_t));
1128 gradient->gradient = parseGradient(text);
1129 gradient->radial = radial;
1131 if(dictionary_lookup(&gradients, name))
1132 syntaxerror("gradient %s defined twice", name);
1133 dictionary_put2(&gradients, name, gradient);
1136 void s_action(const char*text)
1139 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1141 syntaxerror("Couldn't compile ActionScript");
1144 tag = swf_InsertTag(tag, ST_DOACTION);
1146 swf_ActionSet(tag, a);
1151 int s_swf3action(char*name, char*action)
1154 instance_t* object = dictionary_lookup(&instances, name);
1158 a = action_SetTarget(0, name);
1159 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1160 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1161 else if(!strcmp(action, "stop")) a = action_Stop(a);
1162 else if(!strcmp(action, "play")) a = action_Play(a);
1163 a = action_SetTarget(a, "");
1166 tag = swf_InsertTag(tag, ST_DOACTION);
1167 swf_ActionSet(tag, a);
1172 void s_outline(char*name, char*format, char*source)
1181 swf_Shape11DrawerInit(&draw, 0);
1182 draw_string(&draw, source);
1184 shape = swf_ShapeDrawerToShape(&draw);
1185 //shape2 = swf_ShapeToShape2(shape);
1186 //bounds = swf_GetShapeBoundingBox(shape2);
1187 //swf_Shape2Free(shape2);
1188 bounds = swf_ShapeDrawerGetBBox(&draw);
1189 draw.dealloc(&draw);
1191 outline = (outline_t*)malloc(sizeof(outline_t));
1192 memset(outline, 0, sizeof(outline_t));
1193 outline->shape = shape;
1194 outline->bbox = bounds;
1196 if(dictionary_lookup(&outlines, name))
1197 syntaxerror("outline %s defined twice", name);
1198 dictionary_put2(&outlines, name, outline);
1201 int s_playsound(char*name, int loops, int nomultiple, int stop)
1203 sound_t* sound = dictionary_lookup(&sounds, name);
1208 tag = swf_InsertTag(tag, ST_STARTSOUND);
1209 swf_SetU16(tag, sound->id); //id
1210 memset(&info, 0, sizeof(info));
1213 info.nomultiple = nomultiple;
1214 swf_SetSoundInfo(tag, &info);
1218 void s_includeswf(char*name, char*filename)
1226 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1227 f = open(filename,O_RDONLY);
1229 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1230 s_box(name, 0, 0, black, 20, 0);
1233 if (swf_ReadSWF(f,&swf)<0) {
1234 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1235 s_box(name, 0, 0, black, 20, 0);
1240 /* FIXME: The following sets the bounding Box for the character.
1241 It is wrong for two reasons:
1242 a) It may be too small (in case objects in the movie clip at the borders)
1243 b) it may be too big (because the poor movie never got autocropped)
1247 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1248 swf_SetU16(tag, id);
1251 swf_Relocate(&swf, idmap);
1253 ftag = swf.firstTag;
1257 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1258 if(cutout[t] == ftag->id) {
1262 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1264 if(ftag->id == ST_END)
1268 /* We simply dump all tags right after the sprite
1269 header, relying on the fact that swf_OptimizeTagOrder() will
1270 sort things out for us later.
1271 We also rely on the fact that the imported SWF is well-formed.
1273 tag = swf_InsertTag(tag, ftag->id);
1274 swf_SetBlock(tag, ftag->data, ftag->len);
1278 syntaxerror("Included file %s contains errors", filename);
1279 tag = swf_InsertTag(tag, ST_END);
1283 s_addcharacter(name, id, tag, r);
1286 SRECT s_getCharBBox(char*name)
1288 character_t* c = dictionary_lookup(&characters, name);
1289 if(!c) syntaxerror("character '%s' unknown(2)", name);
1292 SRECT s_getInstanceBBox(char*name)
1294 instance_t * i = dictionary_lookup(&instances, name);
1296 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1298 if(!c) syntaxerror("internal error(5)");
1301 parameters_t s_getParameters(char*name)
1303 instance_t * i = dictionary_lookup(&instances, name);
1304 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1305 return i->parameters;
1307 void s_startclip(char*instance, char*character, parameters_t p)
1309 character_t* c = dictionary_lookup(&characters, character);
1313 syntaxerror("character %s not known", character);
1315 i = s_addinstance(instance, c, currentdepth);
1317 m = s_instancepos(i->character->size, &p);
1319 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1320 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1321 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1323 i->lastFrame= currentframe;
1325 stack[stackpos].tag = tag;
1326 stack[stackpos].type = 2;
1335 swf_SetTagPos(stack[stackpos].tag, 0);
1336 swf_GetPlaceObject(stack[stackpos].tag, &p);
1337 p.clipdepth = currentdepth;
1338 swf_ClearTag(stack[stackpos].tag);
1339 swf_SetPlaceObject(stack[stackpos].tag, &p);
1343 void s_put(char*instance, char*character, parameters_t p)
1345 character_t* c = dictionary_lookup(&characters, character);
1349 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1352 i = s_addinstance(instance, c, currentdepth);
1354 m = s_instancepos(i->character->size, &p);
1356 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1357 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1359 i->lastFrame = currentframe;
1363 void s_jump(char*instance, parameters_t p)
1365 instance_t* i = dictionary_lookup(&instances, instance);
1368 syntaxerror("instance %s not known", instance);
1372 m = s_instancepos(i->character->size, &p);
1374 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1375 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1377 i->lastFrame = currentframe;
1380 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1384 if(num==0 || num==1)
1386 ratio = (float)pos/(float)num;
1388 p.x = (p2->x-p1->x)*ratio + p1->x;
1389 p.y = (p2->y-p1->y)*ratio + p1->y;
1390 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1391 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1392 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1393 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1395 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1396 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1397 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1398 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1400 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1401 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1402 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1403 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1405 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1406 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1407 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1408 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1412 void s_change(char*instance, parameters_t p2)
1414 instance_t* i = dictionary_lookup(&instances, instance);
1418 int frame, allframes;
1420 syntaxerror("instance %s not known", instance);
1424 allframes = currentframe - i->lastFrame - 1;
1426 warning(".change ignored. can only .put/.change an object once per frame.");
1430 m = s_instancepos(i->character->size, &p2);
1431 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1432 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1435 /* o.k., we got the start and end point set. Now iterate though all the
1436 tags in between, inserting object changes after each new frame */
1439 if(!t) syntaxerror("internal error(6)");
1441 while(frame < allframes) {
1442 if(t->id == ST_SHOWFRAME) {
1447 p = s_interpolate(&p1, &p2, frame, allframes);
1448 m = s_instancepos(i->character->size, &p); //needed?
1449 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1450 i->lastFrame = currentframe;
1451 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1453 if(frame == allframes)
1458 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1462 void s_delinstance(char*instance)
1464 instance_t* i = dictionary_lookup(&instances, instance);
1466 syntaxerror("instance %s not known", instance);
1468 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1469 swf_SetU16(tag, i->depth);
1470 dictionary_del(&instances, instance);
1473 void s_qchange(char*instance, parameters_t p)
1480 syntaxerror(".end unexpected");
1481 if(stack[stackpos-1].type == 0)
1483 else if(stack[stackpos-1].type == 1)
1485 else if(stack[stackpos-1].type == 2)
1487 else if(stack[stackpos-1].type == 3)
1489 else syntaxerror("internal error 1");
1492 // ------------------------------------------------------------------------
1494 typedef int command_func_t(map_t*args);
1496 SRECT parseBox(char*str)
1499 float xmin, xmax, ymin, ymax;
1500 char*x = strchr(str, 'x');
1502 if(!strcmp(str, "autocrop")) {
1503 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1507 d1 = strchr(x+1, ':');
1509 d2 = strchr(d1+1, ':');
1511 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1515 else if(d1 && !d2) {
1516 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1522 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1527 r.xmin = (SCOORD)(xmin*20);
1528 r.ymin = (SCOORD)(ymin*20);
1529 r.xmax = (SCOORD)(xmax*20);
1530 r.ymax = (SCOORD)(ymax*20);
1533 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1536 float parseFloat(char*str)
1540 int parseInt(char*str)
1545 if(str[0]=='+' || str[0]=='-')
1549 if(str[t]<'0' || str[t]>'9')
1550 syntaxerror("Not an Integer: \"%s\"", str);
1553 int parseTwip(char*str)
1557 if(str[0]=='+' || str[0]=='-') {
1562 dot = strchr(str, '.');
1566 return sign*parseInt(str)*20;
1568 int l=strlen(++dot);
1570 for(s=str;s<dot-1;s++)
1571 if(*s<'0' || *s>'9')
1572 syntaxerror("Not a coordinate: \"%s\"", str);
1574 if(*s<'0' || *s>'9')
1575 syntaxerror("Not a coordinate: \"%s\"", str);
1577 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1578 warning("precision loss: %s converted to twip", str);
1583 return sign*atoi(str)*20;
1585 return sign*atoi(str)*20+atoi(dot)*2;
1587 return sign*atoi(str)*20+atoi(dot)/5;
1592 int isPoint(char*str)
1594 if(strchr(str, '('))
1600 SPOINT parsePoint(char*str)
1604 int l = strlen(str);
1605 char*comma = strchr(str, ',');
1606 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1607 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1608 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1609 p.x = parseTwip(tmp);
1610 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1611 p.y = parseTwip(tmp);
1615 int parseColor2(char*str, RGBA*color)
1617 int l = strlen(str);
1621 struct {unsigned char r,g,b;char*name;} colors[] =
1622 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1623 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1624 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1625 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1626 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1627 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1628 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1629 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1630 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1631 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1632 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1633 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1637 if(str[0]=='#' && (l==7 || l==9)) {
1638 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1640 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1642 color->r = r; color->g = g; color->b = b; color->a = a;
1645 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1646 if(!strcmp(str, colors[t].name)) {
1651 color->r = r; color->g = g; color->b = b; color->a = a;
1657 RGBA parseColor(char*str)
1660 if(!parseColor2(str, &c))
1661 syntaxerror("Expression '%s' is not a color", str);
1665 typedef struct _muladd {
1670 MULADD parseMulAdd(char*str)
1673 char* str2 = (char*)malloc(strlen(str)+5);
1680 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1681 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1682 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1683 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1684 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1685 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1686 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1687 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1688 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1689 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1691 syntaxerror("'%s' is not a valid color transform expression", str);
1693 m.add = (int)(add*256);
1694 m.mul = (int)(mul*256);
1699 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1701 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1702 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1704 if(a<-32768) a=-32768;
1705 if(a>32767) a=32767;
1706 if(m<-32768) m=-32768;
1707 if(m>32767) m=32767;
1713 float parsePercent(char*str)
1715 int l = strlen(str);
1719 return atoi(str)/100.0;
1721 syntaxerror("Expression '%s' is not a percentage", str);
1724 int isPercent(char*str)
1726 return str[strlen(str)-1]=='%';
1728 int parseNewSize(char*str, int size)
1731 return parsePercent(str)*size;
1733 return (int)(atof(str)*20);
1736 int isColor(char*str)
1739 return parseColor2(str, &c);
1742 static char* lu(map_t* args, char*name)
1744 char* value = map_lookup(args, name);
1746 map_dump(args, stdout, "");
1747 syntaxerror("internal error 2: value %s should be set", name);
1752 static int c_flash(map_t*args)
1754 char* name = lu(args, "name");
1755 char* compressstr = lu(args, "compress");
1756 SRECT bbox = parseBox(lu(args, "bbox"));
1757 int version = parseInt(lu(args, "version"));
1758 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1760 RGBA color = parseColor(lu(args, "background"));
1761 if(!strcmp(name, "!default!") || override_outputname)
1764 if(!strcmp(compressstr, "default"))
1765 compress = version==6;
1766 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1768 else if(!strcmp(compressstr, "no"))
1770 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1772 s_swf(name, bbox, version, fps, compress, color);
1775 int isRelative(char*str)
1777 return !strncmp(str, "<plus>", 6) ||
1778 !strncmp(str, "<minus>", 7);
1780 char* getOffset(char*str)
1782 if(!strncmp(str, "<plus>", 6))
1784 if(!strncmp(str, "<minus>", 7))
1786 syntaxerror("internal error (347)");
1789 int getSign(char*str)
1791 if(!strncmp(str, "<plus>", 6))
1793 if(!strncmp(str, "<minus>", 7))
1795 syntaxerror("internal error (348)");
1798 static dictionary_t points;
1799 static mem_t mpoints;
1800 int points_initialized = 0;
1802 SPOINT getPoint(SRECT r, char*name)
1805 if(!strcmp(name, "center")) {
1807 p.x = (r.xmin + r.xmax)/2;
1808 p.y = (r.ymin + r.ymax)/2;
1812 if(points_initialized)
1813 l = (int)dictionary_lookup(&points, name);
1815 syntaxerror("Invalid point: \"%s\".", name);
1818 return *(SPOINT*)&mpoints.buffer[l];
1820 static int c_gradient(map_t*args)
1822 char*name = lu(args, "name");
1823 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1827 syntaxerror("colon (:) expected");
1829 s_gradient(name, text, radial);
1832 static int c_point(map_t*args)
1834 char*name = lu(args, "name");
1838 if(!points_initialized) {
1839 dictionary_init(&points);
1841 points_initialized = 1;
1843 p.x = parseTwip(lu(args, "x"));
1844 p.y = parseTwip(lu(args, "y"));
1845 pos = mem_put(&mpoints, &p, sizeof(p));
1846 string_set(&s1, name);
1848 dictionary_put(&points, s1, (void*)pos);
1851 static int c_play(map_t*args)
1853 char*name = lu(args, "name");
1854 char*loop = lu(args, "loop");
1855 char*nomultiple = lu(args, "nomultiple");
1857 if(!strcmp(nomultiple, "nomultiple"))
1860 nm = parseInt(nomultiple);
1862 if(s_playsound(name, parseInt(loop), nm, 0)) {
1864 } else if(s_swf3action(name, "play")) {
1870 static int c_stop(map_t*args)
1872 char*name = lu(args, "name");
1874 if(s_playsound(name, 0,0,1)) {
1876 } else if(s_swf3action(name, "stop")) {
1879 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1883 static int c_nextframe(map_t*args)
1885 char*name = lu(args, "name");
1887 if(s_swf3action(name, "nextframe")) {
1890 syntaxerror("I don't know anything about movie \"%s\"", name);
1894 static int c_previousframe(map_t*args)
1896 char*name = lu(args, "name");
1898 if(s_swf3action(name, "previousframe")) {
1901 syntaxerror("I don't know anything about movie \"%s\"", name);
1905 static int c_placement(map_t*args, int type)
1907 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1910 char* luminancestr = lu(args, "luminance");
1911 char* scalestr = lu(args, "scale");
1912 char* scalexstr = lu(args, "scalex");
1913 char* scaleystr = lu(args, "scaley");
1914 char* rotatestr = lu(args, "rotate");
1915 char* shearstr = lu(args, "shear");
1916 char* xstr="", *pivotstr="";
1917 char* ystr="", *anglestr="";
1918 char*above = lu(args, "above"); /*FIXME*/
1919 char*below = lu(args, "below");
1920 char* rstr = lu(args, "red");
1921 char* gstr = lu(args, "green");
1922 char* bstr = lu(args, "blue");
1923 char* astr = lu(args, "alpha");
1924 char* pinstr = lu(args, "pin");
1925 char* as = map_lookup(args, "as");
1933 if(type==9) { // (?) .rotate or .arcchange
1934 pivotstr = lu(args, "pivot");
1935 anglestr = lu(args, "angle");
1937 xstr = lu(args, "x");
1938 ystr = lu(args, "y");
1941 luminance = parseMulAdd(luminancestr);
1944 luminance.mul = 256;
1948 if(scalexstr[0]||scaleystr[0])
1949 syntaxerror("scalex/scaley and scale cannot both be set");
1950 scalexstr = scaleystr = scalestr;
1953 if(type == 0 || type == 4) {
1955 character = lu(args, "character");
1956 parameters_clear(&p);
1957 } else if (type == 5) {
1958 character = lu(args, "name");
1959 parameters_clear(&p);
1962 p = s_getParameters(instance);
1967 if(isRelative(xstr)) {
1968 if(type == 0 || type == 4)
1969 syntaxerror("relative x values not allowed for initial put or startclip");
1970 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1972 p.x = parseTwip(xstr);
1976 if(isRelative(ystr)) {
1977 if(type == 0 || type == 4)
1978 syntaxerror("relative y values not allowed for initial put or startclip");
1979 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1981 p.y = parseTwip(ystr);
1985 /* scale, scalex, scaley */
1987 oldbbox = s_getCharBBox(character);
1989 oldbbox = s_getInstanceBBox(instance);
1991 oldwidth = oldbbox.xmax - oldbbox.xmin;
1992 oldheight = oldbbox.ymax - oldbbox.ymin;
1994 if(oldwidth==0) p.scalex = 1.0;
1997 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2001 if(oldheight==0) p.scaley = 1.0;
2004 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2010 if(isRelative(rotatestr)) {
2011 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2013 p.rotate = parseFloat(rotatestr);
2019 if(isRelative(shearstr)) {
2020 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2022 p.shear = parseFloat(shearstr);
2027 if(isPoint(pivotstr))
2028 p.pivot = parsePoint(pivotstr);
2030 p.pivot = getPoint(oldbbox, pivotstr);
2034 p.pin = parsePoint(pinstr);
2036 p.pin = getPoint(oldbbox, pinstr);
2039 /* color transform */
2041 if(rstr[0] || luminancestr[0]) {
2044 r = parseMulAdd(rstr);
2046 r.add = p.cxform.r0;
2047 r.mul = p.cxform.r1;
2049 r = mergeMulAdd(r, luminance);
2050 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2052 if(gstr[0] || luminancestr[0]) {
2055 g = parseMulAdd(gstr);
2057 g.add = p.cxform.g0;
2058 g.mul = p.cxform.g1;
2060 g = mergeMulAdd(g, luminance);
2061 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2063 if(bstr[0] || luminancestr[0]) {
2066 b = parseMulAdd(bstr);
2068 b.add = p.cxform.b0;
2069 b.mul = p.cxform.b1;
2071 b = mergeMulAdd(b, luminance);
2072 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2075 MULADD a = parseMulAdd(astr);
2076 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2080 s_put(instance, character, p);
2082 s_change(instance, p);
2084 s_qchange(instance, p);
2086 s_jump(instance, p);
2088 s_startclip(instance, character, p);
2089 else if(type == 5) {
2091 s_buttonput(character, as, p);
2093 s_buttonput(character, "shape", p);
2098 static int c_put(map_t*args)
2100 c_placement(args, 0);
2103 static int c_change(map_t*args)
2105 c_placement(args, 1);
2108 static int c_qchange(map_t*args)
2110 c_placement(args, 2);
2113 static int c_arcchange(map_t*args)
2115 c_placement(args, 2);
2118 static int c_jump(map_t*args)
2120 c_placement(args, 3);
2123 static int c_startclip(map_t*args)
2125 c_placement(args, 4);
2128 static int c_show(map_t*args)
2130 c_placement(args, 5);
2133 static int c_del(map_t*args)
2135 char*instance = lu(args, "name");
2136 s_delinstance(instance);
2139 static int c_end(map_t*args)
2144 static int c_sprite(map_t*args)
2146 char* name = lu(args, "name");
2150 static int c_frame(map_t*args)
2152 char*framestr = lu(args, "n");
2153 char*cutstr = lu(args, "cut");
2156 if(strcmp(cutstr, "no"))
2158 if(isRelative(framestr)) {
2159 frame = s_getframe();
2160 if(getSign(framestr)<0)
2161 syntaxerror("relative frame expressions must be positive");
2162 frame += parseInt(getOffset(framestr));
2165 frame = parseInt(framestr);
2166 if(s_getframe() >= frame
2167 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2168 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2170 s_frame(frame, cut);
2173 static int c_primitive(map_t*args)
2175 char*name = lu(args, "name");
2176 char*command = lu(args, "commandname");
2177 int width=0, height=0, r=0;
2178 int linewidth = parseTwip(lu(args, "line"));
2179 char*colorstr = lu(args, "color");
2180 RGBA color = parseColor(colorstr);
2181 char*fillstr = lu(args, "fill");
2188 if(!strcmp(command, "circle"))
2190 else if(!strcmp(command, "filled"))
2194 width = parseTwip(lu(args, "width"));
2195 height = parseTwip(lu(args, "height"));
2196 } else if (type==1) {
2197 r = parseTwip(lu(args, "r"));
2198 } else if (type==2) {
2199 outline = lu(args, "outline");
2202 if(!strcmp(fillstr, "fill"))
2204 if(!strcmp(fillstr, "none"))
2206 if(width<0 || height<0 || linewidth<0 || r<0)
2207 syntaxerror("values width, height, line, r must be positive");
2209 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2210 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2211 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2215 static int c_textshape(map_t*args)
2217 char*name = lu(args, "name");
2218 char*text = lu(args, "text");
2219 char*font = lu(args, "font");
2220 float size = parsePercent(lu(args, "size"));
2222 s_textshape(name, font, size, text);
2226 static int c_swf(map_t*args)
2228 char*name = lu(args, "name");
2229 char*filename = lu(args, "filename");
2230 char*command = lu(args, "commandname");
2231 if(!strcmp(command, "shape"))
2232 warning("Please use .swf instead of .shape");
2233 s_includeswf(name, filename);
2237 static int c_font(map_t*args)
2239 char*name = lu(args, "name");
2240 char*filename = lu(args, "filename");
2241 s_font(name, filename);
2245 static int c_sound(map_t*args)
2247 char*name = lu(args, "name");
2248 char*filename = lu(args, "filename");
2249 s_sound(name, filename);
2253 static int c_text(map_t*args)
2255 char*name = lu(args, "name");
2256 char*text = lu(args, "text");
2257 char*font = lu(args, "font");
2258 float size = parsePercent(lu(args, "size"));
2259 RGBA color = parseColor(lu(args, "color"));
2260 s_text(name, font, text, (int)(size*100), color);
2264 static int c_soundtrack(map_t*args)
2269 static int c_image(map_t*args)
2271 char*command = lu(args, "commandname");
2272 char*name = lu(args, "name");
2273 char*filename = lu(args, "filename");
2274 if(!strcmp(command,"jpeg")) {
2275 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2276 s_image(name, "jpeg", filename, quality);
2278 s_image(name, "png", filename, 0);
2283 static int c_outline(map_t*args)
2285 char*name = lu(args, "name");
2286 char*format = lu(args, "format");
2290 syntaxerror("colon (:) expected");
2292 s_outline(name, format, text);
2296 int fakechar(map_t*args)
2298 char*name = lu(args, "name");
2299 s_box(name, 0, 0, black, 20, 0);
2303 static int c_egon(map_t*args) {return fakechar(args);}
2304 static int c_button(map_t*args) {
2305 char*name = lu(args, "name");
2309 static int current_button_flags = 0;
2310 static int c_on_press(map_t*args)
2312 char*position = lu(args, "position");
2314 if(!strcmp(position, "inside")) {
2315 current_button_flags |= BC_OVERUP_OVERDOWN;
2316 } else if(!strcmp(position, "outside")) {
2317 //current_button_flags |= BC_IDLE_OUTDOWN;
2318 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2319 } else if(!strcmp(position, "anywhere")) {
2320 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2323 if(type == RAWDATA) {
2325 s_buttonaction(current_button_flags, action);
2326 current_button_flags = 0;
2332 static int c_on_release(map_t*args)
2334 char*position = lu(args, "position");
2336 if(!strcmp(position, "inside")) {
2337 current_button_flags |= BC_OVERDOWN_OVERUP;
2338 } else if(!strcmp(position, "outside")) {
2339 current_button_flags |= BC_OUTDOWN_IDLE;
2340 } else if(!strcmp(position, "anywhere")) {
2341 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2344 if(type == RAWDATA) {
2346 s_buttonaction(current_button_flags, action);
2347 current_button_flags = 0;
2353 static int c_on_move_in(map_t*args)
2355 char*position = lu(args, "state");
2357 if(!strcmp(position, "pressed")) {
2358 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2359 } else if(!strcmp(position, "not_pressed")) {
2360 current_button_flags |= BC_IDLE_OVERUP;
2361 } else if(!strcmp(position, "any")) {
2362 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2365 if(type == RAWDATA) {
2367 s_buttonaction(current_button_flags, action);
2368 current_button_flags = 0;
2374 static int c_on_move_out(map_t*args)
2376 char*position = lu(args, "state");
2378 if(!strcmp(position, "pressed")) {
2379 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2380 } else if(!strcmp(position, "not_pressed")) {
2381 current_button_flags |= BC_OVERUP_IDLE;
2382 } else if(!strcmp(position, "any")) {
2383 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2386 if(type == RAWDATA) {
2388 s_buttonaction(current_button_flags, action);
2389 current_button_flags = 0;
2395 static int c_on_key(map_t*args)
2397 char*key = lu(args, "key");
2399 if(strlen(key)==1) {
2402 current_button_flags |= 0x4000 + (key[0]*0x200);
2404 syntaxerror("invalid character: %c"+key[0]);
2409 <ctrl-x> = 0x200*(x-'a')
2413 syntaxerror("invalid key: %s",key);
2416 if(type == RAWDATA) {
2418 s_buttonaction(current_button_flags, action);
2419 current_button_flags = 0;
2426 static int c_edittext(map_t*args)
2428 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2429 char*name = lu(args, "name");
2430 char*font = lu(args, "font");
2431 int size = (int)(100*20*parsePercent(lu(args, "size")));
2432 int width = parseTwip(lu(args, "width"));
2433 int height = parseTwip(lu(args, "height"));
2434 char*text = lu(args, "text");
2435 RGBA color = parseColor(lu(args, "color"));
2436 int maxlength = parseInt(lu(args, "maxlength"));
2437 char*variable = lu(args, "variable");
2438 char*passwordstr = lu(args, "password");
2439 char*wordwrapstr = lu(args, "wordwrap");
2440 char*multilinestr = lu(args, "multiline");
2441 char*htmlstr = lu(args, "html");
2442 char*noselectstr = lu(args, "noselect");
2443 char*readonlystr = lu(args, "readonly");
2444 char*borderstr = lu(args, "border");
2447 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2448 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2449 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2450 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2451 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2452 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2453 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2455 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2459 static int c_morphshape(map_t*args) {return fakechar(args);}
2460 static int c_movie(map_t*args) {return fakechar(args);}
2462 static int c_texture(map_t*args) {return 0;}
2464 static int c_action(map_t*args)
2467 if(type != RAWDATA) {
2468 syntaxerror("colon (:) expected");
2478 command_func_t* func;
2481 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2482 {"frame", c_frame, "n=<plus>1 @cut=no"},
2483 // "import" type stuff
2484 {"swf", c_swf, "name filename"},
2485 {"shape", c_swf, "name filename"},
2486 {"jpeg", c_image, "name filename quality=80%"},
2487 {"png", c_image, "name filename"},
2488 {"movie", c_movie, "name filename"},
2489 {"sound", c_sound, "name filename"},
2490 {"font", c_font, "name filename"},
2491 {"soundtrack", c_soundtrack, "filename"},
2493 // generators of primitives
2495 {"point", c_point, "name x=0 y=0"},
2496 {"gradient", c_gradient, "name @radial=0"},
2497 {"outline", c_outline, "name format=simple"},
2498 {"textshape", c_textshape, "name font size=100% text"},
2500 // character generators
2501 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2502 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2503 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2505 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2506 {"text", c_text, "name text font size=100% color=white"},
2507 {"edittext", c_edittext, "name font size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0"},
2508 {"morphshape", c_morphshape, "name start end"},
2509 {"button", c_button, "name"},
2510 {"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="},
2511 {"on_press", c_on_press, "position=inside"},
2512 {"on_release", c_on_release, "position=anywhere"},
2513 {"on_move_in", c_on_move_out, "state=not_pressed"},
2514 {"on_move_out", c_on_move_out, "state=not_pressed"},
2515 {"on_key", c_on_key, "key=any"},
2518 {"play", c_play, "name loop=0 @nomultiple=0"},
2519 {"stop", c_stop, "name"},
2520 {"nextframe", c_nextframe, "name"},
2521 {"previousframe", c_previousframe, "name"},
2523 // object placement tags
2524 {"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="},
2525 {"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="},
2526 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2527 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2528 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2529 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2530 {"del", c_del, "name"},
2531 // virtual object placement
2532 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2534 // commands which start a block
2535 //startclip (see above)
2536 {"sprite", c_sprite, "name"},
2537 {"action", c_action, ""},
2543 static map_t parseArguments(char*command, char*pattern)
2559 string_set(&t1, "commandname");
2560 string_set(&t2, command);
2561 map_put(&result, t1, t2);
2563 if(!pattern || !*pattern)
2570 if(!strncmp("<i> ", x, 3)) {
2572 if(type == COMMAND || type == RAWDATA) {
2574 syntaxerror("character name expected");
2576 name[pos].str = "instance";
2578 value[pos].str = text;
2579 value[pos].len = strlen(text);
2583 if(type == ASSIGNMENT)
2586 name[pos].str = "character";
2588 value[pos].str = text;
2589 value[pos].len = strlen(text);
2597 isboolean[pos] = (x[0] =='@');
2610 name[pos].len = d-x;
2615 name[pos].len = e-x;
2616 value[pos].str = e+1;
2617 value[pos].len = d-e-1;
2625 /* for(t=0;t<len;t++) {
2626 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2627 isboolean[t]?"(boolean)":"");
2632 if(type == RAWDATA || type == COMMAND) {
2637 // first, search for boolean arguments
2638 for(pos=0;pos<len;pos++)
2640 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2642 if(type == ASSIGNMENT)
2644 value[pos].str = text;
2645 value[pos].len = strlen(text);
2646 /*printf("setting boolean parameter %s (to %s)\n",
2647 strdup_n(name[pos], namelen[pos]),
2648 strdup_n(value[pos], valuelen[pos]));*/
2653 // second, search for normal arguments
2655 for(pos=0;pos<len;pos++)
2657 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2658 (type != ASSIGNMENT && !set[pos])) {
2660 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2662 if(type == ASSIGNMENT)
2665 value[pos].str = text;
2666 value[pos].len = strlen(text);
2668 printf("setting parameter %s (to %s)\n",
2669 strdup_n(name[pos].str, name[pos].len),
2670 strdup_n(value[pos].str, value[pos].len));
2676 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2680 for(t=0;t<len;t++) {
2681 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2684 for(t=0;t<len;t++) {
2685 if(value[t].str && value[t].str[0] == '*') {
2686 //relative default- take value from some other parameter
2688 for(s=0;s<len;s++) {
2689 if(value[s].len == value[t].len-1 &&
2690 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2691 value[t].str = value[s].str;
2694 if(value[t].str == 0) {
2696 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2700 /* ok, now construct the dictionary from the parameters */
2704 map_put(&result, name[t], value[t]);
2708 static void parseArgumentsForCommand(char*command)
2713 msg("<verbose> parse Command: %s (line %d)", command, line);
2715 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2716 if(!strcmp(arguments[t].command, command)) {
2718 /* ugly hack- will be removed soon (once documentation and .sc generating
2719 utilities have been changed) */
2720 if(!strcmp(command, "swf") && !stackpos) {
2721 warning("Please use .flash instead of .swf- this will be mandatory soon");
2726 args = parseArguments(command, arguments[t].arguments);
2732 syntaxerror("command %s not known", command);
2734 // catch missing .flash directives at the beginning of a file
2735 if(strcmp(command, "flash") && !stackpos)
2737 syntaxerror("No movie defined- use .flash first");
2741 printf(".%s\n", command);fflush(stdout);
2742 map_dump(&args, stdout, "\t");fflush(stdout);
2745 (*arguments[nr].func)(&args);
2747 /*if(!strcmp(command, "button") ||
2748 !strcmp(command, "action")) {
2751 if(type == COMMAND) {
2752 if(!strcmp(text, "end"))
2766 int main (int argc,char ** argv)
2769 processargs(argc, argv);
2770 initLog(0,-1,0,0,-1,verbose);
2773 args_callback_usage(argv[0]);
2777 file = generateTokens(filename);
2779 printf("parser returned error.\n");
2785 while(!noMoreTokens()) {
2788 syntaxerror("command expected");
2789 parseArgumentsForCommand(text);