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_DEFINESHAPE);
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_DEFINESHAPE);
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_DEFINESHAPE);
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);
1026 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1027 swf_FontSetDefine2(tag, font);
1030 if(dictionary_lookup(&fonts, name))
1031 syntaxerror("font %s defined twice", name);
1032 dictionary_put2(&fonts, name, font);
1037 typedef struct _sound_t
1043 void s_sound(char*name, char*filename)
1045 struct WAV wav, wav2;
1050 if(!readWAV(filename, &wav)) {
1051 warning("Couldn't read wav file \"%s\"", filename);
1055 convertWAV2mono(&wav, &wav2, 44100);
1056 samples = (U16*)wav2.data;
1057 numsamples = wav2.size/2;
1061 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1062 swf_SetU16(tag, id); //id
1063 swf_SetSoundDefine(tag, samples, numsamples);
1065 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1069 if(dictionary_lookup(&sounds, name))
1070 syntaxerror("sound %s defined twice", name);
1071 dictionary_put2(&sounds, name, sound);
1079 static char* gradient_getToken(const char**p)
1083 while(**p && strchr(" \t\n\r", **p)) {
1087 while(**p && !strchr(" \t\n\r", **p)) {
1090 result = malloc((*p)-start+1);
1091 memcpy(result,start,(*p)-start+1);
1092 result[(*p)-start] = 0;
1096 float parsePercent(char*str);
1097 RGBA parseColor(char*str);
1099 GRADIENT parseGradient(const char*str)
1102 const char* p = str;
1103 memset(&gradient, 0, sizeof(GRADIENT));
1105 char*posstr,*colorstr;
1108 posstr = gradient_getToken(&p);
1111 pos = parsePercent(posstr);
1112 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1113 colorstr = gradient_getToken(&p);
1114 color = parseColor(colorstr);
1115 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1116 warning("gradient record too big- max size is 8, rest ignored");
1119 gradient.ratios[gradient.num] = (int)(pos*255.0);
1120 gradient.rgba[gradient.num] = color;
1128 void s_gradient(char*name, const char*text, int radial)
1130 gradient_t* gradient;
1131 gradient = malloc(sizeof(gradient_t));
1132 memset(gradient, 0, sizeof(gradient_t));
1133 gradient->gradient = parseGradient(text);
1134 gradient->radial = radial;
1136 if(dictionary_lookup(&gradients, name))
1137 syntaxerror("gradient %s defined twice", name);
1138 dictionary_put2(&gradients, name, gradient);
1141 void s_action(const char*text)
1144 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1146 syntaxerror("Couldn't compile ActionScript");
1149 tag = swf_InsertTag(tag, ST_DOACTION);
1151 swf_ActionSet(tag, a);
1156 int s_swf3action(char*name, char*action)
1159 instance_t* object = dictionary_lookup(&instances, name);
1163 a = action_SetTarget(0, name);
1164 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1165 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1166 else if(!strcmp(action, "stop")) a = action_Stop(a);
1167 else if(!strcmp(action, "play")) a = action_Play(a);
1168 a = action_SetTarget(a, "");
1171 tag = swf_InsertTag(tag, ST_DOACTION);
1172 swf_ActionSet(tag, a);
1177 void s_outline(char*name, char*format, char*source)
1186 swf_Shape11DrawerInit(&draw, 0);
1187 draw_string(&draw, source);
1189 shape = swf_ShapeDrawerToShape(&draw);
1190 //shape2 = swf_ShapeToShape2(shape);
1191 //bounds = swf_GetShapeBoundingBox(shape2);
1192 //swf_Shape2Free(shape2);
1193 bounds = swf_ShapeDrawerGetBBox(&draw);
1194 draw.dealloc(&draw);
1196 outline = (outline_t*)malloc(sizeof(outline_t));
1197 memset(outline, 0, sizeof(outline_t));
1198 outline->shape = shape;
1199 outline->bbox = bounds;
1201 if(dictionary_lookup(&outlines, name))
1202 syntaxerror("outline %s defined twice", name);
1203 dictionary_put2(&outlines, name, outline);
1206 int s_playsound(char*name, int loops, int nomultiple, int stop)
1208 sound_t* sound = dictionary_lookup(&sounds, name);
1213 tag = swf_InsertTag(tag, ST_STARTSOUND);
1214 swf_SetU16(tag, sound->id); //id
1215 memset(&info, 0, sizeof(info));
1218 info.nomultiple = nomultiple;
1219 swf_SetSoundInfo(tag, &info);
1223 void s_includeswf(char*name, char*filename)
1231 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1232 f = open(filename,O_RDONLY);
1234 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1235 s_box(name, 0, 0, black, 20, 0);
1238 if (swf_ReadSWF(f,&swf)<0) {
1239 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1240 s_box(name, 0, 0, black, 20, 0);
1245 /* FIXME: The following sets the bounding Box for the character.
1246 It is wrong for two reasons:
1247 a) It may be too small (in case objects in the movie clip at the borders)
1248 b) it may be too big (because the poor movie never got autocropped)
1252 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1253 swf_SetU16(tag, id);
1256 swf_Relocate(&swf, idmap);
1258 ftag = swf.firstTag;
1262 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1263 if(cutout[t] == ftag->id) {
1267 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1269 if(ftag->id == ST_END)
1273 /* We simply dump all tags right after the sprite
1274 header, relying on the fact that swf_OptimizeTagOrder() will
1275 sort things out for us later.
1276 We also rely on the fact that the imported SWF is well-formed.
1278 tag = swf_InsertTag(tag, ftag->id);
1279 swf_SetBlock(tag, ftag->data, ftag->len);
1283 syntaxerror("Included file %s contains errors", filename);
1284 tag = swf_InsertTag(tag, ST_END);
1288 s_addcharacter(name, id, tag, r);
1291 SRECT s_getCharBBox(char*name)
1293 character_t* c = dictionary_lookup(&characters, name);
1294 if(!c) syntaxerror("character '%s' unknown(2)", name);
1297 SRECT s_getInstanceBBox(char*name)
1299 instance_t * i = dictionary_lookup(&instances, name);
1301 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1303 if(!c) syntaxerror("internal error(5)");
1306 parameters_t s_getParameters(char*name)
1308 instance_t * i = dictionary_lookup(&instances, name);
1309 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1310 return i->parameters;
1312 void s_startclip(char*instance, char*character, parameters_t p)
1314 character_t* c = dictionary_lookup(&characters, character);
1318 syntaxerror("character %s not known", character);
1320 i = s_addinstance(instance, c, currentdepth);
1322 m = s_instancepos(i->character->size, &p);
1324 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1325 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1326 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1328 i->lastFrame= currentframe;
1330 stack[stackpos].tag = tag;
1331 stack[stackpos].type = 2;
1340 swf_SetTagPos(stack[stackpos].tag, 0);
1341 swf_GetPlaceObject(stack[stackpos].tag, &p);
1342 p.clipdepth = currentdepth;
1344 swf_ClearTag(stack[stackpos].tag);
1345 swf_SetPlaceObject(stack[stackpos].tag, &p);
1349 void s_put(char*instance, char*character, parameters_t p)
1351 character_t* c = dictionary_lookup(&characters, character);
1355 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1358 i = s_addinstance(instance, c, currentdepth);
1360 m = s_instancepos(i->character->size, &p);
1362 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1363 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1365 i->lastFrame = currentframe;
1369 void s_jump(char*instance, parameters_t p)
1371 instance_t* i = dictionary_lookup(&instances, instance);
1374 syntaxerror("instance %s not known", instance);
1378 m = s_instancepos(i->character->size, &p);
1380 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1381 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1383 i->lastFrame = currentframe;
1386 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1390 if(num==0 || num==1)
1392 ratio = (float)pos/(float)num;
1394 p.x = (p2->x-p1->x)*ratio + p1->x;
1395 p.y = (p2->y-p1->y)*ratio + p1->y;
1396 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1397 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1398 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1399 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1401 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1402 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1403 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1404 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1406 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1407 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1408 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1409 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1411 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1412 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1413 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1414 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1418 void s_change(char*instance, parameters_t p2)
1420 instance_t* i = dictionary_lookup(&instances, instance);
1424 int frame, allframes;
1426 syntaxerror("instance %s not known", instance);
1430 allframes = currentframe - i->lastFrame - 1;
1432 warning(".change ignored. can only .put/.change an object once per frame.");
1436 m = s_instancepos(i->character->size, &p2);
1437 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1438 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1441 /* o.k., we got the start and end point set. Now iterate though all the
1442 tags in between, inserting object changes after each new frame */
1445 if(!t) syntaxerror("internal error(6)");
1447 while(frame < allframes) {
1448 if(t->id == ST_SHOWFRAME) {
1453 p = s_interpolate(&p1, &p2, frame, allframes);
1454 m = s_instancepos(i->character->size, &p); //needed?
1455 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1456 i->lastFrame = currentframe;
1457 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1459 if(frame == allframes)
1464 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1468 void s_delinstance(char*instance)
1470 instance_t* i = dictionary_lookup(&instances, instance);
1472 syntaxerror("instance %s not known", instance);
1474 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1475 swf_SetU16(tag, i->depth);
1476 dictionary_del(&instances, instance);
1479 void s_qchange(char*instance, parameters_t p)
1486 syntaxerror(".end unexpected");
1487 if(stack[stackpos-1].type == 0)
1489 else if(stack[stackpos-1].type == 1)
1491 else if(stack[stackpos-1].type == 2)
1493 else if(stack[stackpos-1].type == 3)
1495 else syntaxerror("internal error 1");
1498 // ------------------------------------------------------------------------
1500 typedef int command_func_t(map_t*args);
1502 SRECT parseBox(char*str)
1505 float xmin, xmax, ymin, ymax;
1506 char*x = strchr(str, 'x');
1508 if(!strcmp(str, "autocrop")) {
1509 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1513 d1 = strchr(x+1, ':');
1515 d2 = strchr(d1+1, ':');
1517 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1521 else if(d1 && !d2) {
1522 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1528 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1533 r.xmin = (SCOORD)(xmin*20);
1534 r.ymin = (SCOORD)(ymin*20);
1535 r.xmax = (SCOORD)(xmax*20);
1536 r.ymax = (SCOORD)(ymax*20);
1539 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1542 float parseFloat(char*str)
1546 int parseInt(char*str)
1551 if(str[0]=='+' || str[0]=='-')
1555 if(str[t]<'0' || str[t]>'9')
1556 syntaxerror("Not an Integer: \"%s\"", str);
1559 int parseTwip(char*str)
1563 if(str[0]=='+' || str[0]=='-') {
1568 dot = strchr(str, '.');
1572 return sign*parseInt(str)*20;
1574 int l=strlen(++dot);
1576 for(s=str;s<dot-1;s++)
1577 if(*s<'0' || *s>'9')
1578 syntaxerror("Not a coordinate: \"%s\"", str);
1580 if(*s<'0' || *s>'9')
1581 syntaxerror("Not a coordinate: \"%s\"", str);
1583 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1584 warning("precision loss: %s converted to twip", str);
1589 return sign*atoi(str)*20;
1591 return sign*atoi(str)*20+atoi(dot)*2;
1593 return sign*atoi(str)*20+atoi(dot)/5;
1598 int isPoint(char*str)
1600 if(strchr(str, '('))
1606 SPOINT parsePoint(char*str)
1610 int l = strlen(str);
1611 char*comma = strchr(str, ',');
1612 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1613 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1614 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1615 p.x = parseTwip(tmp);
1616 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1617 p.y = parseTwip(tmp);
1621 int parseColor2(char*str, RGBA*color)
1623 int l = strlen(str);
1627 struct {unsigned char r,g,b;char*name;} colors[] =
1628 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1629 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1630 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1631 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1632 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1633 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1634 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1635 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1636 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1637 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1638 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1639 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1643 if(str[0]=='#' && (l==7 || l==9)) {
1644 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1646 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1648 color->r = r; color->g = g; color->b = b; color->a = a;
1651 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1652 if(!strcmp(str, colors[t].name)) {
1657 color->r = r; color->g = g; color->b = b; color->a = a;
1663 RGBA parseColor(char*str)
1666 if(!parseColor2(str, &c))
1667 syntaxerror("Expression '%s' is not a color", str);
1671 typedef struct _muladd {
1676 MULADD parseMulAdd(char*str)
1679 char* str2 = (char*)malloc(strlen(str)+5);
1686 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1687 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1688 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1689 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1690 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1691 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1692 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1693 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1694 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1695 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1697 syntaxerror("'%s' is not a valid color transform expression", str);
1699 m.add = (int)(add*256);
1700 m.mul = (int)(mul*256);
1705 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1707 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1708 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1710 if(a<-32768) a=-32768;
1711 if(a>32767) a=32767;
1712 if(m<-32768) m=-32768;
1713 if(m>32767) m=32767;
1719 float parsePercent(char*str)
1721 int l = strlen(str);
1725 return atoi(str)/100.0;
1727 syntaxerror("Expression '%s' is not a percentage", str);
1730 int isPercent(char*str)
1732 return str[strlen(str)-1]=='%';
1734 int parseNewSize(char*str, int size)
1737 return parsePercent(str)*size;
1739 return (int)(atof(str)*20);
1742 int isColor(char*str)
1745 return parseColor2(str, &c);
1748 static char* lu(map_t* args, char*name)
1750 char* value = map_lookup(args, name);
1752 map_dump(args, stdout, "");
1753 syntaxerror("internal error 2: value %s should be set", name);
1758 static int c_flash(map_t*args)
1760 char* name = lu(args, "name");
1761 char* compressstr = lu(args, "compress");
1762 SRECT bbox = parseBox(lu(args, "bbox"));
1763 int version = parseInt(lu(args, "version"));
1764 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1766 RGBA color = parseColor(lu(args, "background"));
1767 if(!strcmp(name, "!default!") || override_outputname)
1770 if(!strcmp(compressstr, "default"))
1771 compress = version==6;
1772 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1774 else if(!strcmp(compressstr, "no"))
1776 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1778 s_swf(name, bbox, version, fps, compress, color);
1781 int isRelative(char*str)
1783 return !strncmp(str, "<plus>", 6) ||
1784 !strncmp(str, "<minus>", 7);
1786 char* getOffset(char*str)
1788 if(!strncmp(str, "<plus>", 6))
1790 if(!strncmp(str, "<minus>", 7))
1792 syntaxerror("internal error (347)");
1795 int getSign(char*str)
1797 if(!strncmp(str, "<plus>", 6))
1799 if(!strncmp(str, "<minus>", 7))
1801 syntaxerror("internal error (348)");
1804 static dictionary_t points;
1805 static mem_t mpoints;
1806 int points_initialized = 0;
1808 SPOINT getPoint(SRECT r, char*name)
1811 if(!strcmp(name, "center")) {
1813 p.x = (r.xmin + r.xmax)/2;
1814 p.y = (r.ymin + r.ymax)/2;
1818 if(points_initialized)
1819 l = (int)dictionary_lookup(&points, name);
1821 syntaxerror("Invalid point: \"%s\".", name);
1824 return *(SPOINT*)&mpoints.buffer[l];
1826 static int c_gradient(map_t*args)
1828 char*name = lu(args, "name");
1829 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1833 syntaxerror("colon (:) expected");
1835 s_gradient(name, text, radial);
1838 static int c_point(map_t*args)
1840 char*name = lu(args, "name");
1844 if(!points_initialized) {
1845 dictionary_init(&points);
1847 points_initialized = 1;
1849 p.x = parseTwip(lu(args, "x"));
1850 p.y = parseTwip(lu(args, "y"));
1851 pos = mem_put(&mpoints, &p, sizeof(p));
1852 string_set(&s1, name);
1854 dictionary_put(&points, s1, (void*)pos);
1857 static int c_play(map_t*args)
1859 char*name = lu(args, "name");
1860 char*loop = lu(args, "loop");
1861 char*nomultiple = lu(args, "nomultiple");
1863 if(!strcmp(nomultiple, "nomultiple"))
1866 nm = parseInt(nomultiple);
1868 if(s_playsound(name, parseInt(loop), nm, 0)) {
1870 } else if(s_swf3action(name, "play")) {
1876 static int c_stop(map_t*args)
1878 char*name = lu(args, "name");
1880 if(s_playsound(name, 0,0,1)) {
1882 } else if(s_swf3action(name, "stop")) {
1885 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1889 static int c_nextframe(map_t*args)
1891 char*name = lu(args, "name");
1893 if(s_swf3action(name, "nextframe")) {
1896 syntaxerror("I don't know anything about movie \"%s\"", name);
1900 static int c_previousframe(map_t*args)
1902 char*name = lu(args, "name");
1904 if(s_swf3action(name, "previousframe")) {
1907 syntaxerror("I don't know anything about movie \"%s\"", name);
1911 static int c_placement(map_t*args, int type)
1913 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1916 char* luminancestr = lu(args, "luminance");
1917 char* scalestr = lu(args, "scale");
1918 char* scalexstr = lu(args, "scalex");
1919 char* scaleystr = lu(args, "scaley");
1920 char* rotatestr = lu(args, "rotate");
1921 char* shearstr = lu(args, "shear");
1922 char* xstr="", *pivotstr="";
1923 char* ystr="", *anglestr="";
1924 char*above = lu(args, "above"); /*FIXME*/
1925 char*below = lu(args, "below");
1926 char* rstr = lu(args, "red");
1927 char* gstr = lu(args, "green");
1928 char* bstr = lu(args, "blue");
1929 char* astr = lu(args, "alpha");
1930 char* pinstr = lu(args, "pin");
1931 char* as = map_lookup(args, "as");
1939 if(type==9) { // (?) .rotate or .arcchange
1940 pivotstr = lu(args, "pivot");
1941 anglestr = lu(args, "angle");
1943 xstr = lu(args, "x");
1944 ystr = lu(args, "y");
1947 luminance = parseMulAdd(luminancestr);
1950 luminance.mul = 256;
1954 if(scalexstr[0]||scaleystr[0])
1955 syntaxerror("scalex/scaley and scale cannot both be set");
1956 scalexstr = scaleystr = scalestr;
1959 if(type == 0 || type == 4) {
1961 character = lu(args, "character");
1962 parameters_clear(&p);
1963 } else if (type == 5) {
1964 character = lu(args, "name");
1965 parameters_clear(&p);
1968 p = s_getParameters(instance);
1973 if(isRelative(xstr)) {
1974 if(type == 0 || type == 4)
1975 syntaxerror("relative x values not allowed for initial put or startclip");
1976 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1978 p.x = parseTwip(xstr);
1982 if(isRelative(ystr)) {
1983 if(type == 0 || type == 4)
1984 syntaxerror("relative y values not allowed for initial put or startclip");
1985 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1987 p.y = parseTwip(ystr);
1991 /* scale, scalex, scaley */
1993 oldbbox = s_getCharBBox(character);
1995 oldbbox = s_getInstanceBBox(instance);
1997 oldwidth = oldbbox.xmax - oldbbox.xmin;
1998 oldheight = oldbbox.ymax - oldbbox.ymin;
2000 if(oldwidth==0) p.scalex = 1.0;
2003 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2007 if(oldheight==0) p.scaley = 1.0;
2010 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2016 if(isRelative(rotatestr)) {
2017 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2019 p.rotate = parseFloat(rotatestr);
2025 if(isRelative(shearstr)) {
2026 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2028 p.shear = parseFloat(shearstr);
2033 if(isPoint(pivotstr))
2034 p.pivot = parsePoint(pivotstr);
2036 p.pivot = getPoint(oldbbox, pivotstr);
2040 p.pin = parsePoint(pinstr);
2042 p.pin = getPoint(oldbbox, pinstr);
2045 /* color transform */
2047 if(rstr[0] || luminancestr[0]) {
2050 r = parseMulAdd(rstr);
2052 r.add = p.cxform.r0;
2053 r.mul = p.cxform.r1;
2055 r = mergeMulAdd(r, luminance);
2056 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2058 if(gstr[0] || luminancestr[0]) {
2061 g = parseMulAdd(gstr);
2063 g.add = p.cxform.g0;
2064 g.mul = p.cxform.g1;
2066 g = mergeMulAdd(g, luminance);
2067 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2069 if(bstr[0] || luminancestr[0]) {
2072 b = parseMulAdd(bstr);
2074 b.add = p.cxform.b0;
2075 b.mul = p.cxform.b1;
2077 b = mergeMulAdd(b, luminance);
2078 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2081 MULADD a = parseMulAdd(astr);
2082 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2086 s_put(instance, character, p);
2088 s_change(instance, p);
2090 s_qchange(instance, p);
2092 s_jump(instance, p);
2094 s_startclip(instance, character, p);
2095 else if(type == 5) {
2097 s_buttonput(character, as, p);
2099 s_buttonput(character, "shape", p);
2104 static int c_put(map_t*args)
2106 c_placement(args, 0);
2109 static int c_change(map_t*args)
2111 c_placement(args, 1);
2114 static int c_qchange(map_t*args)
2116 c_placement(args, 2);
2119 static int c_arcchange(map_t*args)
2121 c_placement(args, 2);
2124 static int c_jump(map_t*args)
2126 c_placement(args, 3);
2129 static int c_startclip(map_t*args)
2131 c_placement(args, 4);
2134 static int c_show(map_t*args)
2136 c_placement(args, 5);
2139 static int c_del(map_t*args)
2141 char*instance = lu(args, "name");
2142 s_delinstance(instance);
2145 static int c_end(map_t*args)
2150 static int c_sprite(map_t*args)
2152 char* name = lu(args, "name");
2156 static int c_frame(map_t*args)
2158 char*framestr = lu(args, "n");
2159 char*cutstr = lu(args, "cut");
2160 char*name = lu(args, "name");
2163 if(strcmp(cutstr, "no"))
2165 if(isRelative(framestr)) {
2166 frame = s_getframe();
2167 if(getSign(framestr)<0)
2168 syntaxerror("relative frame expressions must be positive");
2169 frame += parseInt(getOffset(framestr));
2172 frame = parseInt(framestr);
2173 if(s_getframe() >= frame
2174 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2175 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2177 s_frame(frame, cut, name);
2180 static int c_primitive(map_t*args)
2182 char*name = lu(args, "name");
2183 char*command = lu(args, "commandname");
2184 int width=0, height=0, r=0;
2185 int linewidth = parseTwip(lu(args, "line"));
2186 char*colorstr = lu(args, "color");
2187 RGBA color = parseColor(colorstr);
2188 char*fillstr = lu(args, "fill");
2195 if(!strcmp(command, "circle"))
2197 else if(!strcmp(command, "filled"))
2201 width = parseTwip(lu(args, "width"));
2202 height = parseTwip(lu(args, "height"));
2203 } else if (type==1) {
2204 r = parseTwip(lu(args, "r"));
2205 } else if (type==2) {
2206 outline = lu(args, "outline");
2209 if(!strcmp(fillstr, "fill"))
2211 if(!strcmp(fillstr, "none"))
2213 if(width<0 || height<0 || linewidth<0 || r<0)
2214 syntaxerror("values width, height, line, r must be positive");
2216 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2217 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2218 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2222 static int c_textshape(map_t*args)
2224 char*name = lu(args, "name");
2225 char*text = lu(args, "text");
2226 char*font = lu(args, "font");
2227 float size = parsePercent(lu(args, "size"));
2229 s_textshape(name, font, size, text);
2233 static int c_swf(map_t*args)
2235 char*name = lu(args, "name");
2236 char*filename = lu(args, "filename");
2237 char*command = lu(args, "commandname");
2238 if(!strcmp(command, "shape"))
2239 warning("Please use .swf instead of .shape");
2240 s_includeswf(name, filename);
2244 static int c_font(map_t*args)
2246 char*name = lu(args, "name");
2247 char*filename = lu(args, "filename");
2248 s_font(name, filename);
2252 static int c_sound(map_t*args)
2254 char*name = lu(args, "name");
2255 char*filename = lu(args, "filename");
2256 s_sound(name, filename);
2260 static int c_text(map_t*args)
2262 char*name = lu(args, "name");
2263 char*text = lu(args, "text");
2264 char*font = lu(args, "font");
2265 float size = parsePercent(lu(args, "size"));
2266 RGBA color = parseColor(lu(args, "color"));
2267 s_text(name, font, text, (int)(size*100), color);
2271 static int c_soundtrack(map_t*args)
2276 static int c_image(map_t*args)
2278 char*command = lu(args, "commandname");
2279 char*name = lu(args, "name");
2280 char*filename = lu(args, "filename");
2281 if(!strcmp(command,"jpeg")) {
2282 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2283 s_image(name, "jpeg", filename, quality);
2285 s_image(name, "png", filename, 0);
2290 static int c_outline(map_t*args)
2292 char*name = lu(args, "name");
2293 char*format = lu(args, "format");
2297 syntaxerror("colon (:) expected");
2299 s_outline(name, format, text);
2303 int fakechar(map_t*args)
2305 char*name = lu(args, "name");
2306 s_box(name, 0, 0, black, 20, 0);
2310 static int c_egon(map_t*args) {return fakechar(args);}
2311 static int c_button(map_t*args) {
2312 char*name = lu(args, "name");
2316 static int current_button_flags = 0;
2317 static int c_on_press(map_t*args)
2319 char*position = lu(args, "position");
2321 if(!strcmp(position, "inside")) {
2322 current_button_flags |= BC_OVERUP_OVERDOWN;
2323 } else if(!strcmp(position, "outside")) {
2324 //current_button_flags |= BC_IDLE_OUTDOWN;
2325 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2326 } else if(!strcmp(position, "anywhere")) {
2327 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2330 if(type == RAWDATA) {
2332 s_buttonaction(current_button_flags, action);
2333 current_button_flags = 0;
2339 static int c_on_release(map_t*args)
2341 char*position = lu(args, "position");
2343 if(!strcmp(position, "inside")) {
2344 current_button_flags |= BC_OVERDOWN_OVERUP;
2345 } else if(!strcmp(position, "outside")) {
2346 current_button_flags |= BC_OUTDOWN_IDLE;
2347 } else if(!strcmp(position, "anywhere")) {
2348 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2351 if(type == RAWDATA) {
2353 s_buttonaction(current_button_flags, action);
2354 current_button_flags = 0;
2360 static int c_on_move_in(map_t*args)
2362 char*position = lu(args, "state");
2364 if(!strcmp(position, "pressed")) {
2365 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2366 } else if(!strcmp(position, "not_pressed")) {
2367 current_button_flags |= BC_IDLE_OVERUP;
2368 } else if(!strcmp(position, "any")) {
2369 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2372 if(type == RAWDATA) {
2374 s_buttonaction(current_button_flags, action);
2375 current_button_flags = 0;
2381 static int c_on_move_out(map_t*args)
2383 char*position = lu(args, "state");
2385 if(!strcmp(position, "pressed")) {
2386 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2387 } else if(!strcmp(position, "not_pressed")) {
2388 current_button_flags |= BC_OVERUP_IDLE;
2389 } else if(!strcmp(position, "any")) {
2390 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2393 if(type == RAWDATA) {
2395 s_buttonaction(current_button_flags, action);
2396 current_button_flags = 0;
2402 static int c_on_key(map_t*args)
2404 char*key = lu(args, "key");
2406 if(strlen(key)==1) {
2409 current_button_flags |= 0x4000 + (key[0]*0x200);
2411 syntaxerror("invalid character: %c"+key[0]);
2416 <ctrl-x> = 0x200*(x-'a')
2420 syntaxerror("invalid key: %s",key);
2423 if(type == RAWDATA) {
2425 s_buttonaction(current_button_flags, action);
2426 current_button_flags = 0;
2433 static int c_edittext(map_t*args)
2435 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2436 char*name = lu(args, "name");
2437 char*font = lu(args, "font");
2438 int size = (int)(100*20*parsePercent(lu(args, "size")));
2439 int width = parseTwip(lu(args, "width"));
2440 int height = parseTwip(lu(args, "height"));
2441 char*text = lu(args, "text");
2442 RGBA color = parseColor(lu(args, "color"));
2443 int maxlength = parseInt(lu(args, "maxlength"));
2444 char*variable = lu(args, "variable");
2445 char*passwordstr = lu(args, "password");
2446 char*wordwrapstr = lu(args, "wordwrap");
2447 char*multilinestr = lu(args, "multiline");
2448 char*htmlstr = lu(args, "html");
2449 char*noselectstr = lu(args, "noselect");
2450 char*readonlystr = lu(args, "readonly");
2451 char*borderstr = lu(args, "border");
2454 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2455 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2456 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2457 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2458 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2459 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2460 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2462 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2466 static int c_morphshape(map_t*args) {return fakechar(args);}
2467 static int c_movie(map_t*args) {return fakechar(args);}
2469 static int c_texture(map_t*args) {return 0;}
2471 static int c_action(map_t*args)
2474 if(type != RAWDATA) {
2475 syntaxerror("colon (:) expected");
2485 command_func_t* func;
2488 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2489 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2490 // "import" type stuff
2491 {"swf", c_swf, "name filename"},
2492 {"shape", c_swf, "name filename"},
2493 {"jpeg", c_image, "name filename quality=80%"},
2494 {"png", c_image, "name filename"},
2495 {"movie", c_movie, "name filename"},
2496 {"sound", c_sound, "name filename"},
2497 {"font", c_font, "name filename"},
2498 {"soundtrack", c_soundtrack, "filename"},
2500 // generators of primitives
2502 {"point", c_point, "name x=0 y=0"},
2503 {"gradient", c_gradient, "name @radial=0"},
2504 {"outline", c_outline, "name format=simple"},
2505 {"textshape", c_textshape, "name font size=100% text"},
2507 // character generators
2508 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2509 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2510 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2512 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2513 {"text", c_text, "name text font size=100% color=white"},
2514 {"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"},
2515 {"morphshape", c_morphshape, "name start end"},
2516 {"button", c_button, "name"},
2517 {"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="},
2518 {"on_press", c_on_press, "position=inside"},
2519 {"on_release", c_on_release, "position=anywhere"},
2520 {"on_move_in", c_on_move_in, "state=not_pressed"},
2521 {"on_move_out", c_on_move_out, "state=not_pressed"},
2522 {"on_key", c_on_key, "key=any"},
2525 {"play", c_play, "name loop=0 @nomultiple=0"},
2526 {"stop", c_stop, "name"},
2527 {"nextframe", c_nextframe, "name"},
2528 {"previousframe", c_previousframe, "name"},
2530 // object placement tags
2531 {"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="},
2532 {"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="},
2533 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2534 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2535 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2536 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2537 {"del", c_del, "name"},
2538 // virtual object placement
2539 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2541 // commands which start a block
2542 //startclip (see above)
2543 {"sprite", c_sprite, "name"},
2544 {"action", c_action, ""},
2550 static map_t parseArguments(char*command, char*pattern)
2566 string_set(&t1, "commandname");
2567 string_set(&t2, command);
2568 map_put(&result, t1, t2);
2570 if(!pattern || !*pattern)
2577 if(!strncmp("<i> ", x, 3)) {
2579 if(type == COMMAND || type == RAWDATA) {
2581 syntaxerror("character name expected");
2583 name[pos].str = "instance";
2585 value[pos].str = text;
2586 value[pos].len = strlen(text);
2590 if(type == ASSIGNMENT)
2593 name[pos].str = "character";
2595 value[pos].str = text;
2596 value[pos].len = strlen(text);
2604 isboolean[pos] = (x[0] =='@');
2617 name[pos].len = d-x;
2622 name[pos].len = e-x;
2623 value[pos].str = e+1;
2624 value[pos].len = d-e-1;
2632 /* for(t=0;t<len;t++) {
2633 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2634 isboolean[t]?"(boolean)":"");
2639 if(type == RAWDATA || type == COMMAND) {
2644 // first, search for boolean arguments
2645 for(pos=0;pos<len;pos++)
2647 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2649 if(type == ASSIGNMENT)
2651 value[pos].str = text;
2652 value[pos].len = strlen(text);
2653 /*printf("setting boolean parameter %s (to %s)\n",
2654 strdup_n(name[pos], namelen[pos]),
2655 strdup_n(value[pos], valuelen[pos]));*/
2660 // second, search for normal arguments
2662 for(pos=0;pos<len;pos++)
2664 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2665 (type != ASSIGNMENT && !set[pos])) {
2667 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2669 if(type == ASSIGNMENT)
2672 value[pos].str = text;
2673 value[pos].len = strlen(text);
2675 printf("setting parameter %s (to %s)\n",
2676 strdup_n(name[pos].str, name[pos].len),
2677 strdup_n(value[pos].str, value[pos].len));
2683 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2687 for(t=0;t<len;t++) {
2688 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2691 for(t=0;t<len;t++) {
2692 if(value[t].str && value[t].str[0] == '*') {
2693 //relative default- take value from some other parameter
2695 for(s=0;s<len;s++) {
2696 if(value[s].len == value[t].len-1 &&
2697 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2698 value[t].str = value[s].str;
2701 if(value[t].str == 0) {
2703 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2707 /* ok, now construct the dictionary from the parameters */
2711 map_put(&result, name[t], value[t]);
2715 static void parseArgumentsForCommand(char*command)
2720 msg("<verbose> parse Command: %s (line %d)", command, line);
2722 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2723 if(!strcmp(arguments[t].command, command)) {
2725 /* ugly hack- will be removed soon (once documentation and .sc generating
2726 utilities have been changed) */
2727 if(!strcmp(command, "swf") && !stackpos) {
2728 warning("Please use .flash instead of .swf- this will be mandatory soon");
2733 args = parseArguments(command, arguments[t].arguments);
2739 syntaxerror("command %s not known", command);
2741 // catch missing .flash directives at the beginning of a file
2742 if(strcmp(command, "flash") && !stackpos)
2744 syntaxerror("No movie defined- use .flash first");
2748 printf(".%s\n", command);fflush(stdout);
2749 map_dump(&args, stdout, "\t");fflush(stdout);
2752 (*arguments[nr].func)(&args);
2754 /*if(!strcmp(command, "button") ||
2755 !strcmp(command, "action")) {
2758 if(type == COMMAND) {
2759 if(!strcmp(text, "end"))
2773 int main (int argc,char ** argv)
2776 processargs(argc, argv);
2777 initLog(0,-1,0,0,-1,verbose);
2780 args_callback_usage(argv[0]);
2784 file = generateTokens(filename);
2786 printf("parser returned error.\n");
2792 while(!noMoreTokens()) {
2795 syntaxerror("command expected");
2796 parseArgumentsForCommand(text);