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;
325 p->pivot.x = 0; p->pivot.y = 0;
328 swf_GetCXForm(0, &p->cxform, 1);
331 static void makeMatrix(MATRIX*m, parameters_t*p)
340 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
341 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
342 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
343 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
345 m->sx = (int)(sx*65536+0.5);
346 m->r1 = (int)(r1*65536+0.5);
347 m->r0 = (int)(r0*65536+0.5);
348 m->sy = (int)(sy*65536+0.5);
352 h = swf_TurnPoint(p->pin, m);
357 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
362 r = swf_TurnRect(rect, &m);
363 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
364 currentrect.xmax == 0 && currentrect.ymax == 0)
367 swf_ExpandRect2(¤trect, &r);
371 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
373 SWF*swf = (SWF*)malloc(sizeof(SWF));
376 syntaxerror(".swf blocks can't be nested");
378 memset(swf, 0, sizeof(swf));
379 swf->fileVersion = version;
381 swf->frameRate = fps;
382 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
383 swf->compressed = compress;
384 swf_SetRGB(tag,&background);
386 if(stackpos==sizeof(stack)/sizeof(stack[0]))
387 syntaxerror("too many levels of recursion");
389 dictionary_init(&characters);
390 dictionary_init(&images);
391 dictionary_init(&outlines);
392 dictionary_init(&gradients);
393 dictionary_init(&instances);
394 dictionary_init(&fonts);
395 dictionary_init(&sounds);
397 memset(&stack[stackpos], 0, sizeof(stack[0]));
398 stack[stackpos].type = 0;
399 stack[stackpos].filename = strdup(name);
400 stack[stackpos].swf = swf;
401 stack[stackpos].oldframe = -1;
406 memset(¤trect, 0, sizeof(currentrect));
409 memset(idmap, 0, sizeof(idmap));
413 void s_sprite(char*name)
415 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
416 swf_SetU16(tag, id); //id
417 swf_SetU16(tag, 0); //frames
419 memset(&stack[stackpos], 0, sizeof(stack[0]));
420 stack[stackpos].type = 1;
421 stack[stackpos].oldframe = currentframe;
422 stack[stackpos].olddepth = currentdepth;
423 stack[stackpos].oldrect = currentrect;
424 stack[stackpos].oldinstances = instances;
425 stack[stackpos].tag = tag;
426 stack[stackpos].id = id;
427 stack[stackpos].name = strdup(name);
429 /* FIXME: those four fields should be bundled together */
430 dictionary_init(&instances);
433 memset(¤trect, 0, sizeof(currentrect));
439 typedef struct _buttonrecord
447 typedef struct _button
451 buttonrecord_t records[4];
454 static button_t mybutton;
456 void s_button(char*name)
458 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
459 swf_SetU16(tag, id); //id
460 swf_ButtonSetFlags(tag, 0); //menu=no
462 memset(&mybutton, 0, sizeof(mybutton));
464 memset(&stack[stackpos], 0, sizeof(stack[0]));
465 stack[stackpos].type = 3;
466 stack[stackpos].tag = tag;
467 stack[stackpos].id = id;
468 stack[stackpos].name = strdup(name);
469 stack[stackpos].oldrect = currentrect;
470 memset(¤trect, 0, sizeof(currentrect));
475 void s_buttonput(char*character, char*as, parameters_t p)
477 character_t* c = dictionary_lookup(&characters, character);
482 if(!stackpos || (stack[stackpos-1].type != 3)) {
483 syntaxerror(".show may only appear in .button");
486 syntaxerror("character %s not known (in .shape %s)", character, character);
488 if(mybutton.endofshapes) {
489 syntaxerror("a .do may not precede a .show", character, character);
492 m = s_instancepos(c->size, &p);
500 if(*s==',' || *s==0) {
501 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
502 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
503 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
504 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
505 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
506 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
513 static void setbuttonrecords(TAG*tag)
515 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
516 if(!mybutton.endofshapes) {
519 if(!mybutton.records[3].set) {
520 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
524 if(mybutton.records[t].set) {
525 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
528 swf_SetU8(tag,0); // end of button records
529 mybutton.endofshapes = 1;
533 void s_buttonaction(int flags, char*action)
539 setbuttonrecords(stack[stackpos-1].tag);
541 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
543 syntaxerror("Couldn't compile ActionScript");
546 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
547 swf_ActionSet(stack[stackpos-1].tag, a);
548 mybutton.nr_actions++;
553 static void setactionend(TAG*tag)
555 if(!mybutton.nr_actions) {
556 /* no actions means we didn't have an actionoffset,
557 which means we can't signal the end of the
558 buttonaction records, so, *sigh*, we have
559 to insert a dummy record */
560 swf_SetU16(tag, 0); //offset
561 swf_SetU16(tag, 0); //condition
562 swf_SetU8(tag, 0); //action
566 static void s_endButton()
569 setbuttonrecords(stack[stackpos-1].tag);
570 setactionend(stack[stackpos-1].tag);
573 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
577 tag = stack[stackpos].tag;
578 currentrect = stack[stackpos].oldrect;
580 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
581 free(stack[stackpos].name);
584 TAG* removeFromTo(TAG*from, TAG*to)
586 TAG*save = from->prev;
588 TAG*next = from->next;
596 static void s_endSprite()
598 SRECT r = currentrect;
600 if(stack[stackpos].cut)
601 tag = removeFromTo(stack[stackpos].cut, tag);
605 /* TODO: before clearing, prepend "<spritename>." to names and
606 copy into old instances dict */
607 dictionary_clear(&instances);
609 currentframe = stack[stackpos].oldframe;
610 currentrect = stack[stackpos].oldrect;
611 currentdepth = stack[stackpos].olddepth;
612 instances = stack[stackpos].oldinstances;
614 tag = swf_InsertTag(tag, ST_END);
616 tag = stack[stackpos].tag;
619 syntaxerror("internal error(7)");
621 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
622 free(stack[stackpos].name);
625 static void s_endSWF()
631 if(stack[stackpos].cut)
632 tag = removeFromTo(stack[stackpos].cut, tag);
636 swf = stack[stackpos].swf;
637 filename = stack[stackpos].filename;
639 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
641 tag = swf_InsertTag(tag, ST_END);
643 swf_OptimizeTagOrder(swf);
645 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
646 swf->movieSize = currentrect; /* "autocrop" */
649 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
650 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
651 swf->movieSize.ymax += 20;
654 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
656 syntaxerror("couldn't create output file %s", filename);
659 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
661 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
665 dictionary_clear(&instances);
666 dictionary_clear(&characters);
667 dictionary_clear(&images);
668 dictionary_clear(&outlines);
669 dictionary_clear(&gradients);
670 dictionary_clear(&fonts);
671 dictionary_clear(&sounds);
681 if(stack[stackpos-1].type == 0)
682 syntaxerror("End of file encountered in .flash block");
683 if(stack[stackpos-1].type == 1)
684 syntaxerror("End of file encountered in .sprite block");
685 if(stack[stackpos-1].type == 2)
686 syntaxerror("End of file encountered in .clip block");
695 void s_frame(int nr, int cut, char*name)
700 for(t=currentframe;t<nr;t++) {
701 tag = swf_InsertTag(tag, ST_SHOWFRAME);
702 if(t==nr-1 && name && *name) {
703 tag = swf_InsertTag(tag, ST_FRAMELABEL);
704 swf_SetString(tag, name);
710 syntaxerror("Can't cut, frame empty");
712 stack[stackpos].cut = tag;
718 int parseColor2(char*str, RGBA*color);
720 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
725 if(texture[0] == '#') {
726 parseColor2(texture, &color);
727 return swf_ShapeAddSolidFillStyle(s, &color);
728 } else if((image = dictionary_lookup(&images, texture))) {
730 swf_GetMatrix(0, &m);
731 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
732 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
735 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
736 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
737 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
739 swf_GetMatrix(0, &m);
740 m.sx = (r->xmax - r->xmin)*2;
741 m.sy = (r->ymax - r->ymin)*2;
742 m.tx = r->xmin + (r->xmax - r->xmin)/2;
743 m.ty = r->ymin + (r->ymax - r->ymin)/2;
744 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
745 } else if (parseColor2(texture, &color)) {
746 return swf_ShapeAddSolidFillStyle(s, &color);
748 syntaxerror("not a color/fillstyle: %s", texture);
753 RGBA black={r:0,g:0,b:0,a:0};
754 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
763 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
765 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
768 fs1 = addFillStyle(s, &r2, texture);
771 r.xmin = r2.xmin-linewidth-linewidth/2;
772 r.ymin = r2.ymin-linewidth-linewidth/2;
773 r.xmax = r2.xmax+linewidth+linewidth/2;
774 r.ymax = r2.ymax+linewidth+linewidth/2;
776 swf_SetShapeHeader(tag,s);
777 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
778 swf_ShapeSetLine(tag,s,width,0);
779 swf_ShapeSetLine(tag,s,0,height);
780 swf_ShapeSetLine(tag,s,-width,0);
781 swf_ShapeSetLine(tag,s,0,-height);
782 swf_ShapeSetEnd(tag);
785 s_addcharacter(name, id, tag, r);
789 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
795 outline = dictionary_lookup(&outlines, outlinename);
797 syntaxerror("outline %s not defined", outlinename);
801 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
803 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
805 fs1 = addFillStyle(s, &r2, texture);
807 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
809 rect.xmin = r2.xmin-linewidth-linewidth/2;
810 rect.ymin = r2.ymin-linewidth-linewidth/2;
811 rect.xmax = r2.xmax+linewidth+linewidth/2;
812 rect.ymax = r2.ymax+linewidth+linewidth/2;
814 swf_SetRect(tag,&rect);
815 swf_SetShapeStyles(tag, s);
816 swf_SetShapeBits(tag, outline->shape); //does not count bits!
817 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
820 s_addcharacter(name, id, tag, rect);
824 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
829 r2.xmin = r2.ymin = 0;
833 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
835 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
837 fs1 = addFillStyle(s, &r2, texture);
839 rect.xmin = r2.xmin-linewidth-linewidth/2;
840 rect.ymin = r2.ymin-linewidth-linewidth/2;
841 rect.xmax = r2.xmax+linewidth+linewidth/2;
842 rect.ymax = r2.ymax+linewidth+linewidth/2;
844 swf_SetRect(tag,&rect);
845 swf_SetShapeHeader(tag,s);
846 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
847 swf_ShapeSetCircle(tag, s, r,r,r,r);
848 swf_ShapeSetEnd(tag);
851 s_addcharacter(name, id, tag, rect);
855 void s_textshape(char*name, char*fontname, float size, char*_text)
858 U8*text = (U8*)_text;
862 font = dictionary_lookup(&fonts, fontname);
864 syntaxerror("font \"%s\" not known!", fontname);
866 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
867 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
868 s_box(name, 0, 0, black, 20, 0);
871 g = font->ascii2glyph[text[0]];
873 outline = malloc(sizeof(outline_t));
874 memset(outline, 0, sizeof(outline_t));
875 outline->shape = font->glyph[g].shape;
876 outline->bbox = font->layout->bounds[g];
880 swf_Shape11DrawerInit(&draw, 0);
881 swf_DrawText(&draw, font, (int)(size*100), _text);
883 outline->shape = swf_ShapeDrawerToShape(&draw);
884 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
888 if(dictionary_lookup(&outlines, name))
889 syntaxerror("outline %s defined twice", name);
890 dictionary_put2(&outlines, name, outline);
893 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
898 font = dictionary_lookup(&fonts, fontname);
900 syntaxerror("font \"%s\" not known!", fontname);
902 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
904 if(!font->numchars) {
905 s_box(name, 0, 0, black, 20, 0);
908 r = swf_SetDefineText(tag, font, &color, text, size);
910 s_addcharacter(name, id, tag, r);
914 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
917 EditTextLayout layout;
920 font = dictionary_lookup(&fonts, fontname);
922 syntaxerror("font \"%s\" not known!", fontname);
923 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
926 layout.leftmargin = 0;
927 layout.rightmargin = 0;
934 swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
936 s_addcharacter(name, id, tag, r);
940 /* type: either "jpeg" or "png"
942 void s_image(char*name, char*type, char*filename, int quality)
944 /* an image is actually two folded: 1st bitmap, 2nd character.
945 Both of them can be used separately */
947 /* step 1: the bitmap */
952 warning("image type \"png\" not supported yet!");
953 s_box(name, 0, 0, black, 20, 0);
958 warning("no jpeg support compiled in");
959 s_box(name, 0, 0, black, 20, 0);
962 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
963 swf_SetU16(tag, imageID);
965 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
966 syntaxerror("Image \"%s\" not found, or contains errors", filename);
969 swf_GetJPEGSize(filename, &width, &height);
976 s_addimage(name, id, tag, r);
981 /* step 2: the character */
982 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
984 swf_ShapeSetBitmapRect(tag, imageID, width, height);
986 s_addcharacter(name, id, tag, r);
990 void dumpSWF(SWF*swf)
992 TAG* tag = swf->firstTag;
993 printf("vvvvvvvvvvvvvvvvvvvvv\n");
995 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
998 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1001 void s_font(char*name, char*filename)
1004 font = swf_LoadFont(filename);
1007 warning("Couldn't open font file \"%s\"", filename);
1008 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1009 memset(font, 0, sizeof(SWFFONT));
1010 dictionary_put2(&fonts, name, font);
1016 /* fix the layout. Only needed for old fonts */
1018 for(t=0;t<font->numchars;t++) {
1019 font->glyph[t].advance = 0;
1022 swf_FontCreateLayout(font);
1024 /* just in case this thing is used in .edittext later on */
1025 swf_FontPrepareForEditText(font);
1028 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1029 swf_FontSetDefine2(tag, font);
1032 if(dictionary_lookup(&fonts, name))
1033 syntaxerror("font %s defined twice", name);
1034 dictionary_put2(&fonts, name, font);
1039 typedef struct _sound_t
1045 void s_sound(char*name, char*filename)
1047 struct WAV wav, wav2;
1052 if(!readWAV(filename, &wav)) {
1053 warning("Couldn't read wav file \"%s\"", filename);
1057 convertWAV2mono(&wav, &wav2, 44100);
1058 samples = (U16*)wav2.data;
1059 numsamples = wav2.size/2;
1063 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1064 swf_SetU16(tag, id); //id
1065 swf_SetSoundDefine(tag, samples, numsamples);
1067 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1071 if(dictionary_lookup(&sounds, name))
1072 syntaxerror("sound %s defined twice", name);
1073 dictionary_put2(&sounds, name, sound);
1081 static char* gradient_getToken(const char**p)
1085 while(**p && strchr(" \t\n\r", **p)) {
1089 while(**p && !strchr(" \t\n\r", **p)) {
1092 result = malloc((*p)-start+1);
1093 memcpy(result,start,(*p)-start+1);
1094 result[(*p)-start] = 0;
1098 float parsePercent(char*str);
1099 RGBA parseColor(char*str);
1101 GRADIENT parseGradient(const char*str)
1104 const char* p = str;
1105 memset(&gradient, 0, sizeof(GRADIENT));
1107 char*posstr,*colorstr;
1110 posstr = gradient_getToken(&p);
1113 pos = parsePercent(posstr);
1114 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1115 colorstr = gradient_getToken(&p);
1116 color = parseColor(colorstr);
1117 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1118 warning("gradient record too big- max size is 8, rest ignored");
1121 gradient.ratios[gradient.num] = (int)(pos*255.0);
1122 gradient.rgba[gradient.num] = color;
1130 void s_gradient(char*name, const char*text, int radial)
1132 gradient_t* gradient;
1133 gradient = malloc(sizeof(gradient_t));
1134 memset(gradient, 0, sizeof(gradient_t));
1135 gradient->gradient = parseGradient(text);
1136 gradient->radial = radial;
1138 if(dictionary_lookup(&gradients, name))
1139 syntaxerror("gradient %s defined twice", name);
1140 dictionary_put2(&gradients, name, gradient);
1143 void s_action(const char*text)
1146 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1148 syntaxerror("Couldn't compile ActionScript");
1151 tag = swf_InsertTag(tag, ST_DOACTION);
1153 swf_ActionSet(tag, a);
1158 int s_swf3action(char*name, char*action)
1161 instance_t* object = dictionary_lookup(&instances, name);
1165 a = action_SetTarget(0, name);
1166 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1167 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1168 else if(!strcmp(action, "stop")) a = action_Stop(a);
1169 else if(!strcmp(action, "play")) a = action_Play(a);
1170 a = action_SetTarget(a, "");
1173 tag = swf_InsertTag(tag, ST_DOACTION);
1174 swf_ActionSet(tag, a);
1179 void s_outline(char*name, char*format, char*source)
1188 swf_Shape11DrawerInit(&draw, 0);
1189 draw_string(&draw, source);
1191 shape = swf_ShapeDrawerToShape(&draw);
1192 //shape2 = swf_ShapeToShape2(shape);
1193 //bounds = swf_GetShapeBoundingBox(shape2);
1194 //swf_Shape2Free(shape2);
1195 bounds = swf_ShapeDrawerGetBBox(&draw);
1196 draw.dealloc(&draw);
1198 outline = (outline_t*)malloc(sizeof(outline_t));
1199 memset(outline, 0, sizeof(outline_t));
1200 outline->shape = shape;
1201 outline->bbox = bounds;
1203 if(dictionary_lookup(&outlines, name))
1204 syntaxerror("outline %s defined twice", name);
1205 dictionary_put2(&outlines, name, outline);
1208 int s_playsound(char*name, int loops, int nomultiple, int stop)
1210 sound_t* sound = dictionary_lookup(&sounds, name);
1215 tag = swf_InsertTag(tag, ST_STARTSOUND);
1216 swf_SetU16(tag, sound->id); //id
1217 memset(&info, 0, sizeof(info));
1220 info.nomultiple = nomultiple;
1221 swf_SetSoundInfo(tag, &info);
1225 void s_includeswf(char*name, char*filename)
1233 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1234 f = open(filename,O_RDONLY);
1236 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1237 s_box(name, 0, 0, black, 20, 0);
1240 if (swf_ReadSWF(f,&swf)<0) {
1241 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1242 s_box(name, 0, 0, black, 20, 0);
1247 /* FIXME: The following sets the bounding Box for the character.
1248 It is wrong for two reasons:
1249 a) It may be too small (in case objects in the movie clip at the borders)
1250 b) it may be too big (because the poor movie never got autocropped)
1254 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1255 swf_SetU16(tag, id);
1258 swf_Relocate(&swf, idmap);
1260 ftag = swf.firstTag;
1264 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1265 if(cutout[t] == ftag->id) {
1269 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1271 if(ftag->id == ST_END)
1275 /* We simply dump all tags right after the sprite
1276 header, relying on the fact that swf_OptimizeTagOrder() will
1277 sort things out for us later.
1278 We also rely on the fact that the imported SWF is well-formed.
1280 tag = swf_InsertTag(tag, ftag->id);
1281 swf_SetBlock(tag, ftag->data, ftag->len);
1285 syntaxerror("Included file %s contains errors", filename);
1286 tag = swf_InsertTag(tag, ST_END);
1290 s_addcharacter(name, id, tag, r);
1293 SRECT s_getCharBBox(char*name)
1295 character_t* c = dictionary_lookup(&characters, name);
1296 if(!c) syntaxerror("character '%s' unknown(2)", name);
1299 SRECT s_getInstanceBBox(char*name)
1301 instance_t * i = dictionary_lookup(&instances, name);
1303 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1305 if(!c) syntaxerror("internal error(5)");
1308 parameters_t s_getParameters(char*name)
1310 instance_t * i = dictionary_lookup(&instances, name);
1311 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1312 return i->parameters;
1314 void s_startclip(char*instance, char*character, parameters_t p)
1316 character_t* c = dictionary_lookup(&characters, character);
1320 syntaxerror("character %s not known", character);
1322 i = s_addinstance(instance, c, currentdepth);
1324 m = s_instancepos(i->character->size, &p);
1326 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1327 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1328 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1330 i->lastFrame= currentframe;
1332 stack[stackpos].tag = tag;
1333 stack[stackpos].type = 2;
1342 swf_SetTagPos(stack[stackpos].tag, 0);
1343 swf_GetPlaceObject(stack[stackpos].tag, &p);
1344 p.clipdepth = currentdepth;
1346 swf_ClearTag(stack[stackpos].tag);
1347 swf_SetPlaceObject(stack[stackpos].tag, &p);
1351 void s_put(char*instance, char*character, parameters_t p)
1353 character_t* c = dictionary_lookup(&characters, character);
1357 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1360 i = s_addinstance(instance, c, currentdepth);
1362 m = s_instancepos(i->character->size, &p);
1364 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1365 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1367 i->lastFrame = currentframe;
1371 void s_jump(char*instance, parameters_t p)
1373 instance_t* i = dictionary_lookup(&instances, instance);
1376 syntaxerror("instance %s not known", instance);
1380 m = s_instancepos(i->character->size, &p);
1382 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1383 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1385 i->lastFrame = currentframe;
1388 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1392 if(num==0 || num==1)
1394 ratio = (float)pos/(float)num;
1396 p.x = (p2->x-p1->x)*ratio + p1->x;
1397 p.y = (p2->y-p1->y)*ratio + p1->y;
1398 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1399 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1400 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1401 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1403 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1404 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1405 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1406 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1408 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1409 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1410 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1411 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1413 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1414 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1415 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1416 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1420 void s_change(char*instance, parameters_t p2)
1422 instance_t* i = dictionary_lookup(&instances, instance);
1426 int frame, allframes;
1428 syntaxerror("instance %s not known", instance);
1432 allframes = currentframe - i->lastFrame - 1;
1434 warning(".change ignored. can only .put/.change an object once per frame.");
1438 m = s_instancepos(i->character->size, &p2);
1439 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1440 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1443 /* o.k., we got the start and end point set. Now iterate though all the
1444 tags in between, inserting object changes after each new frame */
1447 if(!t) syntaxerror("internal error(6)");
1449 while(frame < allframes) {
1450 if(t->id == ST_SHOWFRAME) {
1455 p = s_interpolate(&p1, &p2, frame, allframes);
1456 m = s_instancepos(i->character->size, &p); //needed?
1457 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1458 i->lastFrame = currentframe;
1459 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1461 if(frame == allframes)
1466 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1470 void s_delinstance(char*instance)
1472 instance_t* i = dictionary_lookup(&instances, instance);
1474 syntaxerror("instance %s not known", instance);
1476 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1477 swf_SetU16(tag, i->depth);
1478 dictionary_del(&instances, instance);
1481 void s_qchange(char*instance, parameters_t p)
1488 syntaxerror(".end unexpected");
1489 if(stack[stackpos-1].type == 0)
1491 else if(stack[stackpos-1].type == 1)
1493 else if(stack[stackpos-1].type == 2)
1495 else if(stack[stackpos-1].type == 3)
1497 else syntaxerror("internal error 1");
1500 // ------------------------------------------------------------------------
1502 typedef int command_func_t(map_t*args);
1504 SRECT parseBox(char*str)
1507 float xmin, xmax, ymin, ymax;
1508 char*x = strchr(str, 'x');
1510 if(!strcmp(str, "autocrop")) {
1511 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1515 d1 = strchr(x+1, ':');
1517 d2 = strchr(d1+1, ':');
1519 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1523 else if(d1 && !d2) {
1524 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1530 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1535 r.xmin = (SCOORD)(xmin*20);
1536 r.ymin = (SCOORD)(ymin*20);
1537 r.xmax = (SCOORD)(xmax*20);
1538 r.ymax = (SCOORD)(ymax*20);
1541 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1544 float parseFloat(char*str)
1548 int parseInt(char*str)
1553 if(str[0]=='+' || str[0]=='-')
1557 if(str[t]<'0' || str[t]>'9')
1558 syntaxerror("Not an Integer: \"%s\"", str);
1561 int parseTwip(char*str)
1565 if(str[0]=='+' || str[0]=='-') {
1570 dot = strchr(str, '.');
1574 return sign*parseInt(str)*20;
1576 int l=strlen(++dot);
1578 for(s=str;s<dot-1;s++)
1579 if(*s<'0' || *s>'9')
1580 syntaxerror("Not a coordinate: \"%s\"", str);
1582 if(*s<'0' || *s>'9')
1583 syntaxerror("Not a coordinate: \"%s\"", str);
1585 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1586 warning("precision loss: %s converted to twip", str);
1591 return sign*atoi(str)*20;
1593 return sign*atoi(str)*20+atoi(dot)*2;
1595 return sign*atoi(str)*20+atoi(dot)/5;
1600 int isPoint(char*str)
1602 if(strchr(str, '('))
1608 SPOINT parsePoint(char*str)
1612 int l = strlen(str);
1613 char*comma = strchr(str, ',');
1614 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1615 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1616 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1617 p.x = parseTwip(tmp);
1618 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1619 p.y = parseTwip(tmp);
1623 int parseColor2(char*str, RGBA*color)
1625 int l = strlen(str);
1629 struct {unsigned char r,g,b;char*name;} colors[] =
1630 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1631 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1632 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1633 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1634 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1635 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1636 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1637 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1638 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1639 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1640 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1641 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1645 if(str[0]=='#' && (l==7 || l==9)) {
1646 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1648 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1650 color->r = r; color->g = g; color->b = b; color->a = a;
1653 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1654 if(!strcmp(str, colors[t].name)) {
1659 color->r = r; color->g = g; color->b = b; color->a = a;
1665 RGBA parseColor(char*str)
1668 if(!parseColor2(str, &c))
1669 syntaxerror("Expression '%s' is not a color", str);
1673 typedef struct _muladd {
1678 MULADD parseMulAdd(char*str)
1681 char* str2 = (char*)malloc(strlen(str)+5);
1688 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1689 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1690 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1691 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1692 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1693 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1694 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1695 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1696 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1697 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1699 syntaxerror("'%s' is not a valid color transform expression", str);
1701 m.add = (int)(add*256);
1702 m.mul = (int)(mul*256);
1707 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1709 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1710 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1712 if(a<-32768) a=-32768;
1713 if(a>32767) a=32767;
1714 if(m<-32768) m=-32768;
1715 if(m>32767) m=32767;
1721 float parsePercent(char*str)
1723 int l = strlen(str);
1727 return atoi(str)/100.0;
1729 syntaxerror("Expression '%s' is not a percentage", str);
1732 int isPercent(char*str)
1734 return str[strlen(str)-1]=='%';
1736 int parseNewSize(char*str, int size)
1739 return parsePercent(str)*size;
1741 return (int)(atof(str)*20);
1744 int isColor(char*str)
1747 return parseColor2(str, &c);
1750 static char* lu(map_t* args, char*name)
1752 char* value = map_lookup(args, name);
1754 map_dump(args, stdout, "");
1755 syntaxerror("internal error 2: value %s should be set", name);
1760 static int c_flash(map_t*args)
1762 char* name = lu(args, "name");
1763 char* compressstr = lu(args, "compress");
1764 SRECT bbox = parseBox(lu(args, "bbox"));
1765 int version = parseInt(lu(args, "version"));
1766 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1768 RGBA color = parseColor(lu(args, "background"));
1769 if(!strcmp(name, "!default!") || override_outputname)
1772 if(!strcmp(compressstr, "default"))
1773 compress = version==6;
1774 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1776 else if(!strcmp(compressstr, "no"))
1778 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1780 s_swf(name, bbox, version, fps, compress, color);
1783 int isRelative(char*str)
1785 return !strncmp(str, "<plus>", 6) ||
1786 !strncmp(str, "<minus>", 7);
1788 char* getOffset(char*str)
1790 if(!strncmp(str, "<plus>", 6))
1792 if(!strncmp(str, "<minus>", 7))
1794 syntaxerror("internal error (347)");
1797 int getSign(char*str)
1799 if(!strncmp(str, "<plus>", 6))
1801 if(!strncmp(str, "<minus>", 7))
1803 syntaxerror("internal error (348)");
1806 static dictionary_t points;
1807 static mem_t mpoints;
1808 int points_initialized = 0;
1810 SPOINT getPoint(SRECT r, char*name)
1813 if(!strcmp(name, "center")) {
1815 p.x = (r.xmin + r.xmax)/2;
1816 p.y = (r.ymin + r.ymax)/2;
1820 if(points_initialized)
1821 l = (int)dictionary_lookup(&points, name);
1823 syntaxerror("Invalid point: \"%s\".", name);
1826 return *(SPOINT*)&mpoints.buffer[l];
1828 static int c_gradient(map_t*args)
1830 char*name = lu(args, "name");
1831 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1835 syntaxerror("colon (:) expected");
1837 s_gradient(name, text, radial);
1840 static int c_point(map_t*args)
1842 char*name = lu(args, "name");
1846 if(!points_initialized) {
1847 dictionary_init(&points);
1849 points_initialized = 1;
1851 p.x = parseTwip(lu(args, "x"));
1852 p.y = parseTwip(lu(args, "y"));
1853 pos = mem_put(&mpoints, &p, sizeof(p));
1854 string_set(&s1, name);
1856 dictionary_put(&points, s1, (void*)pos);
1859 static int c_play(map_t*args)
1861 char*name = lu(args, "name");
1862 char*loop = lu(args, "loop");
1863 char*nomultiple = lu(args, "nomultiple");
1865 if(!strcmp(nomultiple, "nomultiple"))
1868 nm = parseInt(nomultiple);
1870 if(s_playsound(name, parseInt(loop), nm, 0)) {
1872 } else if(s_swf3action(name, "play")) {
1878 static int c_stop(map_t*args)
1880 char*name = lu(args, "name");
1882 if(s_playsound(name, 0,0,1)) {
1884 } else if(s_swf3action(name, "stop")) {
1887 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1891 static int c_nextframe(map_t*args)
1893 char*name = lu(args, "name");
1895 if(s_swf3action(name, "nextframe")) {
1898 syntaxerror("I don't know anything about movie \"%s\"", name);
1902 static int c_previousframe(map_t*args)
1904 char*name = lu(args, "name");
1906 if(s_swf3action(name, "previousframe")) {
1909 syntaxerror("I don't know anything about movie \"%s\"", name);
1913 static int c_placement(map_t*args, int type)
1915 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1918 char* luminancestr = lu(args, "luminance");
1919 char* scalestr = lu(args, "scale");
1920 char* scalexstr = lu(args, "scalex");
1921 char* scaleystr = lu(args, "scaley");
1922 char* rotatestr = lu(args, "rotate");
1923 char* shearstr = lu(args, "shear");
1924 char* xstr="", *pivotstr="";
1925 char* ystr="", *anglestr="";
1926 char*above = lu(args, "above"); /*FIXME*/
1927 char*below = lu(args, "below");
1928 char* rstr = lu(args, "red");
1929 char* gstr = lu(args, "green");
1930 char* bstr = lu(args, "blue");
1931 char* astr = lu(args, "alpha");
1932 char* pinstr = lu(args, "pin");
1933 char* as = map_lookup(args, "as");
1941 if(type==9) { // (?) .rotate or .arcchange
1942 pivotstr = lu(args, "pivot");
1943 anglestr = lu(args, "angle");
1945 xstr = lu(args, "x");
1946 ystr = lu(args, "y");
1949 luminance = parseMulAdd(luminancestr);
1952 luminance.mul = 256;
1956 if(scalexstr[0]||scaleystr[0])
1957 syntaxerror("scalex/scaley and scale cannot both be set");
1958 scalexstr = scaleystr = scalestr;
1961 if(type == 0 || type == 4) {
1963 character = lu(args, "character");
1964 parameters_clear(&p);
1965 } else if (type == 5) {
1966 character = lu(args, "name");
1967 parameters_clear(&p);
1970 p = s_getParameters(instance);
1975 if(isRelative(xstr)) {
1976 if(type == 0 || type == 4)
1977 syntaxerror("relative x values not allowed for initial put or startclip");
1978 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1980 p.x = parseTwip(xstr);
1984 if(isRelative(ystr)) {
1985 if(type == 0 || type == 4)
1986 syntaxerror("relative y values not allowed for initial put or startclip");
1987 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1989 p.y = parseTwip(ystr);
1993 /* scale, scalex, scaley */
1995 oldbbox = s_getCharBBox(character);
1997 oldbbox = s_getInstanceBBox(instance);
1999 oldwidth = oldbbox.xmax - oldbbox.xmin;
2000 oldheight = oldbbox.ymax - oldbbox.ymin;
2002 if(oldwidth==0) p.scalex = 1.0;
2005 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2009 if(oldheight==0) p.scaley = 1.0;
2012 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2018 if(isRelative(rotatestr)) {
2019 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2021 p.rotate = parseFloat(rotatestr);
2027 if(isRelative(shearstr)) {
2028 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2030 p.shear = parseFloat(shearstr);
2035 if(isPoint(pivotstr))
2036 p.pivot = parsePoint(pivotstr);
2038 p.pivot = getPoint(oldbbox, pivotstr);
2042 p.pin = parsePoint(pinstr);
2044 p.pin = getPoint(oldbbox, pinstr);
2047 /* color transform */
2049 if(rstr[0] || luminancestr[0]) {
2052 r = parseMulAdd(rstr);
2054 r.add = p.cxform.r0;
2055 r.mul = p.cxform.r1;
2057 r = mergeMulAdd(r, luminance);
2058 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2060 if(gstr[0] || luminancestr[0]) {
2063 g = parseMulAdd(gstr);
2065 g.add = p.cxform.g0;
2066 g.mul = p.cxform.g1;
2068 g = mergeMulAdd(g, luminance);
2069 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2071 if(bstr[0] || luminancestr[0]) {
2074 b = parseMulAdd(bstr);
2076 b.add = p.cxform.b0;
2077 b.mul = p.cxform.b1;
2079 b = mergeMulAdd(b, luminance);
2080 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2083 MULADD a = parseMulAdd(astr);
2084 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2088 s_put(instance, character, p);
2090 s_change(instance, p);
2092 s_qchange(instance, p);
2094 s_jump(instance, p);
2096 s_startclip(instance, character, p);
2097 else if(type == 5) {
2099 s_buttonput(character, as, p);
2101 s_buttonput(character, "shape", p);
2106 static int c_put(map_t*args)
2108 c_placement(args, 0);
2111 static int c_change(map_t*args)
2113 c_placement(args, 1);
2116 static int c_qchange(map_t*args)
2118 c_placement(args, 2);
2121 static int c_arcchange(map_t*args)
2123 c_placement(args, 2);
2126 static int c_jump(map_t*args)
2128 c_placement(args, 3);
2131 static int c_startclip(map_t*args)
2133 c_placement(args, 4);
2136 static int c_show(map_t*args)
2138 c_placement(args, 5);
2141 static int c_del(map_t*args)
2143 char*instance = lu(args, "name");
2144 s_delinstance(instance);
2147 static int c_end(map_t*args)
2152 static int c_sprite(map_t*args)
2154 char* name = lu(args, "name");
2158 static int c_frame(map_t*args)
2160 char*framestr = lu(args, "n");
2161 char*cutstr = lu(args, "cut");
2162 char*name = lu(args, "name");
2165 if(strcmp(cutstr, "no"))
2167 if(isRelative(framestr)) {
2168 frame = s_getframe();
2169 if(getSign(framestr)<0)
2170 syntaxerror("relative frame expressions must be positive");
2171 frame += parseInt(getOffset(framestr));
2174 frame = parseInt(framestr);
2175 if(s_getframe() >= frame
2176 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2177 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2179 s_frame(frame, cut, name);
2182 static int c_primitive(map_t*args)
2184 char*name = lu(args, "name");
2185 char*command = lu(args, "commandname");
2186 int width=0, height=0, r=0;
2187 int linewidth = parseTwip(lu(args, "line"));
2188 char*colorstr = lu(args, "color");
2189 RGBA color = parseColor(colorstr);
2190 char*fillstr = lu(args, "fill");
2197 if(!strcmp(command, "circle"))
2199 else if(!strcmp(command, "filled"))
2203 width = parseTwip(lu(args, "width"));
2204 height = parseTwip(lu(args, "height"));
2205 } else if (type==1) {
2206 r = parseTwip(lu(args, "r"));
2207 } else if (type==2) {
2208 outline = lu(args, "outline");
2211 if(!strcmp(fillstr, "fill"))
2213 if(!strcmp(fillstr, "none"))
2215 if(width<0 || height<0 || linewidth<0 || r<0)
2216 syntaxerror("values width, height, line, r must be positive");
2218 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2219 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2220 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2224 static int c_textshape(map_t*args)
2226 char*name = lu(args, "name");
2227 char*text = lu(args, "text");
2228 char*font = lu(args, "font");
2229 float size = parsePercent(lu(args, "size"));
2231 s_textshape(name, font, size, text);
2235 static int c_swf(map_t*args)
2237 char*name = lu(args, "name");
2238 char*filename = lu(args, "filename");
2239 char*command = lu(args, "commandname");
2240 if(!strcmp(command, "shape"))
2241 warning("Please use .swf instead of .shape");
2242 s_includeswf(name, filename);
2246 static int c_font(map_t*args)
2248 char*name = lu(args, "name");
2249 char*filename = lu(args, "filename");
2250 s_font(name, filename);
2254 static int c_sound(map_t*args)
2256 char*name = lu(args, "name");
2257 char*filename = lu(args, "filename");
2258 s_sound(name, filename);
2262 static int c_text(map_t*args)
2264 char*name = lu(args, "name");
2265 char*text = lu(args, "text");
2266 char*font = lu(args, "font");
2267 float size = parsePercent(lu(args, "size"));
2268 RGBA color = parseColor(lu(args, "color"));
2269 s_text(name, font, text, (int)(size*100), color);
2273 static int c_soundtrack(map_t*args)
2278 static int c_image(map_t*args)
2280 char*command = lu(args, "commandname");
2281 char*name = lu(args, "name");
2282 char*filename = lu(args, "filename");
2283 if(!strcmp(command,"jpeg")) {
2284 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2285 s_image(name, "jpeg", filename, quality);
2287 s_image(name, "png", filename, 0);
2292 static int c_outline(map_t*args)
2294 char*name = lu(args, "name");
2295 char*format = lu(args, "format");
2299 syntaxerror("colon (:) expected");
2301 s_outline(name, format, text);
2305 int fakechar(map_t*args)
2307 char*name = lu(args, "name");
2308 s_box(name, 0, 0, black, 20, 0);
2312 static int c_egon(map_t*args) {return fakechar(args);}
2313 static int c_button(map_t*args) {
2314 char*name = lu(args, "name");
2318 static int current_button_flags = 0;
2319 static int c_on_press(map_t*args)
2321 char*position = lu(args, "position");
2323 if(!strcmp(position, "inside")) {
2324 current_button_flags |= BC_OVERUP_OVERDOWN;
2325 } else if(!strcmp(position, "outside")) {
2326 //current_button_flags |= BC_IDLE_OUTDOWN;
2327 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2328 } else if(!strcmp(position, "anywhere")) {
2329 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2332 if(type == RAWDATA) {
2334 s_buttonaction(current_button_flags, action);
2335 current_button_flags = 0;
2341 static int c_on_release(map_t*args)
2343 char*position = lu(args, "position");
2345 if(!strcmp(position, "inside")) {
2346 current_button_flags |= BC_OVERDOWN_OVERUP;
2347 } else if(!strcmp(position, "outside")) {
2348 current_button_flags |= BC_OUTDOWN_IDLE;
2349 } else if(!strcmp(position, "anywhere")) {
2350 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2353 if(type == RAWDATA) {
2355 s_buttonaction(current_button_flags, action);
2356 current_button_flags = 0;
2362 static int c_on_move_in(map_t*args)
2364 char*position = lu(args, "state");
2366 if(!strcmp(position, "pressed")) {
2367 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2368 } else if(!strcmp(position, "not_pressed")) {
2369 current_button_flags |= BC_IDLE_OVERUP;
2370 } else if(!strcmp(position, "any")) {
2371 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2374 if(type == RAWDATA) {
2376 s_buttonaction(current_button_flags, action);
2377 current_button_flags = 0;
2383 static int c_on_move_out(map_t*args)
2385 char*position = lu(args, "state");
2387 if(!strcmp(position, "pressed")) {
2388 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2389 } else if(!strcmp(position, "not_pressed")) {
2390 current_button_flags |= BC_OVERUP_IDLE;
2391 } else if(!strcmp(position, "any")) {
2392 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2395 if(type == RAWDATA) {
2397 s_buttonaction(current_button_flags, action);
2398 current_button_flags = 0;
2404 static int c_on_key(map_t*args)
2406 char*key = lu(args, "key");
2408 if(strlen(key)==1) {
2411 current_button_flags |= 0x4000 + (key[0]*0x200);
2413 syntaxerror("invalid character: %c"+key[0]);
2418 <ctrl-x> = 0x200*(x-'a')
2422 syntaxerror("invalid key: %s",key);
2425 if(type == RAWDATA) {
2427 s_buttonaction(current_button_flags, action);
2428 current_button_flags = 0;
2435 static int c_edittext(map_t*args)
2437 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2438 char*name = lu(args, "name");
2439 char*font = lu(args, "font");
2440 int size = (int)(1024*parsePercent(lu(args, "size")));
2441 int width = parseTwip(lu(args, "width"));
2442 int height = parseTwip(lu(args, "height"));
2443 char*text = lu(args, "text");
2444 RGBA color = parseColor(lu(args, "color"));
2445 int maxlength = parseInt(lu(args, "maxlength"));
2446 char*variable = lu(args, "variable");
2447 char*passwordstr = lu(args, "password");
2448 char*wordwrapstr = lu(args, "wordwrap");
2449 char*multilinestr = lu(args, "multiline");
2450 char*htmlstr = lu(args, "html");
2451 char*noselectstr = lu(args, "noselect");
2452 char*readonlystr = lu(args, "readonly");
2453 char*borderstr = lu(args, "border");
2456 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2457 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2458 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2459 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2460 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2461 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2462 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2464 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2468 static int c_morphshape(map_t*args) {return fakechar(args);}
2469 static int c_movie(map_t*args) {return fakechar(args);}
2471 static int c_texture(map_t*args) {return 0;}
2473 static int c_action(map_t*args)
2476 if(type != RAWDATA) {
2477 syntaxerror("colon (:) expected");
2487 command_func_t* func;
2490 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2491 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2492 // "import" type stuff
2493 {"swf", c_swf, "name filename"},
2494 {"shape", c_swf, "name filename"},
2495 {"jpeg", c_image, "name filename quality=80%"},
2496 {"png", c_image, "name filename"},
2497 {"movie", c_movie, "name filename"},
2498 {"sound", c_sound, "name filename"},
2499 {"font", c_font, "name filename"},
2500 {"soundtrack", c_soundtrack, "filename"},
2502 // generators of primitives
2504 {"point", c_point, "name x=0 y=0"},
2505 {"gradient", c_gradient, "name @radial=0"},
2506 {"outline", c_outline, "name format=simple"},
2507 {"textshape", c_textshape, "name font size=100% text"},
2509 // character generators
2510 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2511 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2512 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2514 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2515 {"text", c_text, "name text font size=100% color=white"},
2516 {"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"},
2517 {"morphshape", c_morphshape, "name start end"},
2518 {"button", c_button, "name"},
2519 {"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="},
2520 {"on_press", c_on_press, "position=inside"},
2521 {"on_release", c_on_release, "position=anywhere"},
2522 {"on_move_in", c_on_move_in, "state=not_pressed"},
2523 {"on_move_out", c_on_move_out, "state=not_pressed"},
2524 {"on_key", c_on_key, "key=any"},
2527 {"play", c_play, "name loop=0 @nomultiple=0"},
2528 {"stop", c_stop, "name"},
2529 {"nextframe", c_nextframe, "name"},
2530 {"previousframe", c_previousframe, "name"},
2532 // object placement tags
2533 {"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="},
2534 {"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="},
2535 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2536 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2537 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2538 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2539 {"del", c_del, "name"},
2540 // virtual object placement
2541 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2543 // commands which start a block
2544 //startclip (see above)
2545 {"sprite", c_sprite, "name"},
2546 {"action", c_action, ""},
2552 static map_t parseArguments(char*command, char*pattern)
2568 string_set(&t1, "commandname");
2569 string_set(&t2, command);
2570 map_put(&result, t1, t2);
2572 if(!pattern || !*pattern)
2579 if(!strncmp("<i> ", x, 3)) {
2581 if(type == COMMAND || type == RAWDATA) {
2583 syntaxerror("character name expected");
2585 name[pos].str = "instance";
2587 value[pos].str = text;
2588 value[pos].len = strlen(text);
2592 if(type == ASSIGNMENT)
2595 name[pos].str = "character";
2597 value[pos].str = text;
2598 value[pos].len = strlen(text);
2606 isboolean[pos] = (x[0] =='@');
2619 name[pos].len = d-x;
2624 name[pos].len = e-x;
2625 value[pos].str = e+1;
2626 value[pos].len = d-e-1;
2634 /* for(t=0;t<len;t++) {
2635 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2636 isboolean[t]?"(boolean)":"");
2641 if(type == RAWDATA || type == COMMAND) {
2646 // first, search for boolean arguments
2647 for(pos=0;pos<len;pos++)
2649 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2651 if(type == ASSIGNMENT)
2653 value[pos].str = text;
2654 value[pos].len = strlen(text);
2655 /*printf("setting boolean parameter %s (to %s)\n",
2656 strdup_n(name[pos], namelen[pos]),
2657 strdup_n(value[pos], valuelen[pos]));*/
2662 // second, search for normal arguments
2664 for(pos=0;pos<len;pos++)
2666 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2667 (type != ASSIGNMENT && !set[pos])) {
2669 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2671 if(type == ASSIGNMENT)
2674 value[pos].str = text;
2675 value[pos].len = strlen(text);
2677 printf("setting parameter %s (to %s)\n",
2678 strdup_n(name[pos].str, name[pos].len),
2679 strdup_n(value[pos].str, value[pos].len));
2685 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2689 for(t=0;t<len;t++) {
2690 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2693 for(t=0;t<len;t++) {
2694 if(value[t].str && value[t].str[0] == '*') {
2695 //relative default- take value from some other parameter
2697 for(s=0;s<len;s++) {
2698 if(value[s].len == value[t].len-1 &&
2699 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2700 value[t].str = value[s].str;
2703 if(value[t].str == 0) {
2705 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2709 /* ok, now construct the dictionary from the parameters */
2713 map_put(&result, name[t], value[t]);
2717 static void parseArgumentsForCommand(char*command)
2722 msg("<verbose> parse Command: %s (line %d)", command, line);
2724 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2725 if(!strcmp(arguments[t].command, command)) {
2727 /* ugly hack- will be removed soon (once documentation and .sc generating
2728 utilities have been changed) */
2729 if(!strcmp(command, "swf") && !stackpos) {
2730 warning("Please use .flash instead of .swf- this will be mandatory soon");
2735 args = parseArguments(command, arguments[t].arguments);
2741 syntaxerror("command %s not known", command);
2743 // catch missing .flash directives at the beginning of a file
2744 if(strcmp(command, "flash") && !stackpos)
2746 syntaxerror("No movie defined- use .flash first");
2750 printf(".%s\n", command);fflush(stdout);
2751 map_dump(&args, stdout, "\t");fflush(stdout);
2754 (*arguments[nr].func)(&args);
2756 /*if(!strcmp(command, "button") ||
2757 !strcmp(command, "action")) {
2760 if(type == COMMAND) {
2761 if(!strcmp(text, "end"))
2775 int main (int argc,char ** argv)
2778 processargs(argc, argv);
2779 initLog(0,-1,0,0,-1,verbose);
2782 args_callback_usage(argv[0]);
2786 file = generateTokens(filename);
2788 printf("parser returned error.\n");
2794 while(!noMoreTokens()) {
2797 syntaxerror("command expected");
2798 parseArgumentsForCommand(text);