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 swf_DumpMatrix(stdout,&mybutton.records[0].matrix);
515 static void setbuttonrecords(TAG*tag)
517 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
518 if(!mybutton.endofshapes) {
521 if(!mybutton.records[3].set) {
522 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
526 if(mybutton.records[t].set) {
527 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
530 swf_SetU8(tag,0); // end of button records
531 mybutton.endofshapes = 1;
535 void s_buttonaction(int flags, char*action)
541 setbuttonrecords(stack[stackpos-1].tag);
543 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
545 syntaxerror("Couldn't compile ActionScript");
548 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
549 swf_ActionSet(stack[stackpos-1].tag, a);
550 mybutton.nr_actions++;
555 static void setactionend(TAG*tag)
557 if(!mybutton.nr_actions) {
558 /* no actions means we didn't have an actionoffset,
559 which means we can't signal the end of the
560 buttonaction records, so, *sigh*, we have
561 to insert a dummy record */
562 swf_SetU16(tag, 0); //offset
563 swf_SetU16(tag, 0); //condition
564 swf_SetU8(tag, 0); //action
568 static void s_endButton()
571 setbuttonrecords(stack[stackpos-1].tag);
572 setactionend(stack[stackpos-1].tag);
575 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
579 tag = stack[stackpos].tag;
580 currentrect = stack[stackpos].oldrect;
582 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
583 free(stack[stackpos].name);
586 TAG* removeFromTo(TAG*from, TAG*to)
588 TAG*save = from->prev;
590 TAG*next = from->next;
598 static void s_endSprite()
600 SRECT r = currentrect;
602 if(stack[stackpos].cut)
603 tag = removeFromTo(stack[stackpos].cut, tag);
607 /* TODO: before clearing, prepend "<spritename>." to names and
608 copy into old instances dict */
609 dictionary_clear(&instances);
611 currentframe = stack[stackpos].oldframe;
612 currentrect = stack[stackpos].oldrect;
613 currentdepth = stack[stackpos].olddepth;
614 instances = stack[stackpos].oldinstances;
616 tag = swf_InsertTag(tag, ST_END);
618 tag = stack[stackpos].tag;
621 syntaxerror("internal error(7)");
623 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
624 free(stack[stackpos].name);
627 static void s_endSWF()
633 if(stack[stackpos].cut)
634 tag = removeFromTo(stack[stackpos].cut, tag);
638 swf = stack[stackpos].swf;
639 filename = stack[stackpos].filename;
641 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
643 tag = swf_InsertTag(tag, ST_END);
645 swf_OptimizeTagOrder(swf);
647 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
648 swf->movieSize = currentrect; /* "autocrop" */
651 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
652 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
653 swf->movieSize.ymax += 20;
656 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
658 syntaxerror("couldn't create output file %s", filename);
661 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
663 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
667 dictionary_clear(&instances);
668 dictionary_clear(&characters);
669 dictionary_clear(&images);
670 dictionary_clear(&outlines);
671 dictionary_clear(&gradients);
672 dictionary_clear(&fonts);
673 dictionary_clear(&sounds);
683 if(stack[stackpos-1].type == 0)
684 syntaxerror("End of file encountered in .flash block");
685 if(stack[stackpos-1].type == 1)
686 syntaxerror("End of file encountered in .sprite block");
687 if(stack[stackpos-1].type == 2)
688 syntaxerror("End of file encountered in .clip block");
697 void s_frame(int nr, int cut)
702 for(t=currentframe;t<nr;t++) {
703 tag = swf_InsertTag(tag, ST_SHOWFRAME);
708 syntaxerror("Can't cut, frame empty");
710 stack[stackpos].cut = tag;
716 int parseColor2(char*str, RGBA*color);
718 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
723 if(texture[0] == '#') {
724 parseColor2(texture, &color);
725 return swf_ShapeAddSolidFillStyle(s, &color);
726 } else if((image = dictionary_lookup(&images, texture))) {
728 swf_GetMatrix(0, &m);
729 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
730 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
733 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
734 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
735 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
737 swf_GetMatrix(0, &m);
738 m.sx = (r->xmax - r->xmin)*2;
739 m.sy = (r->ymax - r->ymin)*2;
740 m.tx = r->xmin + (r->xmax - r->xmin)/2;
741 m.ty = r->ymin + (r->ymax - r->ymin)/2;
742 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
743 } else if (parseColor2(texture, &color)) {
744 return swf_ShapeAddSolidFillStyle(s, &color);
746 syntaxerror("not a color/fillstyle: %s", texture);
751 RGBA black={r:0,g:0,b:0,a:0};
752 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
761 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
763 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
766 fs1 = addFillStyle(s, &r2, texture);
769 r.xmin = r2.xmin-linewidth-linewidth/2;
770 r.ymin = r2.ymin-linewidth-linewidth/2;
771 r.xmax = r2.xmax+linewidth+linewidth/2;
772 r.ymax = r2.ymax+linewidth+linewidth/2;
774 swf_SetShapeHeader(tag,s);
775 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
776 swf_ShapeSetLine(tag,s,width,0);
777 swf_ShapeSetLine(tag,s,0,height);
778 swf_ShapeSetLine(tag,s,-width,0);
779 swf_ShapeSetLine(tag,s,0,-height);
780 swf_ShapeSetEnd(tag);
783 s_addcharacter(name, id, tag, r);
787 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
793 outline = dictionary_lookup(&outlines, outlinename);
795 syntaxerror("outline %s not defined", outlinename);
799 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
801 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
803 fs1 = addFillStyle(s, &r2, texture);
805 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
807 rect.xmin = r2.xmin-linewidth-linewidth/2;
808 rect.ymin = r2.ymin-linewidth-linewidth/2;
809 rect.xmax = r2.xmax+linewidth+linewidth/2;
810 rect.ymax = r2.ymax+linewidth+linewidth/2;
812 swf_SetRect(tag,&rect);
813 swf_SetShapeStyles(tag, s);
814 swf_SetShapeBits(tag, outline->shape); //does not count bits!
815 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
818 s_addcharacter(name, id, tag, rect);
822 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
827 r2.xmin = r2.ymin = 0;
831 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
833 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
835 fs1 = addFillStyle(s, &r2, texture);
837 rect.xmin = r2.xmin-linewidth-linewidth/2;
838 rect.ymin = r2.ymin-linewidth-linewidth/2;
839 rect.xmax = r2.xmax+linewidth+linewidth/2;
840 rect.ymax = r2.ymax+linewidth+linewidth/2;
842 swf_SetRect(tag,&rect);
843 swf_SetShapeHeader(tag,s);
844 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
845 swf_ShapeSetCircle(tag, s, r,r,r,r);
846 swf_ShapeSetEnd(tag);
849 s_addcharacter(name, id, tag, rect);
853 void s_textshape(char*name, char*fontname, char*_text)
856 U8*text = (U8*)_text;
860 font = dictionary_lookup(&fonts, fontname);
862 syntaxerror("font \"%s\" not known!", fontname);
864 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
865 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
866 s_box(name, 0, 0, black, 20, 0);
869 g = font->ascii2glyph[text[0]];
871 outline = malloc(sizeof(outline_t));
872 memset(outline, 0, sizeof(outline_t));
873 outline->shape = font->glyph[g].shape;
874 outline->bbox = font->layout->bounds[g];
878 swf_Shape11DrawerInit(&draw, 0);
879 swf_DrawText(&draw, font, _text);
881 outline->shape = swf_ShapeDrawerToShape(&draw);
882 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
886 if(dictionary_lookup(&outlines, name))
887 syntaxerror("outline %s defined twice", name);
888 dictionary_put2(&outlines, name, outline);
891 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
896 font = dictionary_lookup(&fonts, fontname);
898 syntaxerror("font \"%s\" not known!", fontname);
900 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
902 if(!font->numchars) {
903 s_box(name, 0, 0, black, 20, 0);
906 r = swf_SetDefineText(tag, font, &color, text, size);
908 s_addcharacter(name, id, tag, r);
912 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
915 EditTextLayout layout;
918 font = dictionary_lookup(&fonts, fontname);
920 syntaxerror("font \"%s\" not known!", fontname);
921 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
924 layout.leftmargin = 0;
925 layout.rightmargin = 0;
932 swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
934 s_addcharacter(name, id, tag, r);
938 /* type: either "jpeg" or "png"
940 void s_image(char*name, char*type, char*filename, int quality)
942 /* an image is actually two folded: 1st bitmap, 2nd character.
943 Both of them can be used separately */
945 /* step 1: the bitmap */
950 warning("image type \"png\" not supported yet!");
951 s_box(name, 0, 0, black, 20, 0);
956 warning("no jpeg support compiled in");
957 s_box(name, 0, 0, black, 20, 0);
960 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
961 swf_SetU16(tag, imageID);
963 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
964 syntaxerror("Image \"%s\" not found, or contains errors", filename);
967 swf_GetJPEGSize(filename, &width, &height);
974 s_addimage(name, id, tag, r);
979 /* step 2: the character */
980 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
982 swf_ShapeSetBitmapRect(tag, imageID, width, height);
984 s_addcharacter(name, id, tag, r);
988 void dumpSWF(SWF*swf)
990 TAG* tag = swf->firstTag;
991 printf("vvvvvvvvvvvvvvvvvvvvv\n");
993 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
996 printf("^^^^^^^^^^^^^^^^^^^^^\n");
999 void s_font(char*name, char*filename)
1002 font = swf_LoadFont(filename);
1005 warning("Couldn't open font file \"%s\"", filename);
1006 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1007 memset(font, 0, sizeof(SWFFONT));
1008 dictionary_put2(&fonts, name, font);
1014 /* fix the layout. Only needed for old fonts */
1016 for(t=0;t<font->numchars;t++) {
1017 font->glyph[t].advance = 0;
1020 swf_FontCreateLayout(font);
1024 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1025 swf_FontSetDefine2(tag, font);
1028 if(dictionary_lookup(&fonts, name))
1029 syntaxerror("font %s defined twice", name);
1030 dictionary_put2(&fonts, name, font);
1035 typedef struct _sound_t
1041 void s_sound(char*name, char*filename)
1043 struct WAV wav, wav2;
1048 if(!readWAV(filename, &wav)) {
1049 warning("Couldn't read wav file \"%s\"", filename);
1053 convertWAV2mono(&wav, &wav2, 44100);
1054 samples = (U16*)wav2.data;
1055 numsamples = wav2.size/2;
1059 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1060 swf_SetU16(tag, id); //id
1061 swf_SetSoundDefine(tag, samples, numsamples);
1063 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1067 if(dictionary_lookup(&sounds, name))
1068 syntaxerror("sound %s defined twice", name);
1069 dictionary_put2(&sounds, name, sound);
1077 static char* gradient_getToken(const char**p)
1081 while(**p && strchr(" \t\n\r", **p)) {
1085 while(**p && !strchr(" \t\n\r", **p)) {
1088 result = malloc((*p)-start+1);
1089 memcpy(result,start,(*p)-start+1);
1090 result[(*p)-start] = 0;
1094 float parsePercent(char*str);
1095 RGBA parseColor(char*str);
1097 GRADIENT parseGradient(const char*str)
1100 const char* p = str;
1101 memset(&gradient, 0, sizeof(GRADIENT));
1103 char*posstr,*colorstr;
1106 posstr = gradient_getToken(&p);
1109 pos = parsePercent(posstr);
1110 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1111 colorstr = gradient_getToken(&p);
1112 color = parseColor(colorstr);
1113 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1114 warning("gradient record too big- max size is 8, rest ignored");
1117 gradient.ratios[gradient.num] = (int)(pos*255.0);
1118 gradient.rgba[gradient.num] = color;
1126 void s_gradient(char*name, const char*text, int radial)
1128 gradient_t* gradient;
1129 gradient = malloc(sizeof(gradient_t));
1130 memset(gradient, 0, sizeof(gradient_t));
1131 gradient->gradient = parseGradient(text);
1132 gradient->radial = radial;
1134 if(dictionary_lookup(&gradients, name))
1135 syntaxerror("gradient %s defined twice", name);
1136 dictionary_put2(&gradients, name, gradient);
1139 void s_action(const char*text)
1142 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1144 syntaxerror("Couldn't compile ActionScript");
1147 tag = swf_InsertTag(tag, ST_DOACTION);
1149 swf_ActionSet(tag, a);
1154 int s_swf3action(char*name, char*action)
1157 instance_t* object = dictionary_lookup(&instances, name);
1161 a = action_SetTarget(0, name);
1162 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1163 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1164 else if(!strcmp(action, "stop")) a = action_Stop(a);
1165 else if(!strcmp(action, "play")) a = action_Play(a);
1166 a = action_SetTarget(a, "");
1169 tag = swf_InsertTag(tag, ST_DOACTION);
1170 swf_ActionSet(tag, a);
1175 void s_outline(char*name, char*format, char*source)
1184 swf_Shape11DrawerInit(&draw, 0);
1185 draw_string(&draw, source);
1187 shape = swf_ShapeDrawerToShape(&draw);
1188 //shape2 = swf_ShapeToShape2(shape);
1189 //bounds = swf_GetShapeBoundingBox(shape2);
1190 //swf_Shape2Free(shape2);
1191 bounds = swf_ShapeDrawerGetBBox(&draw);
1192 draw.dealloc(&draw);
1194 outline = (outline_t*)malloc(sizeof(outline_t));
1195 memset(outline, 0, sizeof(outline_t));
1196 outline->shape = shape;
1197 outline->bbox = bounds;
1199 if(dictionary_lookup(&outlines, name))
1200 syntaxerror("outline %s defined twice", name);
1201 dictionary_put2(&outlines, name, outline);
1204 int s_playsound(char*name, int loops, int nomultiple, int stop)
1206 sound_t* sound = dictionary_lookup(&sounds, name);
1211 tag = swf_InsertTag(tag, ST_STARTSOUND);
1212 swf_SetU16(tag, sound->id); //id
1213 memset(&info, 0, sizeof(info));
1216 info.nomultiple = nomultiple;
1217 swf_SetSoundInfo(tag, &info);
1221 void s_includeswf(char*name, char*filename)
1229 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1230 f = open(filename,O_RDONLY);
1232 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1233 s_box(name, 0, 0, black, 20, 0);
1236 if (swf_ReadSWF(f,&swf)<0) {
1237 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1238 s_box(name, 0, 0, black, 20, 0);
1243 /* FIXME: The following sets the bounding Box for the character.
1244 It is wrong for two reasons:
1245 a) It may be too small (in case objects in the movie clip at the borders)
1246 b) it may be too big (because the poor movie never got autocropped)
1250 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1251 swf_SetU16(tag, id);
1254 swf_Relocate(&swf, idmap);
1256 ftag = swf.firstTag;
1260 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1261 if(cutout[t] == ftag->id) {
1265 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1267 if(ftag->id == ST_END)
1271 /* We simply dump all tags right after the sprite
1272 header, relying on the fact that swf_OptimizeTagOrder() will
1273 sort things out for us later.
1274 We also rely on the fact that the imported SWF is well-formed.
1276 tag = swf_InsertTag(tag, ftag->id);
1277 swf_SetBlock(tag, ftag->data, ftag->len);
1281 syntaxerror("Included file %s contains errors", filename);
1282 tag = swf_InsertTag(tag, ST_END);
1286 s_addcharacter(name, id, tag, r);
1289 SRECT s_getCharBBox(char*name)
1291 character_t* c = dictionary_lookup(&characters, name);
1292 if(!c) syntaxerror("character '%s' unknown(2)", name);
1295 SRECT s_getInstanceBBox(char*name)
1297 instance_t * i = dictionary_lookup(&instances, name);
1299 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1301 if(!c) syntaxerror("internal error(5)");
1304 parameters_t s_getParameters(char*name)
1306 instance_t * i = dictionary_lookup(&instances, name);
1307 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1308 return i->parameters;
1310 void s_startclip(char*instance, char*character, parameters_t p)
1312 character_t* c = dictionary_lookup(&characters, character);
1316 syntaxerror("character %s not known", character);
1318 i = s_addinstance(instance, c, currentdepth);
1320 m = s_instancepos(i->character->size, &p);
1322 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1323 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1324 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1326 i->lastFrame= currentframe;
1328 stack[stackpos].tag = tag;
1329 stack[stackpos].type = 2;
1338 swf_SetTagPos(stack[stackpos].tag, 0);
1339 swf_GetPlaceObject(stack[stackpos].tag, &p);
1340 p.clipdepth = currentdepth;
1341 swf_ClearTag(stack[stackpos].tag);
1342 swf_SetPlaceObject(stack[stackpos].tag, &p);
1346 void s_put(char*instance, char*character, parameters_t p)
1348 character_t* c = dictionary_lookup(&characters, character);
1352 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1355 i = s_addinstance(instance, c, currentdepth);
1357 m = s_instancepos(i->character->size, &p);
1359 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1360 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1362 i->lastFrame = currentframe;
1366 void s_jump(char*instance, parameters_t p)
1368 instance_t* i = dictionary_lookup(&instances, instance);
1371 syntaxerror("instance %s not known", instance);
1375 m = s_instancepos(i->character->size, &p);
1377 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1378 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1380 i->lastFrame = currentframe;
1383 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1387 if(num==0 || num==1)
1389 ratio = (float)pos/(float)num;
1391 p.x = (p2->x-p1->x)*ratio + p1->x;
1392 p.y = (p2->y-p1->y)*ratio + p1->y;
1393 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1394 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1395 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1396 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1398 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1399 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1400 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1401 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1403 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1404 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1405 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1406 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1408 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1409 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1410 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1411 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1415 void s_change(char*instance, parameters_t p2)
1417 instance_t* i = dictionary_lookup(&instances, instance);
1421 int frame, allframes;
1423 syntaxerror("instance %s not known", instance);
1427 allframes = currentframe - i->lastFrame - 1;
1429 warning(".change ignored. can only .put/.change an object once per frame.");
1433 m = s_instancepos(i->character->size, &p2);
1434 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1435 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1438 /* o.k., we got the start and end point set. Now iterate though all the
1439 tags in between, inserting object changes after each new frame */
1442 if(!t) syntaxerror("internal error(6)");
1444 while(frame < allframes) {
1445 if(t->id == ST_SHOWFRAME) {
1450 p = s_interpolate(&p1, &p2, frame, allframes);
1451 m = s_instancepos(i->character->size, &p); //needed?
1452 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1453 i->lastFrame = currentframe;
1454 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1456 if(frame == allframes)
1461 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1465 void s_delinstance(char*instance)
1467 instance_t* i = dictionary_lookup(&instances, instance);
1469 syntaxerror("instance %s not known", instance);
1471 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1472 swf_SetU16(tag, i->depth);
1473 dictionary_del(&instances, instance);
1476 void s_qchange(char*instance, parameters_t p)
1483 syntaxerror(".end unexpected");
1484 if(stack[stackpos-1].type == 0)
1486 else if(stack[stackpos-1].type == 1)
1488 else if(stack[stackpos-1].type == 2)
1490 else if(stack[stackpos-1].type == 3)
1492 else syntaxerror("internal error 1");
1495 // ------------------------------------------------------------------------
1497 typedef int command_func_t(map_t*args);
1499 SRECT parseBox(char*str)
1502 float xmin, xmax, ymin, ymax;
1503 char*x = strchr(str, 'x');
1505 if(!strcmp(str, "autocrop")) {
1506 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1510 d1 = strchr(x+1, ':');
1512 d2 = strchr(d1+1, ':');
1514 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1518 else if(d1 && !d2) {
1519 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1525 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1530 r.xmin = (SCOORD)(xmin*20);
1531 r.ymin = (SCOORD)(ymin*20);
1532 r.xmax = (SCOORD)(xmax*20);
1533 r.ymax = (SCOORD)(ymax*20);
1536 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1539 float parseFloat(char*str)
1543 int parseInt(char*str)
1548 if(str[0]=='+' || str[0]=='-')
1552 if(str[t]<'0' || str[t]>'9')
1553 syntaxerror("Not an Integer: \"%s\"", str);
1556 int parseTwip(char*str)
1560 if(str[0]=='+' || str[0]=='-') {
1565 dot = strchr(str, '.');
1569 return sign*parseInt(str)*20;
1571 int l=strlen(++dot);
1573 for(s=str;s<dot-1;s++)
1574 if(*s<'0' || *s>'9')
1575 syntaxerror("Not a coordinate: \"%s\"", str);
1577 if(*s<'0' || *s>'9')
1578 syntaxerror("Not a coordinate: \"%s\"", str);
1580 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1581 warning("precision loss: %s converted to twip", str);
1586 return sign*atoi(str)*20;
1588 return sign*atoi(str)*20+atoi(dot)*2;
1590 return sign*atoi(str)*20+atoi(dot)/5;
1595 int isPoint(char*str)
1597 if(strchr(str, '('))
1603 SPOINT parsePoint(char*str)
1607 int l = strlen(str);
1608 char*comma = strchr(str, ',');
1609 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1610 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1611 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1612 p.x = parseTwip(tmp);
1613 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1614 p.y = parseTwip(tmp);
1618 int parseColor2(char*str, RGBA*color)
1620 int l = strlen(str);
1624 struct {unsigned char r,g,b;char*name;} colors[] =
1625 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1626 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1627 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1628 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1629 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1630 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1631 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1632 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1633 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1634 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1635 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1636 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1640 if(str[0]=='#' && (l==7 || l==9)) {
1641 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1643 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1645 color->r = r; color->g = g; color->b = b; color->a = a;
1648 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1649 if(!strcmp(str, colors[t].name)) {
1654 color->r = r; color->g = g; color->b = b; color->a = a;
1660 RGBA parseColor(char*str)
1663 if(!parseColor2(str, &c))
1664 syntaxerror("Expression '%s' is not a color", str);
1668 typedef struct _muladd {
1673 MULADD parseMulAdd(char*str)
1676 char* str2 = (char*)malloc(strlen(str)+5);
1683 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1684 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1685 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1686 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1687 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1688 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1689 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1690 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1691 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1692 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1694 syntaxerror("'%s' is not a valid color transform expression", str);
1696 m.add = (int)(add*256);
1697 m.mul = (int)(mul*256);
1702 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1704 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1705 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1707 if(a<-32768) a=-32768;
1708 if(a>32767) a=32767;
1709 if(m<-32768) m=-32768;
1710 if(m>32767) m=32767;
1716 float parsePercent(char*str)
1718 int l = strlen(str);
1722 return atoi(str)/100.0;
1724 syntaxerror("Expression '%s' is not a percentage", str);
1727 int isPercent(char*str)
1729 return str[strlen(str)-1]=='%';
1731 int parseNewSize(char*str, int size)
1734 return parsePercent(str)*size;
1736 return (int)(atof(str)*20);
1739 int isColor(char*str)
1742 return parseColor2(str, &c);
1745 static char* lu(map_t* args, char*name)
1747 char* value = map_lookup(args, name);
1749 map_dump(args, stdout, "");
1750 syntaxerror("internal error 2: value %s should be set", name);
1755 static int c_flash(map_t*args)
1757 char* name = lu(args, "name");
1758 char* compressstr = lu(args, "compress");
1759 SRECT bbox = parseBox(lu(args, "bbox"));
1760 int version = parseInt(lu(args, "version"));
1761 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1763 RGBA color = parseColor(lu(args, "background"));
1764 if(!strcmp(name, "!default!") || override_outputname)
1767 if(!strcmp(compressstr, "default"))
1768 compress = version==6;
1769 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1771 else if(!strcmp(compressstr, "no"))
1773 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1775 s_swf(name, bbox, version, fps, compress, color);
1778 int isRelative(char*str)
1780 return !strncmp(str, "<plus>", 6) ||
1781 !strncmp(str, "<minus>", 7);
1783 char* getOffset(char*str)
1785 if(!strncmp(str, "<plus>", 6))
1787 if(!strncmp(str, "<minus>", 7))
1789 syntaxerror("internal error (347)");
1792 int getSign(char*str)
1794 if(!strncmp(str, "<plus>", 6))
1796 if(!strncmp(str, "<minus>", 7))
1798 syntaxerror("internal error (348)");
1801 static dictionary_t points;
1802 static mem_t mpoints;
1803 int points_initialized = 0;
1805 SPOINT getPoint(SRECT r, char*name)
1808 if(!strcmp(name, "center")) {
1810 p.x = (r.xmin + r.xmax)/2;
1811 p.y = (r.ymin + r.ymax)/2;
1815 if(points_initialized)
1816 l = (int)dictionary_lookup(&points, name);
1818 syntaxerror("Invalid point: \"%s\".", name);
1821 return *(SPOINT*)&mpoints.buffer[l];
1823 static int c_gradient(map_t*args)
1825 char*name = lu(args, "name");
1826 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1830 syntaxerror("colon (:) expected");
1832 s_gradient(name, text, radial);
1835 static int c_point(map_t*args)
1837 char*name = lu(args, "name");
1841 if(!points_initialized) {
1842 dictionary_init(&points);
1844 points_initialized = 1;
1846 p.x = parseTwip(lu(args, "x"));
1847 p.y = parseTwip(lu(args, "y"));
1848 pos = mem_put(&mpoints, &p, sizeof(p));
1849 string_set(&s1, name);
1851 dictionary_put(&points, s1, (void*)pos);
1854 static int c_play(map_t*args)
1856 char*name = lu(args, "name");
1857 char*loop = lu(args, "loop");
1858 char*nomultiple = lu(args, "nomultiple");
1860 if(!strcmp(nomultiple, "nomultiple"))
1863 nm = parseInt(nomultiple);
1865 if(s_playsound(name, parseInt(loop), nm, 0)) {
1867 } else if(s_swf3action(name, "play")) {
1873 static int c_stop(map_t*args)
1875 char*name = lu(args, "name");
1877 if(s_playsound(name, 0,0,1)) {
1879 } else if(s_swf3action(name, "stop")) {
1882 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1886 static int c_nextframe(map_t*args)
1888 char*name = lu(args, "name");
1890 if(s_swf3action(name, "nextframe")) {
1893 syntaxerror("I don't know anything about movie \"%s\"", name);
1897 static int c_previousframe(map_t*args)
1899 char*name = lu(args, "name");
1901 if(s_swf3action(name, "previousframe")) {
1904 syntaxerror("I don't know anything about movie \"%s\"", name);
1908 static int c_placement(map_t*args, int type)
1910 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1913 char* luminancestr = lu(args, "luminance");
1914 char* scalestr = lu(args, "scale");
1915 char* scalexstr = lu(args, "scalex");
1916 char* scaleystr = lu(args, "scaley");
1917 char* rotatestr = lu(args, "rotate");
1918 char* shearstr = lu(args, "shear");
1919 char* xstr="", *pivotstr="";
1920 char* ystr="", *anglestr="";
1921 char*above = lu(args, "above"); /*FIXME*/
1922 char*below = lu(args, "below");
1923 char* rstr = lu(args, "red");
1924 char* gstr = lu(args, "green");
1925 char* bstr = lu(args, "blue");
1926 char* astr = lu(args, "alpha");
1927 char* pinstr = lu(args, "pin");
1928 char* as = map_lookup(args, "as");
1936 if(type==9) { // (?) .rotate or .arcchange
1937 pivotstr = lu(args, "pivot");
1938 anglestr = lu(args, "angle");
1940 xstr = lu(args, "x");
1941 ystr = lu(args, "y");
1944 luminance = parseMulAdd(luminancestr);
1947 luminance.mul = 256;
1951 if(scalexstr[0]||scaleystr[0])
1952 syntaxerror("scalex/scaley and scale cannot both be set");
1953 scalexstr = scaleystr = scalestr;
1956 if(type == 0 || type == 4) {
1958 character = lu(args, "character");
1959 parameters_clear(&p);
1960 } else if (type == 5) {
1961 character = lu(args, "name");
1962 parameters_clear(&p);
1965 p = s_getParameters(instance);
1970 if(isRelative(xstr)) {
1971 if(type == 0 || type == 4)
1972 syntaxerror("relative x values not allowed for initial put or startclip");
1973 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1975 p.x = parseTwip(xstr);
1979 if(isRelative(ystr)) {
1980 if(type == 0 || type == 4)
1981 syntaxerror("relative y values not allowed for initial put or startclip");
1982 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1984 p.y = parseTwip(ystr);
1988 /* scale, scalex, scaley */
1990 oldbbox = s_getCharBBox(character);
1992 oldbbox = s_getInstanceBBox(instance);
1994 oldwidth = oldbbox.xmax - oldbbox.xmin;
1995 oldheight = oldbbox.ymax - oldbbox.ymin;
1997 if(oldwidth==0) p.scalex = 1.0;
2000 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2004 if(oldheight==0) p.scaley = 1.0;
2007 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2013 if(isRelative(rotatestr)) {
2014 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2016 p.rotate = parseFloat(rotatestr);
2022 if(isRelative(shearstr)) {
2023 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2025 p.shear = parseFloat(shearstr);
2030 if(isPoint(pivotstr))
2031 p.pivot = parsePoint(pivotstr);
2033 p.pivot = getPoint(oldbbox, pivotstr);
2037 p.pin = parsePoint(pinstr);
2039 p.pin = getPoint(oldbbox, pinstr);
2042 /* color transform */
2044 if(rstr[0] || luminancestr[0]) {
2047 r = parseMulAdd(rstr);
2049 r.add = p.cxform.r0;
2050 r.mul = p.cxform.r1;
2052 r = mergeMulAdd(r, luminance);
2053 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2055 if(gstr[0] || luminancestr[0]) {
2058 g = parseMulAdd(gstr);
2060 g.add = p.cxform.g0;
2061 g.mul = p.cxform.g1;
2063 g = mergeMulAdd(g, luminance);
2064 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2066 if(bstr[0] || luminancestr[0]) {
2069 b = parseMulAdd(bstr);
2071 b.add = p.cxform.b0;
2072 b.mul = p.cxform.b1;
2074 b = mergeMulAdd(b, luminance);
2075 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2078 MULADD a = parseMulAdd(astr);
2079 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2083 s_put(instance, character, p);
2085 s_change(instance, p);
2087 s_qchange(instance, p);
2089 s_jump(instance, p);
2091 s_startclip(instance, character, p);
2092 else if(type == 5) {
2094 s_buttonput(character, as, p);
2096 s_buttonput(character, "shape", p);
2101 static int c_put(map_t*args)
2103 c_placement(args, 0);
2106 static int c_change(map_t*args)
2108 c_placement(args, 1);
2111 static int c_qchange(map_t*args)
2113 c_placement(args, 2);
2116 static int c_arcchange(map_t*args)
2118 c_placement(args, 2);
2121 static int c_jump(map_t*args)
2123 c_placement(args, 3);
2126 static int c_startclip(map_t*args)
2128 c_placement(args, 4);
2131 static int c_show(map_t*args)
2133 c_placement(args, 5);
2136 static int c_del(map_t*args)
2138 char*instance = lu(args, "name");
2139 s_delinstance(instance);
2142 static int c_end(map_t*args)
2147 static int c_sprite(map_t*args)
2149 char* name = lu(args, "name");
2153 static int c_frame(map_t*args)
2155 char*framestr = lu(args, "n");
2156 char*cutstr = lu(args, "cut");
2159 if(strcmp(cutstr, "no"))
2161 if(isRelative(framestr)) {
2162 frame = s_getframe();
2163 if(getSign(framestr)<0)
2164 syntaxerror("relative frame expressions must be positive");
2165 frame += parseInt(getOffset(framestr));
2168 frame = parseInt(framestr);
2169 if(s_getframe() >= frame
2170 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2171 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2173 s_frame(frame, cut);
2176 static int c_primitive(map_t*args)
2178 char*name = lu(args, "name");
2179 char*command = lu(args, "commandname");
2180 int width=0, height=0, r=0;
2181 int linewidth = parseTwip(lu(args, "line"));
2182 char*colorstr = lu(args, "color");
2183 RGBA color = parseColor(colorstr);
2184 char*fillstr = lu(args, "fill");
2191 if(!strcmp(command, "circle"))
2193 else if(!strcmp(command, "filled"))
2197 width = parseTwip(lu(args, "width"));
2198 height = parseTwip(lu(args, "height"));
2199 } else if (type==1) {
2200 r = parseTwip(lu(args, "r"));
2201 } else if (type==2) {
2202 outline = lu(args, "outline");
2205 if(!strcmp(fillstr, "fill"))
2207 if(!strcmp(fillstr, "none"))
2209 if(width<0 || height<0 || linewidth<0 || r<0)
2210 syntaxerror("values width, height, line, r must be positive");
2212 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2213 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2214 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2218 static int c_textshape(map_t*args)
2220 char*name = lu(args, "name");
2221 char*text = lu(args, "text");
2222 char*font = lu(args, "font");
2224 s_textshape(name, font, text);
2228 static int c_swf(map_t*args)
2230 char*name = lu(args, "name");
2231 char*filename = lu(args, "filename");
2232 char*command = lu(args, "commandname");
2233 if(!strcmp(command, "shape"))
2234 warning("Please use .swf instead of .shape");
2235 s_includeswf(name, filename);
2239 static int c_font(map_t*args)
2241 char*name = lu(args, "name");
2242 char*filename = lu(args, "filename");
2243 s_font(name, filename);
2247 static int c_sound(map_t*args)
2249 char*name = lu(args, "name");
2250 char*filename = lu(args, "filename");
2251 s_sound(name, filename);
2255 static int c_text(map_t*args)
2257 char*name = lu(args, "name");
2258 char*text = lu(args, "text");
2259 char*font = lu(args, "font");
2260 float size = parsePercent(lu(args, "size"));
2261 RGBA color = parseColor(lu(args, "color"));
2262 s_text(name, font, text, (int)(size*100), color);
2266 static int c_soundtrack(map_t*args)
2271 static int c_image(map_t*args)
2273 char*command = lu(args, "commandname");
2274 char*name = lu(args, "name");
2275 char*filename = lu(args, "filename");
2276 if(!strcmp(command,"jpeg")) {
2277 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2278 s_image(name, "jpeg", filename, quality);
2280 s_image(name, "png", filename, 0);
2285 static int c_outline(map_t*args)
2287 char*name = lu(args, "name");
2288 char*format = lu(args, "format");
2292 syntaxerror("colon (:) expected");
2294 s_outline(name, format, text);
2298 int fakechar(map_t*args)
2300 char*name = lu(args, "name");
2301 s_box(name, 0, 0, black, 20, 0);
2305 static int c_egon(map_t*args) {return fakechar(args);}
2306 static int c_button(map_t*args) {
2307 char*name = lu(args, "name");
2311 static int current_button_flags = 0;
2312 static int c_on_press(map_t*args)
2314 char*position = lu(args, "position");
2316 if(!strcmp(position, "inside")) {
2317 current_button_flags |= BC_OVERUP_OVERDOWN;
2318 } else if(!strcmp(position, "outside")) {
2319 //current_button_flags |= BC_IDLE_OUTDOWN;
2320 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2321 } else if(!strcmp(position, "anywhere")) {
2322 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2325 if(type == RAWDATA) {
2327 s_buttonaction(current_button_flags, action);
2328 current_button_flags = 0;
2334 static int c_on_release(map_t*args)
2336 char*position = lu(args, "position");
2338 if(!strcmp(position, "inside")) {
2339 current_button_flags |= BC_OVERDOWN_OVERUP;
2340 } else if(!strcmp(position, "outside")) {
2341 current_button_flags |= BC_OUTDOWN_IDLE;
2342 } else if(!strcmp(position, "anywhere")) {
2343 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2346 if(type == RAWDATA) {
2348 s_buttonaction(current_button_flags, action);
2349 current_button_flags = 0;
2355 static int c_on_move_in(map_t*args)
2357 char*position = lu(args, "state");
2359 if(!strcmp(position, "pressed")) {
2360 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2361 } else if(!strcmp(position, "not_pressed")) {
2362 current_button_flags |= BC_IDLE_OVERUP;
2363 } else if(!strcmp(position, "any")) {
2364 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2367 if(type == RAWDATA) {
2369 s_buttonaction(current_button_flags, action);
2370 current_button_flags = 0;
2376 static int c_on_move_out(map_t*args)
2378 char*position = lu(args, "state");
2380 if(!strcmp(position, "pressed")) {
2381 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2382 } else if(!strcmp(position, "not_pressed")) {
2383 current_button_flags |= BC_OVERUP_IDLE;
2384 } else if(!strcmp(position, "any")) {
2385 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2388 if(type == RAWDATA) {
2390 s_buttonaction(current_button_flags, action);
2391 current_button_flags = 0;
2397 static int c_on_key(map_t*args)
2399 char*key = lu(args, "key");
2401 if(strlen(key)==1) {
2404 current_button_flags |= 0x4000 + (key[0]*0x200);
2406 syntaxerror("invalid character: %c"+key[0]);
2411 <ctrl-x> = 0x200*(x-'a')
2415 syntaxerror("invalid key: %s",key);
2418 if(type == RAWDATA) {
2420 s_buttonaction(current_button_flags, action);
2421 current_button_flags = 0;
2428 static int c_edittext(map_t*args)
2430 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2431 char*name = lu(args, "name");
2432 char*font = lu(args, "font");
2433 int size = (int)(100*20*parsePercent(lu(args, "size")));
2434 int width = parseTwip(lu(args, "width"));
2435 int height = parseTwip(lu(args, "height"));
2436 char*text = lu(args, "text");
2437 RGBA color = parseColor(lu(args, "color"));
2438 int maxlength = parseInt(lu(args, "maxlength"));
2439 char*variable = lu(args, "variable");
2440 char*passwordstr = lu(args, "password");
2441 char*wordwrapstr = lu(args, "wordwrap");
2442 char*multilinestr = lu(args, "multiline");
2443 char*htmlstr = lu(args, "html");
2444 char*noselectstr = lu(args, "noselect");
2445 char*readonlystr = lu(args, "readonly");
2448 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2449 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2450 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2451 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2452 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2453 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
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 text font"},
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"},
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);