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 {
237 static void character_init(character_t*c)
239 memset(c, 0, sizeof(character_t));
241 static character_t* character_new()
244 c = (character_t*)malloc(sizeof(character_t));
248 static void instance_init(instance_t*i)
250 memset(i, 0, sizeof(instance_t));
252 static instance_t* instance_new()
255 c = (instance_t*)malloc(sizeof(instance_t));
260 static void incrementid()
264 syntaxerror("Out of character ids.");
269 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
271 character_t* c = character_new();
273 c->definingTag = ctag;
276 if(dictionary_lookup(&characters, name))
277 syntaxerror("character %s defined twice", name);
278 dictionary_put2(&characters, name, c);
280 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
282 swf_SetString(tag, name);
283 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
286 swf_SetString(tag, name);
288 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
290 character_t* c = character_new();
291 c->definingTag = ctag;
295 if(dictionary_lookup(&images, name))
296 syntaxerror("image %s defined twice", name);
297 dictionary_put2(&images, name, c);
299 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
301 instance_t* i = instance_new();
304 //swf_GetMatrix(0, &i->matrix);
305 if(dictionary_lookup(&instances, name))
306 syntaxerror("object %s defined twice", name);
307 dictionary_put2(&instances, name, i);
311 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)
314 p->scalex = scalex; p->scaley = scaley;
315 p->pin = pin; p->pivot = pivot;
316 p->rotate = rotate; p->cxform = cxform;
320 static void parameters_clear(parameters_t*p)
323 p->scalex = 1.0; p->scaley = 1.0;
326 p->pivot.x = 0; p->pivot.y = 0;
329 swf_GetCXForm(0, &p->cxform, 1);
332 static void makeMatrix(MATRIX*m, parameters_t*p)
341 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
342 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
343 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
344 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
346 m->sx = (int)(sx*65536+0.5);
347 m->r1 = (int)(r1*65536+0.5);
348 m->r0 = (int)(r0*65536+0.5);
349 m->sy = (int)(sy*65536+0.5);
353 h = swf_TurnPoint(p->pin, m);
358 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
363 r = swf_TurnRect(rect, &m);
364 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
365 currentrect.xmax == 0 && currentrect.ymax == 0)
368 swf_ExpandRect2(¤trect, &r);
372 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
374 SWF*swf = (SWF*)malloc(sizeof(SWF));
377 syntaxerror(".swf blocks can't be nested");
379 memset(swf, 0, sizeof(swf));
380 swf->fileVersion = version;
382 swf->frameRate = fps;
383 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
384 swf->compressed = compress;
385 swf_SetRGB(tag,&background);
387 if(stackpos==sizeof(stack)/sizeof(stack[0]))
388 syntaxerror("too many levels of recursion");
390 dictionary_init(&characters);
391 dictionary_init(&images);
392 dictionary_init(&outlines);
393 dictionary_init(&gradients);
394 dictionary_init(&instances);
395 dictionary_init(&fonts);
396 dictionary_init(&sounds);
398 memset(&stack[stackpos], 0, sizeof(stack[0]));
399 stack[stackpos].type = 0;
400 stack[stackpos].filename = strdup(name);
401 stack[stackpos].swf = swf;
402 stack[stackpos].oldframe = -1;
407 memset(¤trect, 0, sizeof(currentrect));
410 memset(idmap, 0, sizeof(idmap));
414 void s_sprite(char*name)
416 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
417 swf_SetU16(tag, id); //id
418 swf_SetU16(tag, 0); //frames
420 memset(&stack[stackpos], 0, sizeof(stack[0]));
421 stack[stackpos].type = 1;
422 stack[stackpos].oldframe = currentframe;
423 stack[stackpos].olddepth = currentdepth;
424 stack[stackpos].oldrect = currentrect;
425 stack[stackpos].oldinstances = instances;
426 stack[stackpos].tag = tag;
427 stack[stackpos].id = id;
428 stack[stackpos].name = strdup(name);
430 /* FIXME: those four fields should be bundled together */
431 dictionary_init(&instances);
434 memset(¤trect, 0, sizeof(currentrect));
440 typedef struct _buttonrecord
448 typedef struct _button
452 buttonrecord_t records[4];
455 static button_t mybutton;
457 void s_button(char*name)
459 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
460 swf_SetU16(tag, id); //id
461 swf_ButtonSetFlags(tag, 0); //menu=no
463 memset(&mybutton, 0, sizeof(mybutton));
465 memset(&stack[stackpos], 0, sizeof(stack[0]));
466 stack[stackpos].type = 3;
467 stack[stackpos].tag = tag;
468 stack[stackpos].id = id;
469 stack[stackpos].name = strdup(name);
470 stack[stackpos].oldrect = currentrect;
471 memset(¤trect, 0, sizeof(currentrect));
476 void s_buttonput(char*character, char*as, parameters_t p)
478 character_t* c = dictionary_lookup(&characters, character);
483 if(!stackpos || (stack[stackpos-1].type != 3)) {
484 syntaxerror(".show may only appear in .button");
487 syntaxerror("character %s not known (in .shape %s)", character, character);
489 if(mybutton.endofshapes) {
490 syntaxerror("a .do may not precede a .show", character, character);
493 m = s_instancepos(c->size, &p);
501 if(*s==',' || *s==0) {
502 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
503 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
504 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
505 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
506 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
507 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
514 static void setbuttonrecords(TAG*tag)
516 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
517 if(!mybutton.endofshapes) {
520 if(!mybutton.records[3].set) {
521 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
525 if(mybutton.records[t].set) {
526 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
529 swf_SetU8(tag,0); // end of button records
530 mybutton.endofshapes = 1;
534 void s_buttonaction(int flags, char*action)
540 setbuttonrecords(stack[stackpos-1].tag);
542 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
544 syntaxerror("Couldn't compile ActionScript");
547 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
548 swf_ActionSet(stack[stackpos-1].tag, a);
549 mybutton.nr_actions++;
554 static void setactionend(TAG*tag)
556 if(!mybutton.nr_actions) {
557 /* no actions means we didn't have an actionoffset,
558 which means we can't signal the end of the
559 buttonaction records, so, *sigh*, we have
560 to insert a dummy record */
561 swf_SetU16(tag, 0); //offset
562 swf_SetU16(tag, 0); //condition
563 swf_SetU8(tag, 0); //action
567 static void s_endButton()
570 setbuttonrecords(stack[stackpos-1].tag);
571 setactionend(stack[stackpos-1].tag);
574 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
578 tag = stack[stackpos].tag;
579 currentrect = stack[stackpos].oldrect;
581 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
582 free(stack[stackpos].name);
585 TAG* removeFromTo(TAG*from, TAG*to)
587 TAG*save = from->prev;
589 TAG*next = from->next;
597 static void s_endSprite()
599 SRECT r = currentrect;
601 if(stack[stackpos].cut)
602 tag = removeFromTo(stack[stackpos].cut, tag);
606 /* TODO: before clearing, prepend "<spritename>." to names and
607 copy into old instances dict */
608 dictionary_clear(&instances);
610 currentframe = stack[stackpos].oldframe;
611 currentrect = stack[stackpos].oldrect;
612 currentdepth = stack[stackpos].olddepth;
613 instances = stack[stackpos].oldinstances;
615 tag = swf_InsertTag(tag, ST_END);
617 tag = stack[stackpos].tag;
620 syntaxerror("internal error(7)");
622 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
623 free(stack[stackpos].name);
626 static void s_endSWF()
632 if(stack[stackpos].cut)
633 tag = removeFromTo(stack[stackpos].cut, tag);
637 swf = stack[stackpos].swf;
638 filename = stack[stackpos].filename;
640 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
642 tag = swf_InsertTag(tag, ST_END);
644 swf_OptimizeTagOrder(swf);
646 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
647 swf->movieSize = currentrect; /* "autocrop" */
650 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
651 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
652 swf->movieSize.ymax += 20;
655 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
657 syntaxerror("couldn't create output file %s", filename);
660 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
662 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
666 dictionary_clear(&instances);
667 dictionary_clear(&characters);
668 dictionary_clear(&images);
669 dictionary_clear(&outlines);
670 dictionary_clear(&gradients);
671 dictionary_clear(&fonts);
672 dictionary_clear(&sounds);
682 if(stack[stackpos-1].type == 0)
683 syntaxerror("End of file encountered in .flash block");
684 if(stack[stackpos-1].type == 1)
685 syntaxerror("End of file encountered in .sprite block");
686 if(stack[stackpos-1].type == 2)
687 syntaxerror("End of file encountered in .clip block");
696 void s_frame(int nr, int cut, char*name)
701 for(t=currentframe;t<nr;t++) {
702 tag = swf_InsertTag(tag, ST_SHOWFRAME);
703 if(t==nr-1 && name && *name) {
704 tag = swf_InsertTag(tag, ST_FRAMELABEL);
705 swf_SetString(tag, name);
711 syntaxerror("Can't cut, frame empty");
713 stack[stackpos].cut = tag;
719 int parseColor2(char*str, RGBA*color);
721 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
726 if(texture[0] == '#') {
727 parseColor2(texture, &color);
728 return swf_ShapeAddSolidFillStyle(s, &color);
729 } else if((image = dictionary_lookup(&images, texture))) {
731 swf_GetMatrix(0, &m);
732 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
733 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
736 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
737 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
738 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
742 swf_GetMatrix(0, &rot);
743 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
744 csin = sin(-gradient->rotate*2*3.14159265358979/360);
746 rot.r1 = -csin*65536;
749 r2 = swf_TurnRect(*r, &rot);
750 swf_GetMatrix(0, &m);
751 m.sx = (r2.xmax - r2.xmin)*2*ccos;
752 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
753 m.r0 = (r2.ymax - r2.ymin)*2*csin;
754 m.sy = (r2.ymax - r2.ymin)*2*ccos;
755 m.tx = r->xmin + (r->xmax - r->xmin)/2;
756 m.ty = r->ymin + (r->ymax - r->ymin)/2;
757 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
758 } else if (parseColor2(texture, &color)) {
759 return swf_ShapeAddSolidFillStyle(s, &color);
761 syntaxerror("not a color/fillstyle: %s", texture);
766 RGBA black={r:0,g:0,b:0,a:0};
767 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
776 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
778 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
781 fs1 = addFillStyle(s, &r2, texture);
784 r.xmin = r2.xmin-linewidth-linewidth/2;
785 r.ymin = r2.ymin-linewidth-linewidth/2;
786 r.xmax = r2.xmax+linewidth+linewidth/2;
787 r.ymax = r2.ymax+linewidth+linewidth/2;
789 swf_SetShapeHeader(tag,s);
790 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
791 swf_ShapeSetLine(tag,s,width,0);
792 swf_ShapeSetLine(tag,s,0,height);
793 swf_ShapeSetLine(tag,s,-width,0);
794 swf_ShapeSetLine(tag,s,0,-height);
795 swf_ShapeSetEnd(tag);
798 s_addcharacter(name, id, tag, r);
802 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
808 outline = dictionary_lookup(&outlines, outlinename);
810 syntaxerror("outline %s not defined", outlinename);
814 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
816 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
818 fs1 = addFillStyle(s, &r2, texture);
820 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
822 rect.xmin = r2.xmin-linewidth-linewidth/2;
823 rect.ymin = r2.ymin-linewidth-linewidth/2;
824 rect.xmax = r2.xmax+linewidth+linewidth/2;
825 rect.ymax = r2.ymax+linewidth+linewidth/2;
827 swf_SetRect(tag,&rect);
828 swf_SetShapeStyles(tag, s);
829 swf_SetShapeBits(tag, outline->shape); //does not count bits!
830 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
833 s_addcharacter(name, id, tag, rect);
837 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
842 r2.xmin = r2.ymin = 0;
846 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
848 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
850 fs1 = addFillStyle(s, &r2, texture);
852 rect.xmin = r2.xmin-linewidth-linewidth/2;
853 rect.ymin = r2.ymin-linewidth-linewidth/2;
854 rect.xmax = r2.xmax+linewidth+linewidth/2;
855 rect.ymax = r2.ymax+linewidth+linewidth/2;
857 swf_SetRect(tag,&rect);
858 swf_SetShapeHeader(tag,s);
859 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
860 swf_ShapeSetCircle(tag, s, r,r,r,r);
861 swf_ShapeSetEnd(tag);
864 s_addcharacter(name, id, tag, rect);
868 void s_textshape(char*name, char*fontname, float size, char*_text)
871 U8*text = (U8*)_text;
875 font = dictionary_lookup(&fonts, fontname);
877 syntaxerror("font \"%s\" not known!", fontname);
879 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
880 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
881 s_box(name, 0, 0, black, 20, 0);
884 g = font->ascii2glyph[text[0]];
886 outline = malloc(sizeof(outline_t));
887 memset(outline, 0, sizeof(outline_t));
888 outline->shape = font->glyph[g].shape;
889 outline->bbox = font->layout->bounds[g];
893 swf_Shape11DrawerInit(&draw, 0);
894 swf_DrawText(&draw, font, (int)(size*100), _text);
896 outline->shape = swf_ShapeDrawerToShape(&draw);
897 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
901 if(dictionary_lookup(&outlines, name))
902 syntaxerror("outline %s defined twice", name);
903 dictionary_put2(&outlines, name, outline);
906 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
911 font = dictionary_lookup(&fonts, fontname);
913 syntaxerror("font \"%s\" not known!", fontname);
915 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
917 if(!font->numchars) {
918 s_box(name, 0, 0, black, 20, 0);
921 r = swf_SetDefineText(tag, font, &color, text, size);
923 s_addcharacter(name, id, tag, r);
927 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
930 EditTextLayout layout;
933 font = dictionary_lookup(&fonts, fontname);
935 syntaxerror("font \"%s\" not known!", fontname);
936 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
939 layout.leftmargin = 0;
940 layout.rightmargin = 0;
947 swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
949 s_addcharacter(name, id, tag, r);
953 /* type: either "jpeg" or "png"
955 void s_image(char*name, char*type, char*filename, int quality)
957 /* an image is actually two folded: 1st bitmap, 2nd character.
958 Both of them can be used separately */
960 /* step 1: the bitmap */
965 warning("image type \"png\" not supported yet!");
966 s_box(name, 0, 0, black, 20, 0);
971 warning("no jpeg support compiled in");
972 s_box(name, 0, 0, black, 20, 0);
975 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
976 swf_SetU16(tag, imageID);
978 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
979 syntaxerror("Image \"%s\" not found, or contains errors", filename);
982 swf_GetJPEGSize(filename, &width, &height);
989 s_addimage(name, id, tag, r);
994 /* step 2: the character */
995 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
997 swf_ShapeSetBitmapRect(tag, imageID, width, height);
999 s_addcharacter(name, id, tag, r);
1003 void dumpSWF(SWF*swf)
1005 TAG* tag = swf->firstTag;
1006 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1008 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1011 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1014 void s_font(char*name, char*filename)
1017 font = swf_LoadFont(filename);
1020 warning("Couldn't open font file \"%s\"", filename);
1021 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1022 memset(font, 0, sizeof(SWFFONT));
1023 dictionary_put2(&fonts, name, font);
1029 /* fix the layout. Only needed for old fonts */
1031 for(t=0;t<font->numchars;t++) {
1032 font->glyph[t].advance = 0;
1035 swf_FontCreateLayout(font);
1037 /* just in case this thing is used in .edittext later on */
1038 swf_FontPrepareForEditText(font);
1041 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1042 swf_FontSetDefine2(tag, font);
1045 if(dictionary_lookup(&fonts, name))
1046 syntaxerror("font %s defined twice", name);
1047 dictionary_put2(&fonts, name, font);
1052 typedef struct _sound_t
1058 void s_sound(char*name, char*filename)
1060 struct WAV wav, wav2;
1065 if(!readWAV(filename, &wav)) {
1066 warning("Couldn't read wav file \"%s\"", filename);
1070 convertWAV2mono(&wav, &wav2, 44100);
1071 samples = (U16*)wav2.data;
1072 numsamples = wav2.size/2;
1076 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1077 swf_SetU16(tag, id); //id
1078 swf_SetSoundDefine(tag, samples, numsamples);
1080 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1084 if(dictionary_lookup(&sounds, name))
1085 syntaxerror("sound %s defined twice", name);
1086 dictionary_put2(&sounds, name, sound);
1094 static char* gradient_getToken(const char**p)
1098 while(**p && strchr(" \t\n\r", **p)) {
1102 while(**p && !strchr(" \t\n\r", **p)) {
1105 result = malloc((*p)-start+1);
1106 memcpy(result,start,(*p)-start+1);
1107 result[(*p)-start] = 0;
1111 float parsePercent(char*str);
1112 RGBA parseColor(char*str);
1114 GRADIENT parseGradient(const char*str)
1117 const char* p = str;
1118 memset(&gradient, 0, sizeof(GRADIENT));
1120 char*posstr,*colorstr;
1123 posstr = gradient_getToken(&p);
1126 pos = parsePercent(posstr);
1127 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1128 colorstr = gradient_getToken(&p);
1129 color = parseColor(colorstr);
1130 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1131 warning("gradient record too big- max size is 8, rest ignored");
1134 gradient.ratios[gradient.num] = (int)(pos*255.0);
1135 gradient.rgba[gradient.num] = color;
1143 void s_gradient(char*name, const char*text, int radial, int rotate)
1145 gradient_t* gradient;
1146 gradient = malloc(sizeof(gradient_t));
1147 memset(gradient, 0, sizeof(gradient_t));
1148 gradient->gradient = parseGradient(text);
1149 gradient->radial = radial;
1150 gradient->rotate = rotate;
1152 if(dictionary_lookup(&gradients, name))
1153 syntaxerror("gradient %s defined twice", name);
1154 dictionary_put2(&gradients, name, gradient);
1157 void s_action(const char*text)
1160 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1162 syntaxerror("Couldn't compile ActionScript");
1165 tag = swf_InsertTag(tag, ST_DOACTION);
1167 swf_ActionSet(tag, a);
1172 int s_swf3action(char*name, char*action)
1175 instance_t* object = dictionary_lookup(&instances, name);
1179 a = action_SetTarget(0, name);
1180 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1181 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1182 else if(!strcmp(action, "stop")) a = action_Stop(a);
1183 else if(!strcmp(action, "play")) a = action_Play(a);
1184 a = action_SetTarget(a, "");
1187 tag = swf_InsertTag(tag, ST_DOACTION);
1188 swf_ActionSet(tag, a);
1193 void s_outline(char*name, char*format, char*source)
1202 swf_Shape11DrawerInit(&draw, 0);
1203 draw_string(&draw, source);
1205 shape = swf_ShapeDrawerToShape(&draw);
1206 //shape2 = swf_ShapeToShape2(shape);
1207 //bounds = swf_GetShapeBoundingBox(shape2);
1208 //swf_Shape2Free(shape2);
1209 bounds = swf_ShapeDrawerGetBBox(&draw);
1210 draw.dealloc(&draw);
1212 outline = (outline_t*)malloc(sizeof(outline_t));
1213 memset(outline, 0, sizeof(outline_t));
1214 outline->shape = shape;
1215 outline->bbox = bounds;
1217 if(dictionary_lookup(&outlines, name))
1218 syntaxerror("outline %s defined twice", name);
1219 dictionary_put2(&outlines, name, outline);
1222 int s_playsound(char*name, int loops, int nomultiple, int stop)
1224 sound_t* sound = dictionary_lookup(&sounds, name);
1229 tag = swf_InsertTag(tag, ST_STARTSOUND);
1230 swf_SetU16(tag, sound->id); //id
1231 memset(&info, 0, sizeof(info));
1234 info.nomultiple = nomultiple;
1235 swf_SetSoundInfo(tag, &info);
1239 void s_includeswf(char*name, char*filename)
1247 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1248 f = open(filename,O_RDONLY|O_BINARY);
1250 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1251 s_box(name, 0, 0, black, 20, 0);
1254 if (swf_ReadSWF(f,&swf)<0) {
1255 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1256 s_box(name, 0, 0, black, 20, 0);
1261 /* FIXME: The following sets the bounding Box for the character.
1262 It is wrong for two reasons:
1263 a) It may be too small (in case objects in the movie clip at the borders)
1264 b) it may be too big (because the poor movie never got autocropped)
1268 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1269 swf_SetU16(tag, id);
1272 swf_Relocate(&swf, idmap);
1274 ftag = swf.firstTag;
1278 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1279 if(cutout[t] == ftag->id) {
1283 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1285 if(ftag->id == ST_END)
1289 /* We simply dump all tags right after the sprite
1290 header, relying on the fact that swf_OptimizeTagOrder() will
1291 sort things out for us later.
1292 We also rely on the fact that the imported SWF is well-formed.
1294 tag = swf_InsertTag(tag, ftag->id);
1295 swf_SetBlock(tag, ftag->data, ftag->len);
1299 syntaxerror("Included file %s contains errors", filename);
1300 tag = swf_InsertTag(tag, ST_END);
1304 s_addcharacter(name, id, tag, r);
1307 SRECT s_getCharBBox(char*name)
1309 character_t* c = dictionary_lookup(&characters, name);
1310 if(!c) syntaxerror("character '%s' unknown(2)", name);
1313 SRECT s_getInstanceBBox(char*name)
1315 instance_t * i = dictionary_lookup(&instances, name);
1317 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1319 if(!c) syntaxerror("internal error(5)");
1322 parameters_t s_getParameters(char*name)
1324 instance_t * i = dictionary_lookup(&instances, name);
1325 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1326 return i->parameters;
1328 void s_startclip(char*instance, char*character, parameters_t p)
1330 character_t* c = dictionary_lookup(&characters, character);
1334 syntaxerror("character %s not known", character);
1336 i = s_addinstance(instance, c, currentdepth);
1338 m = s_instancepos(i->character->size, &p);
1340 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1341 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1342 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1344 i->lastFrame= currentframe;
1346 stack[stackpos].tag = tag;
1347 stack[stackpos].type = 2;
1356 swf_SetTagPos(stack[stackpos].tag, 0);
1357 swf_GetPlaceObject(stack[stackpos].tag, &p);
1358 p.clipdepth = currentdepth;
1360 swf_ClearTag(stack[stackpos].tag);
1361 swf_SetPlaceObject(stack[stackpos].tag, &p);
1365 void s_put(char*instance, char*character, parameters_t p)
1367 character_t* c = dictionary_lookup(&characters, character);
1371 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1374 i = s_addinstance(instance, c, currentdepth);
1376 m = s_instancepos(i->character->size, &p);
1378 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1379 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1381 i->lastFrame = currentframe;
1385 void s_jump(char*instance, parameters_t p)
1387 instance_t* i = dictionary_lookup(&instances, instance);
1390 syntaxerror("instance %s not known", instance);
1394 m = s_instancepos(i->character->size, &p);
1396 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1397 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1399 i->lastFrame = currentframe;
1402 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1406 if(num==0 || num==1)
1408 ratio = (float)pos/(float)num;
1410 p.x = (p2->x-p1->x)*ratio + p1->x;
1411 p.y = (p2->y-p1->y)*ratio + p1->y;
1412 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1413 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1414 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1415 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1417 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1418 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1419 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1420 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1422 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1423 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1424 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1425 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1427 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1428 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1429 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1430 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1434 void s_change(char*instance, parameters_t p2)
1436 instance_t* i = dictionary_lookup(&instances, instance);
1440 int frame, allframes;
1442 syntaxerror("instance %s not known", instance);
1446 allframes = currentframe - i->lastFrame - 1;
1448 warning(".change ignored. can only .put/.change an object once per frame.");
1452 m = s_instancepos(i->character->size, &p2);
1453 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1454 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1457 /* o.k., we got the start and end point set. Now iterate though all the
1458 tags in between, inserting object changes after each new frame */
1461 if(!t) syntaxerror("internal error(6)");
1463 while(frame < allframes) {
1464 if(t->id == ST_SHOWFRAME) {
1469 p = s_interpolate(&p1, &p2, frame, allframes);
1470 m = s_instancepos(i->character->size, &p); //needed?
1471 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1472 i->lastFrame = currentframe;
1473 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1475 if(frame == allframes)
1480 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1484 void s_delinstance(char*instance)
1486 instance_t* i = dictionary_lookup(&instances, instance);
1488 syntaxerror("instance %s not known", instance);
1490 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1491 swf_SetU16(tag, i->depth);
1492 dictionary_del(&instances, instance);
1495 void s_qchange(char*instance, parameters_t p)
1502 syntaxerror(".end unexpected");
1503 if(stack[stackpos-1].type == 0)
1505 else if(stack[stackpos-1].type == 1)
1507 else if(stack[stackpos-1].type == 2)
1509 else if(stack[stackpos-1].type == 3)
1511 else syntaxerror("internal error 1");
1514 // ------------------------------------------------------------------------
1516 typedef int command_func_t(map_t*args);
1518 SRECT parseBox(char*str)
1521 float xmin, xmax, ymin, ymax;
1522 char*x = strchr(str, 'x');
1524 if(!strcmp(str, "autocrop")) {
1525 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1529 d1 = strchr(x+1, ':');
1531 d2 = strchr(d1+1, ':');
1533 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1537 else if(d1 && !d2) {
1538 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1544 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1549 r.xmin = (SCOORD)(xmin*20);
1550 r.ymin = (SCOORD)(ymin*20);
1551 r.xmax = (SCOORD)(xmax*20);
1552 r.ymax = (SCOORD)(ymax*20);
1555 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1558 float parseFloat(char*str)
1562 int parseInt(char*str)
1567 if(str[0]=='+' || str[0]=='-')
1571 if(str[t]<'0' || str[t]>'9')
1572 syntaxerror("Not an Integer: \"%s\"", str);
1575 int parseTwip(char*str)
1579 if(str[0]=='+' || str[0]=='-') {
1584 dot = strchr(str, '.');
1588 return sign*parseInt(str)*20;
1590 int l=strlen(++dot);
1592 for(s=str;s<dot-1;s++)
1593 if(*s<'0' || *s>'9')
1594 syntaxerror("Not a coordinate: \"%s\"", str);
1596 if(*s<'0' || *s>'9')
1597 syntaxerror("Not a coordinate: \"%s\"", str);
1599 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1600 warning("precision loss: %s converted to twip", str);
1605 return sign*atoi(str)*20;
1607 return sign*atoi(str)*20+atoi(dot)*2;
1609 return sign*atoi(str)*20+atoi(dot)/5;
1614 int isPoint(char*str)
1616 if(strchr(str, '('))
1622 SPOINT parsePoint(char*str)
1626 int l = strlen(str);
1627 char*comma = strchr(str, ',');
1628 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1629 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1630 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1631 p.x = parseTwip(tmp);
1632 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1633 p.y = parseTwip(tmp);
1637 int parseColor2(char*str, RGBA*color)
1639 int l = strlen(str);
1643 struct {unsigned char r,g,b;char*name;} colors[] =
1644 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1645 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1646 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1647 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1648 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1649 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1650 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1651 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1652 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1653 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1654 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1655 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1659 if(str[0]=='#' && (l==7 || l==9)) {
1660 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1662 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1664 color->r = r; color->g = g; color->b = b; color->a = a;
1667 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1668 if(!strcmp(str, colors[t].name)) {
1673 color->r = r; color->g = g; color->b = b; color->a = a;
1679 RGBA parseColor(char*str)
1682 if(!parseColor2(str, &c))
1683 syntaxerror("Expression '%s' is not a color", str);
1687 typedef struct _muladd {
1692 MULADD parseMulAdd(char*str)
1695 char* str2 = (char*)malloc(strlen(str)+5);
1702 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1703 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1704 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1705 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1706 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1707 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1708 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1709 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1710 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1711 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1713 syntaxerror("'%s' is not a valid color transform expression", str);
1715 m.add = (int)(add*256);
1716 m.mul = (int)(mul*256);
1721 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1723 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1724 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1726 if(a<-32768) a=-32768;
1727 if(a>32767) a=32767;
1728 if(m<-32768) m=-32768;
1729 if(m>32767) m=32767;
1735 float parsePercent(char*str)
1737 int l = strlen(str);
1741 return atoi(str)/100.0;
1743 syntaxerror("Expression '%s' is not a percentage", str);
1746 int isPercent(char*str)
1748 return str[strlen(str)-1]=='%';
1750 int parseNewSize(char*str, int size)
1753 return parsePercent(str)*size;
1755 return (int)(atof(str)*20);
1758 int isColor(char*str)
1761 return parseColor2(str, &c);
1764 static char* lu(map_t* args, char*name)
1766 char* value = map_lookup(args, name);
1768 map_dump(args, stdout, "");
1769 syntaxerror("internal error 2: value %s should be set", name);
1774 static int c_flash(map_t*args)
1776 char* name = lu(args, "name");
1777 char* compressstr = lu(args, "compress");
1778 SRECT bbox = parseBox(lu(args, "bbox"));
1779 int version = parseInt(lu(args, "version"));
1780 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1782 RGBA color = parseColor(lu(args, "background"));
1783 if(!strcmp(name, "!default!") || override_outputname)
1786 if(!strcmp(compressstr, "default"))
1787 compress = version==6;
1788 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1790 else if(!strcmp(compressstr, "no"))
1792 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1794 s_swf(name, bbox, version, fps, compress, color);
1797 int isRelative(char*str)
1799 return !strncmp(str, "<plus>", 6) ||
1800 !strncmp(str, "<minus>", 7);
1802 char* getOffset(char*str)
1804 if(!strncmp(str, "<plus>", 6))
1806 if(!strncmp(str, "<minus>", 7))
1808 syntaxerror("internal error (347)");
1811 int getSign(char*str)
1813 if(!strncmp(str, "<plus>", 6))
1815 if(!strncmp(str, "<minus>", 7))
1817 syntaxerror("internal error (348)");
1820 static dictionary_t points;
1821 static mem_t mpoints;
1822 int points_initialized = 0;
1824 SPOINT getPoint(SRECT r, char*name)
1827 if(!strcmp(name, "center")) {
1829 p.x = (r.xmin + r.xmax)/2;
1830 p.y = (r.ymin + r.ymax)/2;
1834 if(points_initialized)
1835 l = (int)dictionary_lookup(&points, name);
1837 syntaxerror("Invalid point: \"%s\".", name);
1840 return *(SPOINT*)&mpoints.buffer[l];
1842 static int c_gradient(map_t*args)
1844 char*name = lu(args, "name");
1845 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1846 int rotate = parseInt(lu(args, "rotate"));
1850 syntaxerror("colon (:) expected");
1852 s_gradient(name, text, radial,rotate);
1855 static int c_point(map_t*args)
1857 char*name = lu(args, "name");
1861 if(!points_initialized) {
1862 dictionary_init(&points);
1864 points_initialized = 1;
1866 p.x = parseTwip(lu(args, "x"));
1867 p.y = parseTwip(lu(args, "y"));
1868 pos = mem_put(&mpoints, &p, sizeof(p));
1869 string_set(&s1, name);
1871 dictionary_put(&points, s1, (void*)pos);
1874 static int c_play(map_t*args)
1876 char*name = lu(args, "name");
1877 char*loop = lu(args, "loop");
1878 char*nomultiple = lu(args, "nomultiple");
1880 if(!strcmp(nomultiple, "nomultiple"))
1883 nm = parseInt(nomultiple);
1885 if(s_playsound(name, parseInt(loop), nm, 0)) {
1887 } else if(s_swf3action(name, "play")) {
1893 static int c_stop(map_t*args)
1895 char*name = lu(args, "name");
1897 if(s_playsound(name, 0,0,1)) {
1899 } else if(s_swf3action(name, "stop")) {
1902 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1906 static int c_nextframe(map_t*args)
1908 char*name = lu(args, "name");
1910 if(s_swf3action(name, "nextframe")) {
1913 syntaxerror("I don't know anything about movie \"%s\"", name);
1917 static int c_previousframe(map_t*args)
1919 char*name = lu(args, "name");
1921 if(s_swf3action(name, "previousframe")) {
1924 syntaxerror("I don't know anything about movie \"%s\"", name);
1928 static int c_placement(map_t*args, int type)
1930 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1933 char* luminancestr = lu(args, "luminance");
1934 char* scalestr = lu(args, "scale");
1935 char* scalexstr = lu(args, "scalex");
1936 char* scaleystr = lu(args, "scaley");
1937 char* rotatestr = lu(args, "rotate");
1938 char* shearstr = lu(args, "shear");
1939 char* xstr="", *pivotstr="";
1940 char* ystr="", *anglestr="";
1941 char*above = lu(args, "above"); /*FIXME*/
1942 char*below = lu(args, "below");
1943 char* rstr = lu(args, "red");
1944 char* gstr = lu(args, "green");
1945 char* bstr = lu(args, "blue");
1946 char* astr = lu(args, "alpha");
1947 char* pinstr = lu(args, "pin");
1948 char* as = map_lookup(args, "as");
1956 if(type==9) { // (?) .rotate or .arcchange
1957 pivotstr = lu(args, "pivot");
1958 anglestr = lu(args, "angle");
1960 xstr = lu(args, "x");
1961 ystr = lu(args, "y");
1964 luminance = parseMulAdd(luminancestr);
1967 luminance.mul = 256;
1971 if(scalexstr[0]||scaleystr[0])
1972 syntaxerror("scalex/scaley and scale cannot both be set");
1973 scalexstr = scaleystr = scalestr;
1976 if(type == 0 || type == 4) {
1978 character = lu(args, "character");
1979 parameters_clear(&p);
1980 } else if (type == 5) {
1981 character = lu(args, "name");
1982 parameters_clear(&p);
1985 p = s_getParameters(instance);
1990 if(isRelative(xstr)) {
1991 if(type == 0 || type == 4)
1992 syntaxerror("relative x values not allowed for initial put or startclip");
1993 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1995 p.x = parseTwip(xstr);
1999 if(isRelative(ystr)) {
2000 if(type == 0 || type == 4)
2001 syntaxerror("relative y values not allowed for initial put or startclip");
2002 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2004 p.y = parseTwip(ystr);
2008 /* scale, scalex, scaley */
2010 oldbbox = s_getCharBBox(character);
2012 oldbbox = s_getInstanceBBox(instance);
2014 oldwidth = oldbbox.xmax - oldbbox.xmin;
2015 oldheight = oldbbox.ymax - oldbbox.ymin;
2017 if(oldwidth==0) p.scalex = 1.0;
2020 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2024 if(oldheight==0) p.scaley = 1.0;
2027 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2033 if(isRelative(rotatestr)) {
2034 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2036 p.rotate = parseFloat(rotatestr);
2042 if(isRelative(shearstr)) {
2043 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2045 p.shear = parseFloat(shearstr);
2050 if(isPoint(pivotstr))
2051 p.pivot = parsePoint(pivotstr);
2053 p.pivot = getPoint(oldbbox, pivotstr);
2057 p.pin = parsePoint(pinstr);
2059 p.pin = getPoint(oldbbox, pinstr);
2062 /* color transform */
2064 if(rstr[0] || luminancestr[0]) {
2067 r = parseMulAdd(rstr);
2069 r.add = p.cxform.r0;
2070 r.mul = p.cxform.r1;
2072 r = mergeMulAdd(r, luminance);
2073 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2075 if(gstr[0] || luminancestr[0]) {
2078 g = parseMulAdd(gstr);
2080 g.add = p.cxform.g0;
2081 g.mul = p.cxform.g1;
2083 g = mergeMulAdd(g, luminance);
2084 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2086 if(bstr[0] || luminancestr[0]) {
2089 b = parseMulAdd(bstr);
2091 b.add = p.cxform.b0;
2092 b.mul = p.cxform.b1;
2094 b = mergeMulAdd(b, luminance);
2095 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2098 MULADD a = parseMulAdd(astr);
2099 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2103 s_put(instance, character, p);
2105 s_change(instance, p);
2107 s_qchange(instance, p);
2109 s_jump(instance, p);
2111 s_startclip(instance, character, p);
2112 else if(type == 5) {
2114 s_buttonput(character, as, p);
2116 s_buttonput(character, "shape", p);
2121 static int c_put(map_t*args)
2123 c_placement(args, 0);
2126 static int c_change(map_t*args)
2128 c_placement(args, 1);
2131 static int c_qchange(map_t*args)
2133 c_placement(args, 2);
2136 static int c_arcchange(map_t*args)
2138 c_placement(args, 2);
2141 static int c_jump(map_t*args)
2143 c_placement(args, 3);
2146 static int c_startclip(map_t*args)
2148 c_placement(args, 4);
2151 static int c_show(map_t*args)
2153 c_placement(args, 5);
2156 static int c_del(map_t*args)
2158 char*instance = lu(args, "name");
2159 s_delinstance(instance);
2162 static int c_end(map_t*args)
2167 static int c_sprite(map_t*args)
2169 char* name = lu(args, "name");
2173 static int c_frame(map_t*args)
2175 char*framestr = lu(args, "n");
2176 char*cutstr = lu(args, "cut");
2177 char*name = lu(args, "name");
2180 if(strcmp(cutstr, "no"))
2182 if(isRelative(framestr)) {
2183 frame = s_getframe();
2184 if(getSign(framestr)<0)
2185 syntaxerror("relative frame expressions must be positive");
2186 frame += parseInt(getOffset(framestr));
2189 frame = parseInt(framestr);
2190 if(s_getframe() >= frame
2191 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2192 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2194 s_frame(frame, cut, name);
2197 static int c_primitive(map_t*args)
2199 char*name = lu(args, "name");
2200 char*command = lu(args, "commandname");
2201 int width=0, height=0, r=0;
2202 int linewidth = parseTwip(lu(args, "line"));
2203 char*colorstr = lu(args, "color");
2204 RGBA color = parseColor(colorstr);
2205 char*fillstr = lu(args, "fill");
2212 if(!strcmp(command, "circle"))
2214 else if(!strcmp(command, "filled"))
2218 width = parseTwip(lu(args, "width"));
2219 height = parseTwip(lu(args, "height"));
2220 } else if (type==1) {
2221 r = parseTwip(lu(args, "r"));
2222 } else if (type==2) {
2223 outline = lu(args, "outline");
2226 if(!strcmp(fillstr, "fill"))
2228 if(!strcmp(fillstr, "none"))
2230 if(width<0 || height<0 || linewidth<0 || r<0)
2231 syntaxerror("values width, height, line, r must be positive");
2233 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2234 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2235 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2239 static int c_textshape(map_t*args)
2241 char*name = lu(args, "name");
2242 char*text = lu(args, "text");
2243 char*font = lu(args, "font");
2244 float size = parsePercent(lu(args, "size"));
2246 s_textshape(name, font, size, text);
2250 static int c_swf(map_t*args)
2252 char*name = lu(args, "name");
2253 char*filename = lu(args, "filename");
2254 char*command = lu(args, "commandname");
2255 if(!strcmp(command, "shape"))
2256 warning("Please use .swf instead of .shape");
2257 s_includeswf(name, filename);
2261 static int c_font(map_t*args)
2263 char*name = lu(args, "name");
2264 char*filename = lu(args, "filename");
2265 s_font(name, filename);
2269 static int c_sound(map_t*args)
2271 char*name = lu(args, "name");
2272 char*filename = lu(args, "filename");
2273 s_sound(name, filename);
2277 static int c_text(map_t*args)
2279 char*name = lu(args, "name");
2280 char*text = lu(args, "text");
2281 char*font = lu(args, "font");
2282 float size = parsePercent(lu(args, "size"));
2283 RGBA color = parseColor(lu(args, "color"));
2284 s_text(name, font, text, (int)(size*100), color);
2288 static int c_soundtrack(map_t*args)
2293 static int c_image(map_t*args)
2295 char*command = lu(args, "commandname");
2296 char*name = lu(args, "name");
2297 char*filename = lu(args, "filename");
2298 if(!strcmp(command,"jpeg")) {
2299 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2300 s_image(name, "jpeg", filename, quality);
2302 s_image(name, "png", filename, 0);
2307 static int c_outline(map_t*args)
2309 char*name = lu(args, "name");
2310 char*format = lu(args, "format");
2314 syntaxerror("colon (:) expected");
2316 s_outline(name, format, text);
2320 int fakechar(map_t*args)
2322 char*name = lu(args, "name");
2323 s_box(name, 0, 0, black, 20, 0);
2327 static int c_egon(map_t*args) {return fakechar(args);}
2328 static int c_button(map_t*args) {
2329 char*name = lu(args, "name");
2333 static int current_button_flags = 0;
2334 static int c_on_press(map_t*args)
2336 char*position = lu(args, "position");
2338 if(!strcmp(position, "inside")) {
2339 current_button_flags |= BC_OVERUP_OVERDOWN;
2340 } else if(!strcmp(position, "outside")) {
2341 //current_button_flags |= BC_IDLE_OUTDOWN;
2342 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2343 } else if(!strcmp(position, "anywhere")) {
2344 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2347 if(type == RAWDATA) {
2349 s_buttonaction(current_button_flags, action);
2350 current_button_flags = 0;
2356 static int c_on_release(map_t*args)
2358 char*position = lu(args, "position");
2360 if(!strcmp(position, "inside")) {
2361 current_button_flags |= BC_OVERDOWN_OVERUP;
2362 } else if(!strcmp(position, "outside")) {
2363 current_button_flags |= BC_OUTDOWN_IDLE;
2364 } else if(!strcmp(position, "anywhere")) {
2365 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2368 if(type == RAWDATA) {
2370 s_buttonaction(current_button_flags, action);
2371 current_button_flags = 0;
2377 static int c_on_move_in(map_t*args)
2379 char*position = lu(args, "state");
2381 if(!strcmp(position, "pressed")) {
2382 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2383 } else if(!strcmp(position, "not_pressed")) {
2384 current_button_flags |= BC_IDLE_OVERUP;
2385 } else if(!strcmp(position, "any")) {
2386 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2389 if(type == RAWDATA) {
2391 s_buttonaction(current_button_flags, action);
2392 current_button_flags = 0;
2398 static int c_on_move_out(map_t*args)
2400 char*position = lu(args, "state");
2402 if(!strcmp(position, "pressed")) {
2403 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2404 } else if(!strcmp(position, "not_pressed")) {
2405 current_button_flags |= BC_OVERUP_IDLE;
2406 } else if(!strcmp(position, "any")) {
2407 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2410 if(type == RAWDATA) {
2412 s_buttonaction(current_button_flags, action);
2413 current_button_flags = 0;
2419 static int c_on_key(map_t*args)
2421 char*key = lu(args, "key");
2423 if(strlen(key)==1) {
2426 current_button_flags |= 0x4000 + (key[0]*0x200);
2428 syntaxerror("invalid character: %c"+key[0]);
2433 <ctrl-x> = 0x200*(x-'a')
2437 syntaxerror("invalid key: %s",key);
2440 if(type == RAWDATA) {
2442 s_buttonaction(current_button_flags, action);
2443 current_button_flags = 0;
2450 static int c_edittext(map_t*args)
2452 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2453 char*name = lu(args, "name");
2454 char*font = lu(args, "font");
2455 int size = (int)(1024*parsePercent(lu(args, "size")));
2456 int width = parseTwip(lu(args, "width"));
2457 int height = parseTwip(lu(args, "height"));
2458 char*text = lu(args, "text");
2459 RGBA color = parseColor(lu(args, "color"));
2460 int maxlength = parseInt(lu(args, "maxlength"));
2461 char*variable = lu(args, "variable");
2462 char*passwordstr = lu(args, "password");
2463 char*wordwrapstr = lu(args, "wordwrap");
2464 char*multilinestr = lu(args, "multiline");
2465 char*htmlstr = lu(args, "html");
2466 char*noselectstr = lu(args, "noselect");
2467 char*readonlystr = lu(args, "readonly");
2468 char*borderstr = lu(args, "border");
2471 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2472 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2473 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2474 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2475 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2476 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2477 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2479 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2483 static int c_morphshape(map_t*args) {return fakechar(args);}
2484 static int c_movie(map_t*args) {return fakechar(args);}
2486 static int c_texture(map_t*args) {return 0;}
2488 static int c_action(map_t*args)
2491 if(type != RAWDATA) {
2492 syntaxerror("colon (:) expected");
2502 command_func_t* func;
2505 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2506 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2507 // "import" type stuff
2508 {"swf", c_swf, "name filename"},
2509 {"shape", c_swf, "name filename"},
2510 {"jpeg", c_image, "name filename quality=80%"},
2511 {"png", c_image, "name filename"},
2512 {"movie", c_movie, "name filename"},
2513 {"sound", c_sound, "name filename"},
2514 {"font", c_font, "name filename"},
2515 {"soundtrack", c_soundtrack, "filename"},
2517 // generators of primitives
2519 {"point", c_point, "name x=0 y=0"},
2520 {"gradient", c_gradient, "name @radial=0 rotate=0"},
2521 {"outline", c_outline, "name format=simple"},
2522 {"textshape", c_textshape, "name font size=100% text"},
2524 // character generators
2525 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2526 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2527 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2529 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2530 {"text", c_text, "name text font size=100% color=white"},
2531 {"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"},
2532 {"morphshape", c_morphshape, "name start end"},
2533 {"button", c_button, "name"},
2534 {"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="},
2535 {"on_press", c_on_press, "position=inside"},
2536 {"on_release", c_on_release, "position=anywhere"},
2537 {"on_move_in", c_on_move_in, "state=not_pressed"},
2538 {"on_move_out", c_on_move_out, "state=not_pressed"},
2539 {"on_key", c_on_key, "key=any"},
2542 {"play", c_play, "name loop=0 @nomultiple=0"},
2543 {"stop", c_stop, "name"},
2544 {"nextframe", c_nextframe, "name"},
2545 {"previousframe", c_previousframe, "name"},
2547 // object placement tags
2548 {"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="},
2549 {"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="},
2550 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2551 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2552 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2553 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2554 {"del", c_del, "name"},
2555 // virtual object placement
2556 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2558 // commands which start a block
2559 //startclip (see above)
2560 {"sprite", c_sprite, "name"},
2561 {"action", c_action, ""},
2567 static map_t parseArguments(char*command, char*pattern)
2583 string_set(&t1, "commandname");
2584 string_set(&t2, command);
2585 map_put(&result, t1, t2);
2587 if(!pattern || !*pattern)
2594 if(!strncmp("<i> ", x, 3)) {
2596 if(type == COMMAND || type == RAWDATA) {
2598 syntaxerror("character name expected");
2600 name[pos].str = "instance";
2602 value[pos].str = text;
2603 value[pos].len = strlen(text);
2607 if(type == ASSIGNMENT)
2610 name[pos].str = "character";
2612 value[pos].str = text;
2613 value[pos].len = strlen(text);
2621 isboolean[pos] = (x[0] =='@');
2634 name[pos].len = d-x;
2639 name[pos].len = e-x;
2640 value[pos].str = e+1;
2641 value[pos].len = d-e-1;
2649 /* for(t=0;t<len;t++) {
2650 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2651 isboolean[t]?"(boolean)":"");
2656 if(type == RAWDATA || type == COMMAND) {
2661 // first, search for boolean arguments
2662 for(pos=0;pos<len;pos++)
2664 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2666 if(type == ASSIGNMENT)
2668 value[pos].str = text;
2669 value[pos].len = strlen(text);
2670 /*printf("setting boolean parameter %s (to %s)\n",
2671 strdup_n(name[pos], namelen[pos]),
2672 strdup_n(value[pos], valuelen[pos]));*/
2677 // second, search for normal arguments
2679 for(pos=0;pos<len;pos++)
2681 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2682 (type != ASSIGNMENT && !set[pos])) {
2684 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2686 if(type == ASSIGNMENT)
2689 value[pos].str = text;
2690 value[pos].len = strlen(text);
2692 printf("setting parameter %s (to %s)\n",
2693 strdup_n(name[pos].str, name[pos].len),
2694 strdup_n(value[pos].str, value[pos].len));
2700 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2704 for(t=0;t<len;t++) {
2705 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2708 for(t=0;t<len;t++) {
2709 if(value[t].str && value[t].str[0] == '*') {
2710 //relative default- take value from some other parameter
2712 for(s=0;s<len;s++) {
2713 if(value[s].len == value[t].len-1 &&
2714 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2715 value[t].str = value[s].str;
2718 if(value[t].str == 0) {
2720 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2724 /* ok, now construct the dictionary from the parameters */
2728 map_put(&result, name[t], value[t]);
2732 static void parseArgumentsForCommand(char*command)
2737 msg("<verbose> parse Command: %s (line %d)", command, line);
2739 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2740 if(!strcmp(arguments[t].command, command)) {
2742 /* ugly hack- will be removed soon (once documentation and .sc generating
2743 utilities have been changed) */
2744 if(!strcmp(command, "swf") && !stackpos) {
2745 warning("Please use .flash instead of .swf- this will be mandatory soon");
2750 args = parseArguments(command, arguments[t].arguments);
2756 syntaxerror("command %s not known", command);
2758 // catch missing .flash directives at the beginning of a file
2759 if(strcmp(command, "flash") && !stackpos)
2761 syntaxerror("No movie defined- use .flash first");
2765 printf(".%s\n", command);fflush(stdout);
2766 map_dump(&args, stdout, "\t");fflush(stdout);
2769 (*arguments[nr].func)(&args);
2771 /*if(!strcmp(command, "button") ||
2772 !strcmp(command, "action")) {
2775 if(type == COMMAND) {
2776 if(!strcmp(text, "end"))
2790 int main (int argc,char ** argv)
2793 processargs(argc, argv);
2794 initLog(0,-1,0,0,-1,verbose);
2797 args_callback_usage(argv[0]);
2801 file = generateTokens(filename);
2803 printf("parser returned error.\n");
2809 while(!noMoreTokens()) {
2812 syntaxerror("command expected");
2813 parseArgumentsForCommand(text);