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 optimize = 0;
44 static int override_outputname = 0;
46 static struct options_t options[] = {
54 int args_callback_option(char*name,char*val)
56 if(!strcmp(name, "V")) {
57 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
60 else if(!strcmp(name, "o")) {
62 override_outputname = 1;
65 else if(!strcmp(name, "O")) {
69 else if(!strcmp(name, "v")) {
74 printf("Unknown option: -%s\n", name);
79 int args_callback_longoption(char*name,char*val)
81 return args_long2shortoption(options, name, val);
83 void args_callback_usage(char *name)
86 printf("Usage: %s [-o file.swf] file.sc\n", name);
88 printf("-h , --help Print short help message and exit\n");
89 printf("-V , --version Print version info and exit\n");
90 printf("-v , --verbose Increase verbosity. \n");
91 printf("-o , --output <filename> Set output file to <filename>.\n");
94 int args_callback_command(char*name,char*val)
97 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
104 static struct token_t* file;
113 static void syntaxerror(char*format, ...)
117 va_start(arglist, format);
118 vsprintf(buf, format, arglist);
120 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
124 static void warning(char*format, ...)
128 va_start(arglist, format);
129 vsprintf(buf, format, arglist);
131 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
134 static void readToken()
136 type = file[pos].type;
138 syntaxerror("unexpected end of file");
140 text = file[pos].text;
141 textlen = strlen(text);
142 line = file[pos].line;
143 column = file[pos].column;
145 //printf("---> %d(%s) %s\n", type, type_names[type], text);
148 static void pushBack()
151 if(!pos) syntaxerror("internal error 3");
156 textlen = strlen(text);
159 column = file[p].column;
162 static int noMoreTokens()
164 if(file[pos].type == END)
169 // ------------------------------ swf routines ----------------------------
173 int type; //0=swf, 1=sprite, 2=clip, 3=button
179 /* for sprites (1): */
185 dictionary_t oldinstances;
190 static int stackpos = 0;
192 static dictionary_t characters;
193 static dictionary_t images;
194 static dictionary_t textures;
195 static dictionary_t outlines;
196 static dictionary_t gradients;
197 static char idmap[65536];
198 static TAG*tag = 0; //current tag
200 static int id; //current character id
201 static int currentframe; //current frame in current level
202 static SRECT currentrect; //current bounding box in current level
203 static U16 currentdepth;
204 static dictionary_t instances;
205 static dictionary_t fonts;
206 static dictionary_t sounds;
208 typedef struct _parameters {
210 float scalex, scaley;
218 typedef struct _character {
224 typedef struct _instance {
225 character_t*character;
227 parameters_t parameters;
228 TAG* lastTag; //last tag which set the object
229 U16 lastFrame; //frame lastTag is in
232 typedef struct _outline {
237 typedef struct _gradient {
243 typedef struct _texture {
247 static void character_init(character_t*c)
249 memset(c, 0, sizeof(character_t));
251 static character_t* character_new()
254 c = (character_t*)malloc(sizeof(character_t));
258 static void instance_init(instance_t*i)
260 memset(i, 0, sizeof(instance_t));
262 static instance_t* instance_new()
265 c = (instance_t*)malloc(sizeof(instance_t));
270 static void incrementid()
274 syntaxerror("Out of character ids.");
279 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
281 character_t* c = character_new();
283 c->definingTag = ctag;
286 if(dictionary_lookup(&characters, name))
287 syntaxerror("character %s defined twice", name);
288 dictionary_put2(&characters, name, c);
290 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
292 swf_SetString(tag, name);
293 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
296 swf_SetString(tag, name);
298 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
300 character_t* c = character_new();
301 c->definingTag = ctag;
305 if(dictionary_lookup(&images, name))
306 syntaxerror("image %s defined twice", name);
307 dictionary_put2(&images, name, c);
309 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
311 instance_t* i = instance_new();
314 //swf_GetMatrix(0, &i->matrix);
315 if(dictionary_lookup(&instances, name))
316 syntaxerror("object %s defined twice", name);
317 dictionary_put2(&instances, name, i);
321 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)
324 p->scalex = scalex; p->scaley = scaley;
325 p->pin = pin; p->pivot = pivot;
326 p->rotate = rotate; p->cxform = cxform;
330 static void parameters_clear(parameters_t*p)
333 p->scalex = 1.0; p->scaley = 1.0;
336 p->pivot.x = 0; p->pivot.y = 0;
339 swf_GetCXForm(0, &p->cxform, 1);
342 static void makeMatrix(MATRIX*m, parameters_t*p)
351 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
352 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
353 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
354 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
356 m->sx = (int)(sx*65536+0.5);
357 m->r1 = (int)(r1*65536+0.5);
358 m->r0 = (int)(r0*65536+0.5);
359 m->sy = (int)(sy*65536+0.5);
363 h = swf_TurnPoint(p->pin, m);
368 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
373 r = swf_TurnRect(rect, &m);
374 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
375 currentrect.xmax == 0 && currentrect.ymax == 0)
378 swf_ExpandRect2(¤trect, &r);
382 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
384 SWF*swf = (SWF*)malloc(sizeof(SWF));
387 syntaxerror(".swf blocks can't be nested");
389 memset(swf, 0, sizeof(swf));
390 swf->fileVersion = version;
392 swf->frameRate = fps;
393 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
394 swf->compressed = compress;
395 swf_SetRGB(tag,&background);
397 if(stackpos==sizeof(stack)/sizeof(stack[0]))
398 syntaxerror("too many levels of recursion");
400 dictionary_init(&characters);
401 dictionary_init(&images);
402 dictionary_init(&textures);
403 dictionary_init(&outlines);
404 dictionary_init(&gradients);
405 dictionary_init(&instances);
406 dictionary_init(&fonts);
407 dictionary_init(&sounds);
409 memset(&stack[stackpos], 0, sizeof(stack[0]));
410 stack[stackpos].type = 0;
411 stack[stackpos].filename = strdup(name);
412 stack[stackpos].swf = swf;
413 stack[stackpos].oldframe = -1;
418 memset(¤trect, 0, sizeof(currentrect));
421 memset(idmap, 0, sizeof(idmap));
425 void s_sprite(char*name)
427 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
428 swf_SetU16(tag, id); //id
429 swf_SetU16(tag, 0); //frames
431 memset(&stack[stackpos], 0, sizeof(stack[0]));
432 stack[stackpos].type = 1;
433 stack[stackpos].oldframe = currentframe;
434 stack[stackpos].olddepth = currentdepth;
435 stack[stackpos].oldrect = currentrect;
436 stack[stackpos].oldinstances = instances;
437 stack[stackpos].tag = tag;
438 stack[stackpos].id = id;
439 stack[stackpos].name = strdup(name);
441 /* FIXME: those four fields should be bundled together */
442 dictionary_init(&instances);
445 memset(¤trect, 0, sizeof(currentrect));
451 typedef struct _buttonrecord
459 typedef struct _button
463 buttonrecord_t records[4];
466 static button_t mybutton;
468 void s_button(char*name)
470 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
471 swf_SetU16(tag, id); //id
472 swf_ButtonSetFlags(tag, 0); //menu=no
474 memset(&mybutton, 0, sizeof(mybutton));
476 memset(&stack[stackpos], 0, sizeof(stack[0]));
477 stack[stackpos].type = 3;
478 stack[stackpos].tag = tag;
479 stack[stackpos].id = id;
480 stack[stackpos].name = strdup(name);
481 stack[stackpos].oldrect = currentrect;
482 memset(¤trect, 0, sizeof(currentrect));
487 void s_buttonput(char*character, char*as, parameters_t p)
489 character_t* c = dictionary_lookup(&characters, character);
494 if(!stackpos || (stack[stackpos-1].type != 3)) {
495 syntaxerror(".show may only appear in .button");
498 syntaxerror("character %s not known (in .shape %s)", character, character);
500 if(mybutton.endofshapes) {
501 syntaxerror("a .do may not precede a .show", character, character);
504 m = s_instancepos(c->size, &p);
512 if(*s==',' || *s==0) {
513 if(!strncmp(o,"idle",s-o)) {mybutton.records[0]=r;o=s+1;}
514 else if(!strncmp(o,"shape",s-o)) {mybutton.records[0]=r;o=s+1;}
515 else if(!strncmp(o,"hover",s-o)) {mybutton.records[1]=r;o=s+1;}
516 else if(!strncmp(o,"pressed",s-o)) {mybutton.records[2]=r;o=s+1;}
517 else if(!strncmp(o,"area",s-o)) {mybutton.records[3]=r;o=s+1;}
518 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
525 static void setbuttonrecords(TAG*tag)
527 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
528 if(!mybutton.endofshapes) {
531 if(!mybutton.records[3].set) {
532 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
536 if(mybutton.records[t].set) {
537 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
540 swf_SetU8(tag,0); // end of button records
541 mybutton.endofshapes = 1;
545 void s_buttonaction(int flags, char*action)
551 setbuttonrecords(stack[stackpos-1].tag);
553 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
555 syntaxerror("Couldn't compile ActionScript");
558 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
559 swf_ActionSet(stack[stackpos-1].tag, a);
560 mybutton.nr_actions++;
565 static void setactionend(TAG*tag)
567 if(!mybutton.nr_actions) {
568 /* no actions means we didn't have an actionoffset,
569 which means we can't signal the end of the
570 buttonaction records, so, *sigh*, we have
571 to insert a dummy record */
572 swf_SetU16(tag, 0); //offset
573 swf_SetU16(tag, 0); //condition
574 swf_SetU8(tag, 0); //action
578 static void s_endButton()
581 setbuttonrecords(stack[stackpos-1].tag);
582 setactionend(stack[stackpos-1].tag);
585 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
589 tag = stack[stackpos].tag;
590 currentrect = stack[stackpos].oldrect;
592 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
593 free(stack[stackpos].name);
596 TAG* removeFromTo(TAG*from, TAG*to)
598 TAG*save = from->prev;
600 TAG*next = from->next;
608 static void s_endSprite()
610 SRECT r = currentrect;
612 if(stack[stackpos].cut)
613 tag = removeFromTo(stack[stackpos].cut, tag);
617 /* TODO: before clearing, prepend "<spritename>." to names and
618 copy into old instances dict */
619 dictionary_clear(&instances);
621 currentframe = stack[stackpos].oldframe;
622 currentrect = stack[stackpos].oldrect;
623 currentdepth = stack[stackpos].olddepth;
624 instances = stack[stackpos].oldinstances;
626 tag = swf_InsertTag(tag, ST_SHOWFRAME);
627 tag = swf_InsertTag(tag, ST_END);
629 tag = stack[stackpos].tag;
632 syntaxerror("internal error(7)");
634 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
635 free(stack[stackpos].name);
638 static void s_endSWF()
644 if(stack[stackpos].cut)
645 tag = removeFromTo(stack[stackpos].cut, tag);
649 swf = stack[stackpos].swf;
650 filename = stack[stackpos].filename;
652 //if(tag->prev && tag->prev->id != ST_SHOWFRAME)
653 // tag = swf_InsertTag(tag, ST_SHOWFRAME);
654 tag = swf_InsertTag(tag, ST_SHOWFRAME);
656 tag = swf_InsertTag(tag, ST_END);
658 swf_OptimizeTagOrder(swf);
664 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
665 swf->movieSize = currentrect; /* "autocrop" */
668 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
669 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
670 swf->movieSize.ymax += 20;
671 warning("Empty bounding box for movie");
674 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
676 syntaxerror("couldn't create output file %s", filename);
679 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
681 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
685 dictionary_clear(&instances);
686 dictionary_clear(&characters);
687 dictionary_clear(&images);
688 dictionary_clear(&textures);
689 dictionary_clear(&outlines);
690 dictionary_clear(&gradients);
691 dictionary_clear(&fonts);
692 dictionary_clear(&sounds);
702 if(stack[stackpos-1].type == 0)
703 syntaxerror("End of file encountered in .flash block");
704 if(stack[stackpos-1].type == 1)
705 syntaxerror("End of file encountered in .sprite block");
706 if(stack[stackpos-1].type == 2)
707 syntaxerror("End of file encountered in .clip block");
713 return currentframe+1;
716 void s_frame(int nr, int cut, char*name)
722 syntaxerror("Illegal frame number");
723 nr--; // internally, frame 1 is frame 0
725 for(t=currentframe;t<nr;t++) {
726 tag = swf_InsertTag(tag, ST_SHOWFRAME);
727 if(t==nr-1 && name && *name) {
728 tag = swf_InsertTag(tag, ST_FRAMELABEL);
729 swf_SetString(tag, name);
730 swf_SetU8(tag, 1); //make this an anchor
733 if(nr == 0 && currentframe == 0 && name) {
734 tag = swf_InsertTag(tag, ST_FRAMELABEL);
735 swf_SetString(tag, name);
736 swf_SetU8(tag, 1); //make this an anchor
741 syntaxerror("Can't cut, frame empty");
743 stack[stackpos].cut = tag;
749 int parseColor2(char*str, RGBA*color);
751 int addFillStyle(SHAPE*s, SRECT*r, char*name)
758 parseColor2(name, &color);
759 return swf_ShapeAddSolidFillStyle(s, &color);
760 } else if ((texture = dictionary_lookup(&textures, name))) {
761 return swf_ShapeAddFillStyle2(s, &texture->fs);
762 } else if((image = dictionary_lookup(&images, name))) {
764 swf_GetMatrix(0, &m);
765 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
766 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
769 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
770 } else if ((gradient = dictionary_lookup(&gradients, name))) {
774 swf_GetMatrix(0, &rot);
775 ccos = cos(-gradient->rotate*2*3.14159265358979/360);
776 csin = sin(-gradient->rotate*2*3.14159265358979/360);
778 rot.r1 = -csin*65536;
781 r2 = swf_TurnRect(*r, &rot);
782 swf_GetMatrix(0, &m);
783 m.sx = (r2.xmax - r2.xmin)*2*ccos;
784 m.r1 = -(r2.xmax - r2.xmin)*2*csin;
785 m.r0 = (r2.ymax - r2.ymin)*2*csin;
786 m.sy = (r2.ymax - r2.ymin)*2*ccos;
787 m.tx = r->xmin + (r->xmax - r->xmin)/2;
788 m.ty = r->ymin + (r->ymax - r->ymin)/2;
789 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
790 } else if (parseColor2(name, &color)) {
791 return swf_ShapeAddSolidFillStyle(s, &color);
793 syntaxerror("not a color/fillstyle: %s", name);
798 RGBA black={r:0,g:0,b:0,a:0};
799 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
808 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
811 linewidth = linewidth>=20?linewidth-20:0;
812 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
815 fs1 = addFillStyle(s, &r2, texture);
818 r.xmin = r2.xmin-linewidth/2;
819 r.ymin = r2.ymin-linewidth/2;
820 r.xmax = r2.xmax+linewidth/2;
821 r.ymax = r2.ymax+linewidth/2;
823 swf_SetShapeHeader(tag,s);
824 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
825 swf_ShapeSetLine(tag,s,width,0);
826 swf_ShapeSetLine(tag,s,0,height);
827 swf_ShapeSetLine(tag,s,-width,0);
828 swf_ShapeSetLine(tag,s,0,-height);
829 swf_ShapeSetEnd(tag);
832 s_addcharacter(name, id, tag, r);
836 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
842 outline = dictionary_lookup(&outlines, outlinename);
844 syntaxerror("outline %s not defined", outlinename);
848 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
851 linewidth = linewidth>=20?linewidth-20:0;
852 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
855 fs1 = addFillStyle(s, &r2, texture);
858 rect.xmin = r2.xmin-linewidth/2;
859 rect.ymin = r2.ymin-linewidth/2;
860 rect.xmax = r2.xmax+linewidth/2;
861 rect.ymax = r2.ymax+linewidth/2;
863 swf_SetRect(tag,&rect);
864 swf_SetShapeStyles(tag, s);
865 swf_ShapeCountBits(s,0,0);
866 swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
867 &s->data, &s->bitlen, s->bits.fill, s->bits.line);
868 swf_SetShapeBits(tag, s);
869 swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
872 s_addcharacter(name, id, tag, rect);
876 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
881 r2.xmin = r2.ymin = 0;
885 tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
888 linewidth = linewidth>=20?linewidth-20:0;
889 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
892 fs1 = addFillStyle(s, &r2, texture);
894 rect.xmin = r2.xmin-linewidth/2;
895 rect.ymin = r2.ymin-linewidth/2;
896 rect.xmax = r2.xmax+linewidth/2;
897 rect.ymax = r2.ymax+linewidth/2;
899 swf_SetRect(tag,&rect);
900 swf_SetShapeHeader(tag,s);
901 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
902 swf_ShapeSetCircle(tag, s, r,r,r,r);
903 swf_ShapeSetEnd(tag);
906 s_addcharacter(name, id, tag, rect);
910 void s_textshape(char*name, char*fontname, float size, char*_text)
913 U8*text = (U8*)_text;
917 font = dictionary_lookup(&fonts, fontname);
919 syntaxerror("font \"%s\" not known!", fontname);
921 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
922 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
923 s_box(name, 0, 0, black, 20, 0);
926 g = font->ascii2glyph[text[0]];
928 outline = malloc(sizeof(outline_t));
929 memset(outline, 0, sizeof(outline_t));
930 outline->shape = font->glyph[g].shape;
931 outline->bbox = font->layout->bounds[g];
935 swf_Shape11DrawerInit(&draw, 0);
936 swf_DrawText(&draw, font, (int)(size*100), _text);
938 outline->shape = swf_ShapeDrawerToShape(&draw);
939 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
943 if(dictionary_lookup(&outlines, name))
944 syntaxerror("outline %s defined twice", name);
945 dictionary_put2(&outlines, name, outline);
948 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
953 font = dictionary_lookup(&fonts, fontname);
955 syntaxerror("font \"%s\" not known!", fontname);
957 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
959 if(!font->numchars) {
960 s_box(name, 0, 0, black, 20, 0);
963 r = swf_SetDefineText(tag, font, &color, text, size);
965 s_addcharacter(name, id, tag, r);
969 void s_quicktime(char*name, char*url)
974 memset(&r, 0, sizeof(r));
976 tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
978 swf_SetString(tag, url);
980 s_addcharacter(name, id, tag, r);
984 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags, int align)
987 EditTextLayout layout;
990 if(fontname && *fontname) {
991 flags |= ET_USEOUTLINES;
992 font = dictionary_lookup(&fonts, fontname);
994 syntaxerror("font \"%s\" not known!", fontname);
996 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
998 layout.align = align;
999 layout.leftmargin = 0;
1000 layout.rightmargin = 0;
1008 swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1010 s_addcharacter(name, id, tag, r);
1014 /* type: either "jpeg" or "png"
1016 void s_image(char*name, char*type, char*filename, int quality)
1018 /* an image is actually two folded: 1st bitmap, 2nd character.
1019 Both of them can be used separately */
1021 /* step 1: the bitmap */
1026 warning("image type \"png\" not supported yet!");
1027 s_box(name, 0, 0, black, 20, 0);
1031 #ifndef HAVE_LIBJPEG
1032 warning("no jpeg support compiled in");
1033 s_box(name, 0, 0, black, 20, 0);
1036 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1037 swf_SetU16(tag, imageID);
1039 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1040 syntaxerror("Image \"%s\" not found, or contains errors", filename);
1043 swf_GetJPEGSize(filename, &width, &height);
1050 s_addimage(name, id, tag, r);
1055 /* step 2: the character */
1056 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1057 swf_SetU16(tag, id);
1058 swf_ShapeSetBitmapRect(tag, imageID, width, height);
1060 s_addcharacter(name, id, tag, r);
1064 void s_getBitmapSize(char*name, int*width, int*height)
1066 character_t* image = dictionary_lookup(&images, name);
1067 gradient_t* gradient = dictionary_lookup(&gradients,name);
1069 *width = image->size.xmax;
1070 *height = image->size.ymax;
1074 /* internal SWF gradient size */
1075 if(gradient->radial) {
1084 syntaxerror("No such bitmap/gradient: %s", name);
1087 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1089 gradient_t* gradient = dictionary_lookup(&gradients, object);
1090 character_t* bitmap = dictionary_lookup(&images, object);
1091 texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1093 FILLSTYLE*fs = &texture->fs;
1096 fs->type = FILL_TILED;
1097 fs->id_bitmap = bitmap->id;
1098 } else if(gradient) {
1099 fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1100 fs->gradient = gradient->gradient;
1102 p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1103 makeMatrix(&fs->m, &p);
1104 if(gradient && !gradient->radial) {
1111 p2 = swf_TurnPoint(p1, &m);
1116 if(dictionary_lookup(&textures, name))
1117 syntaxerror("texture %s defined twice", name);
1118 dictionary_put2(&textures, name, texture);
1121 void dumpSWF(SWF*swf)
1123 TAG* tag = swf->firstTag;
1124 printf("vvvvvvvvvvvvvvvvvvvvv\n");
1126 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1129 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1132 void s_font(char*name, char*filename)
1135 font = swf_LoadFont(filename);
1138 warning("Couldn't open font file \"%s\"", filename);
1139 font = (SWFFONT*)malloc(sizeof(SWFFONT));
1140 memset(font, 0, sizeof(SWFFONT));
1141 dictionary_put2(&fonts, name, font);
1147 /* fix the layout. Only needed for old fonts */
1149 for(t=0;t<font->numchars;t++) {
1150 font->glyph[t].advance = 0;
1153 swf_FontCreateLayout(font);
1155 /* just in case this thing is used in .edittext later on */
1156 swf_FontPrepareForEditText(font);
1159 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1160 swf_FontSetDefine2(tag, font);
1161 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1163 swf_SetU16(tag, id);
1164 swf_SetString(tag, name);
1167 if(dictionary_lookup(&fonts, name))
1168 syntaxerror("font %s defined twice", name);
1169 dictionary_put2(&fonts, name, font);
1174 typedef struct _sound_t
1180 void s_sound(char*name, char*filename)
1182 struct WAV wav, wav2;
1187 int blocksize = 1152;
1189 if(!readWAV(filename, &wav)) {
1190 warning("Couldn't read wav file \"%s\"", filename);
1194 convertWAV2mono(&wav, &wav2, 44100);
1195 samples = (U16*)wav2.data;
1196 numsamples = wav2.size/2;
1198 #ifdef WORDS_BIGENDIAN
1200 for(t=0;t<numsamples;t++) {
1201 samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1206 if(numsamples%blocksize != 0)
1208 // apply padding, so that block is a multiple of blocksize
1209 int numblocks = (numsamples+blocksize-1)/blocksize;
1212 numsamples2 = numblocks * blocksize;
1213 samples2 = malloc(sizeof(U16)*numsamples2);
1214 memcpy(samples2, samples, numsamples*sizeof(U16));
1215 memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1216 numsamples = numsamples2;
1220 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1221 swf_SetU16(tag, id); //id
1222 swf_SetSoundDefine(tag, samples, numsamples);
1224 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1228 if(dictionary_lookup(&sounds, name))
1229 syntaxerror("sound %s defined twice", name);
1230 dictionary_put2(&sounds, name, sound);
1238 static char* gradient_getToken(const char**p)
1242 while(**p && strchr(" \t\n\r", **p)) {
1246 while(**p && !strchr(" \t\n\r", **p)) {
1249 result = malloc((*p)-start+1);
1250 memcpy(result,start,(*p)-start+1);
1251 result[(*p)-start] = 0;
1255 float parsePercent(char*str);
1256 RGBA parseColor(char*str);
1258 GRADIENT parseGradient(const char*str)
1262 const char* p = str;
1263 memset(&gradient, 0, sizeof(GRADIENT));
1265 char*posstr,*colorstr;
1268 posstr = gradient_getToken(&p);
1271 pos = (int)(parsePercent(posstr)*255.0);
1274 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1275 colorstr = gradient_getToken(&p);
1276 color = parseColor(colorstr);
1277 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1278 warning("gradient record too big- max size is 8, rest ignored");
1281 gradient.ratios[gradient.num] = pos;
1282 gradient.rgba[gradient.num] = color;
1291 void s_gradient(char*name, const char*text, int radial, int rotate)
1293 gradient_t* gradient;
1294 gradient = malloc(sizeof(gradient_t));
1295 memset(gradient, 0, sizeof(gradient_t));
1296 gradient->gradient = parseGradient(text);
1297 gradient->radial = radial;
1298 gradient->rotate = rotate;
1300 if(dictionary_lookup(&gradients, name))
1301 syntaxerror("gradient %s defined twice", name);
1302 dictionary_put2(&gradients, name, gradient);
1305 void s_action(const char*text)
1308 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1310 syntaxerror("Couldn't compile ActionScript");
1313 tag = swf_InsertTag(tag, ST_DOACTION);
1315 swf_ActionSet(tag, a);
1320 void s_initaction(const char*character, const char*text)
1324 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1326 syntaxerror("Couldn't compile ActionScript");
1329 c = (character_t*)dictionary_lookup(&characters, character);
1331 tag = swf_InsertTag(tag, ST_DOINITACTION);
1332 swf_SetU16(tag, c->id);
1333 swf_ActionSet(tag, a);
1338 int s_swf3action(char*name, char*action)
1341 instance_t* object = 0;
1343 dictionary_lookup(&instances, name);
1344 if(!object && name && *name) {
1345 /* we have a name, but couldn't find it. Abort. */
1348 a = action_SetTarget(0, name);
1349 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1350 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1351 else if(!strcmp(action, "stop")) a = action_Stop(a);
1352 else if(!strcmp(action, "play")) a = action_Play(a);
1353 a = action_SetTarget(a, "");
1356 tag = swf_InsertTag(tag, ST_DOACTION);
1357 swf_ActionSet(tag, a);
1362 void s_outline(char*name, char*format, char*source)
1371 //swf_Shape10DrawerInit(&draw, 0);
1372 swf_Shape11DrawerInit(&draw, 0);
1374 draw_string(&draw, source);
1376 shape = swf_ShapeDrawerToShape(&draw);
1377 bounds = swf_ShapeDrawerGetBBox(&draw);
1378 draw.dealloc(&draw);
1380 outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1381 outline->shape = shape;
1382 outline->bbox = bounds;
1384 if(dictionary_lookup(&outlines, name))
1385 syntaxerror("outline %s defined twice", name);
1386 dictionary_put2(&outlines, name, outline);
1389 int s_playsound(char*name, int loops, int nomultiple, int stop)
1395 sound = dictionary_lookup(&sounds, name);
1399 tag = swf_InsertTag(tag, ST_STARTSOUND);
1400 swf_SetU16(tag, sound->id); //id
1401 memset(&info, 0, sizeof(info));
1404 info.nomultiple = nomultiple;
1405 swf_SetSoundInfo(tag, &info);
1409 void s_includeswf(char*name, char*filename)
1417 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1418 f = open(filename,O_RDONLY|O_BINARY);
1420 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1421 s_box(name, 0, 0, black, 20, 0);
1424 if (swf_ReadSWF(f,&swf)<0) {
1425 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1426 s_box(name, 0, 0, black, 20, 0);
1431 /* FIXME: The following sets the bounding Box for the character.
1432 It is wrong for two reasons:
1433 a) It may be too small (in case objects in the movie clip at the borders)
1434 b) it may be too big (because the poor movie never got autocropped)
1438 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1439 swf_SetU16(tag, id);
1440 swf_SetU16(tag, swf.frameCount);
1442 swf_Relocate(&swf, idmap);
1444 ftag = swf.firstTag;
1448 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1449 if(cutout[t] == ftag->id) {
1453 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1455 if(ftag->id == ST_END)
1459 /* We simply dump all tags right after the sprite
1460 header, relying on the fact that swf_OptimizeTagOrder() will
1461 sort things out for us later.
1462 We also rely on the fact that the imported SWF is well-formed.
1464 tag = swf_InsertTag(tag, ftag->id);
1465 swf_SetBlock(tag, ftag->data, ftag->len);
1469 syntaxerror("Included file %s contains errors", filename);
1470 tag = swf_InsertTag(tag, ST_END);
1474 s_addcharacter(name, id, tag, r);
1477 SRECT s_getCharBBox(char*name)
1479 character_t* c = dictionary_lookup(&characters, name);
1480 if(!c) syntaxerror("character '%s' unknown(2)", name);
1483 SRECT s_getInstanceBBox(char*name)
1485 instance_t * i = dictionary_lookup(&instances, name);
1487 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1489 if(!c) syntaxerror("internal error(5)");
1492 parameters_t s_getParameters(char*name)
1494 instance_t * i = dictionary_lookup(&instances, name);
1495 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1496 return i->parameters;
1498 void s_startclip(char*instance, char*character, parameters_t p)
1500 character_t* c = dictionary_lookup(&characters, character);
1504 syntaxerror("character %s not known", character);
1506 i = s_addinstance(instance, c, currentdepth);
1508 m = s_instancepos(i->character->size, &p);
1510 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1511 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1512 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1514 i->lastFrame= currentframe;
1516 stack[stackpos].tag = tag;
1517 stack[stackpos].type = 2;
1526 swf_SetTagPos(stack[stackpos].tag, 0);
1527 swf_GetPlaceObject(stack[stackpos].tag, &p);
1528 p.clipdepth = currentdepth;
1530 swf_ClearTag(stack[stackpos].tag);
1531 swf_SetPlaceObject(stack[stackpos].tag, &p);
1535 void s_put(char*instance, char*character, parameters_t p)
1537 character_t* c = dictionary_lookup(&characters, character);
1541 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1544 i = s_addinstance(instance, c, currentdepth);
1546 m = s_instancepos(i->character->size, &p);
1548 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1549 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1551 i->lastFrame = currentframe;
1555 void s_jump(char*instance, parameters_t p)
1557 instance_t* i = dictionary_lookup(&instances, instance);
1560 syntaxerror("instance %s not known", instance);
1564 m = s_instancepos(i->character->size, &p);
1566 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1567 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1569 i->lastFrame = currentframe;
1572 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1576 if(num==0 || num==1)
1578 ratio = (float)pos/(float)num;
1580 p.x = (p2->x-p1->x)*ratio + p1->x;
1581 p.y = (p2->y-p1->y)*ratio + p1->y;
1582 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1583 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1584 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1585 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1587 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1588 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1589 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1590 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1592 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1593 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1594 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1595 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1597 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1598 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1599 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1600 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1604 void s_change(char*instance, parameters_t p2)
1606 instance_t* i = dictionary_lookup(&instances, instance);
1610 int frame, allframes;
1612 syntaxerror("instance %s not known", instance);
1616 allframes = currentframe - i->lastFrame - 1;
1618 warning(".change ignored. can only .put/.change an object once per frame.");
1622 m = s_instancepos(i->character->size, &p2);
1623 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1624 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1627 /* o.k., we got the start and end point set. Now iterate though all the
1628 tags in between, inserting object changes after each new frame */
1631 if(!t) syntaxerror("internal error(6)");
1633 while(frame < allframes) {
1634 if(t->id == ST_SHOWFRAME) {
1639 p = s_interpolate(&p1, &p2, frame, allframes);
1640 m = s_instancepos(i->character->size, &p); //needed?
1641 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1642 i->lastFrame = currentframe;
1643 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1645 if(frame == allframes)
1650 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1654 void s_delinstance(char*instance)
1656 instance_t* i = dictionary_lookup(&instances, instance);
1658 syntaxerror("instance %s not known", instance);
1660 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1661 swf_SetU16(tag, i->depth);
1662 dictionary_del(&instances, instance);
1665 void s_qchange(char*instance, parameters_t p)
1672 syntaxerror(".end unexpected");
1673 if(stack[stackpos-1].type == 0)
1675 else if(stack[stackpos-1].type == 1)
1677 else if(stack[stackpos-1].type == 2)
1679 else if(stack[stackpos-1].type == 3)
1681 else syntaxerror("internal error 1");
1684 // ------------------------------------------------------------------------
1686 typedef int command_func_t(map_t*args);
1688 SRECT parseBox(char*str)
1691 float xmin, xmax, ymin, ymax;
1692 char*x = strchr(str, 'x');
1694 if(!strcmp(str, "autocrop")) {
1695 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1699 d1 = strchr(x+1, ':');
1701 d2 = strchr(d1+1, ':');
1703 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1707 else if(d1 && !d2) {
1708 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1714 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1719 r.xmin = (SCOORD)(xmin*20);
1720 r.ymin = (SCOORD)(ymin*20);
1721 r.xmax = (SCOORD)(xmax*20);
1722 r.ymax = (SCOORD)(ymax*20);
1725 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1728 float parseFloat(char*str)
1732 int parseInt(char*str)
1737 if(str[0]=='+' || str[0]=='-')
1741 if(str[t]<'0' || str[t]>'9')
1742 syntaxerror("Not an Integer: \"%s\"", str);
1745 int parseTwip(char*str)
1749 if(str[0]=='+' || str[0]=='-') {
1754 dot = strchr(str, '.');
1758 return sign*parseInt(str)*20;
1760 int l=strlen(++dot);
1762 for(s=str;s<dot-1;s++)
1763 if(*s<'0' || *s>'9')
1764 syntaxerror("Not a coordinate: \"%s\"", str);
1766 if(*s<'0' || *s>'9')
1767 syntaxerror("Not a coordinate: \"%s\"", str);
1769 if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1770 warning("precision loss: %s converted to twip: %s", str, dot);
1775 return sign*atoi(str)*20;
1777 return sign*atoi(str)*20+atoi(dot)*2;
1779 return sign*atoi(str)*20+atoi(dot)/5;
1784 int isPoint(char*str)
1786 if(strchr(str, '('))
1792 SPOINT parsePoint(char*str)
1796 int l = strlen(str);
1797 char*comma = strchr(str, ',');
1798 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1799 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1800 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1801 p.x = parseTwip(tmp);
1802 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1803 p.y = parseTwip(tmp);
1807 int parseColor2(char*str, RGBA*color)
1809 int l = strlen(str);
1813 struct {unsigned char r,g,b;char*name;} colors[] =
1814 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1815 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1816 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1817 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1818 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1819 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1820 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1821 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1822 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1823 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1824 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1825 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1829 if(str[0]=='#' && (l==7 || l==9)) {
1830 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1832 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1834 color->r = r; color->g = g; color->b = b; color->a = a;
1837 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1838 if(!strcmp(str, colors[t].name)) {
1843 color->r = r; color->g = g; color->b = b; color->a = a;
1849 RGBA parseColor(char*str)
1852 if(!parseColor2(str, &c))
1853 syntaxerror("Expression '%s' is not a color", str);
1857 typedef struct _muladd {
1862 MULADD parseMulAdd(char*str)
1865 char* str2 = (char*)malloc(strlen(str)+5);
1872 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1873 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1874 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1875 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1876 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1877 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1878 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1879 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1880 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1881 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1883 syntaxerror("'%s' is not a valid color transform expression", str);
1885 m.add = (int)(add*256);
1886 m.mul = (int)(mul*256);
1891 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1893 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1894 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1896 if(a<-32768) a=-32768;
1897 if(a>32767) a=32767;
1898 if(m<-32768) m=-32768;
1899 if(m>32767) m=32767;
1905 float parsePxOrPercent(char*fontname, char*str)
1907 int l = strlen(str);
1908 if(strchr(str, '%'))
1909 return parsePercent(str);
1910 if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1911 float p = atof(str);
1912 return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1914 syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1918 float parsePercent(char*str)
1920 int l = strlen(str);
1924 return atoi(str)/100.0;
1926 syntaxerror("Expression '%s' is not a percentage", str);
1929 int isPercent(char*str)
1931 return str[strlen(str)-1]=='%';
1933 int parseNewSize(char*str, int size)
1936 return parsePercent(str)*size;
1938 return (int)(atof(str)*20);
1941 int isColor(char*str)
1944 return parseColor2(str, &c);
1947 static char* lu(map_t* args, char*name)
1949 char* value = map_lookup(args, name);
1951 map_dump(args, stdout, "");
1952 syntaxerror("internal error 2: value %s should be set", name);
1957 static int c_flash(map_t*args)
1959 char* filename = map_lookup(args, "filename");
1960 char* compressstr = lu(args, "compress");
1961 SRECT bbox = parseBox(lu(args, "bbox"));
1962 int version = parseInt(lu(args, "version"));
1963 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1965 RGBA color = parseColor(lu(args, "background"));
1967 if(!filename || !*filename) {
1968 /* for compatibility */
1969 filename = map_lookup(args, "name");
1970 if(!filename || !*filename) {
1973 //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1974 msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
1978 if(!filename || override_outputname)
1979 filename = outputname;
1981 if(!strcmp(compressstr, "default"))
1982 compress = version==6;
1983 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1985 else if(!strcmp(compressstr, "no"))
1987 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1989 s_swf(filename, bbox, version, fps, compress, color);
1992 int isRelative(char*str)
1994 return !strncmp(str, "<plus>", 6) ||
1995 !strncmp(str, "<minus>", 7);
1997 char* getOffset(char*str)
1999 if(!strncmp(str, "<plus>", 6))
2001 if(!strncmp(str, "<minus>", 7))
2003 syntaxerror("internal error (347)");
2006 int getSign(char*str)
2008 if(!strncmp(str, "<plus>", 6))
2010 if(!strncmp(str, "<minus>", 7))
2012 syntaxerror("internal error (348)");
2015 static dictionary_t points;
2016 static mem_t mpoints;
2017 int points_initialized = 0;
2019 SPOINT getPoint(SRECT r, char*name)
2022 if(!strcmp(name, "center")) {
2024 p.x = (r.xmin + r.xmax)/2;
2025 p.y = (r.ymin + r.ymax)/2;
2029 if(points_initialized)
2030 l = (int)dictionary_lookup(&points, name);
2032 syntaxerror("Invalid point: \"%s\".", name);
2035 return *(SPOINT*)&mpoints.buffer[l];
2038 static int texture2(char*name, char*object, map_t*args, int errors)
2041 char*xstr = map_lookup(args, "x");
2042 char*ystr = map_lookup(args, "y");
2043 char*widthstr = map_lookup(args, "width");
2044 char*heightstr = map_lookup(args, "height");
2045 char*scalestr = map_lookup(args, "scale");
2046 char*scalexstr = map_lookup(args, "scalex");
2047 char*scaleystr = map_lookup(args, "scaley");
2048 char*rotatestr = map_lookup(args, "rotate");
2049 char* shearstr = map_lookup(args, "shear");
2050 char* radiusstr = map_lookup(args, "r");
2052 float scalex = 1.0, scaley = 1.0;
2053 float rotate=0, shear=0;
2055 if(!*xstr && !*ystr) {
2057 syntaxerror("x and y must be set");
2060 if(*scalestr && (*scalexstr || *scaleystr)) {
2061 syntaxerror("scale and scalex/scaley can't both be set");
2064 if((*widthstr || *heightstr) && *radiusstr) {
2065 syntaxerror("width/height and radius can't both be set");
2068 widthstr = radiusstr;
2069 heightstr = radiusstr;
2071 if(!*xstr) xstr="0";
2072 if(!*ystr) ystr="0";
2073 if(!*rotatestr) rotatestr="0";
2074 if(!*shearstr) shearstr="0";
2077 scalex = scaley = parsePercent(scalestr);
2078 } else if(*scalexstr || *scaleystr) {
2079 if(scalexstr) scalex = parsePercent(scalexstr);
2080 if(scaleystr) scaley = parsePercent(scaleystr);
2081 } else if(*widthstr || *heightstr) {
2084 s_getBitmapSize(object, &width, &height);
2086 scalex = (float)parseTwip(widthstr)/(float)width;
2088 scaley = (float)parseTwip(heightstr)/(float)height;
2090 x = parseTwip(xstr);
2091 y = parseTwip(ystr);
2092 rotate = parseFloat(rotatestr);
2093 shear = parseFloat(shearstr);
2095 s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2100 static int c_texture(map_t*args)
2102 char*name = lu(args, "instance");
2103 char*object = lu(args, "character");
2104 return texture2(name, object, args, 1);
2107 static int c_gradient(map_t*args)
2109 char*name = lu(args, "name");
2110 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2111 int rotate = parseInt(lu(args, "rotate"));
2115 syntaxerror("colon (:) expected");
2117 s_gradient(name, text, radial, rotate);
2119 /* check whether we also have placement information,
2120 which would make this a positioned gradient.
2121 If there is placement information, texture2() will
2122 add a texture, which has priority over the gradient.
2124 texture2(name, name, args, 0);
2127 static int c_point(map_t*args)
2129 char*name = lu(args, "name");
2133 if(!points_initialized) {
2134 dictionary_init(&points);
2136 points_initialized = 1;
2138 p.x = parseTwip(lu(args, "x"));
2139 p.y = parseTwip(lu(args, "y"));
2140 pos = mem_put(&mpoints, &p, sizeof(p));
2141 string_set(&s1, name);
2143 dictionary_put(&points, s1, (void*)pos);
2146 static int c_play(map_t*args)
2148 char*name = lu(args, "name");
2149 char*loop = lu(args, "loop");
2150 char*nomultiple = lu(args, "nomultiple");
2152 if(!strcmp(nomultiple, "nomultiple"))
2155 nm = parseInt(nomultiple);
2157 if(s_playsound(name, parseInt(loop), nm, 0)) {
2159 } else if(s_swf3action(name, "play")) {
2165 static int c_stop(map_t*args)
2167 char*name = map_lookup(args, "name");
2169 if(s_playsound(name, 0,0,1)) {
2171 } else if(s_swf3action(name, "stop")) {
2174 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2178 static int c_nextframe(map_t*args)
2180 char*name = lu(args, "name");
2182 if(s_swf3action(name, "nextframe")) {
2185 syntaxerror("I don't know anything about movie \"%s\"", name);
2189 static int c_previousframe(map_t*args)
2191 char*name = lu(args, "name");
2193 if(s_swf3action(name, "previousframe")) {
2196 syntaxerror("I don't know anything about movie \"%s\"", name);
2200 static int c_placement(map_t*args, int type)
2202 char*instance = lu(args, (type==0||type==4)?"instance":"name");
2205 char* luminancestr = lu(args, "luminance");
2206 char* scalestr = lu(args, "scale");
2207 char* scalexstr = lu(args, "scalex");
2208 char* scaleystr = lu(args, "scaley");
2209 char* rotatestr = lu(args, "rotate");
2210 char* shearstr = lu(args, "shear");
2211 char* xstr="", *pivotstr="";
2212 char* ystr="", *anglestr="";
2213 char*above = lu(args, "above"); /*FIXME*/
2214 char*below = lu(args, "below");
2215 char* rstr = lu(args, "red");
2216 char* gstr = lu(args, "green");
2217 char* bstr = lu(args, "blue");
2218 char* astr = lu(args, "alpha");
2219 char* pinstr = lu(args, "pin");
2220 char* as = map_lookup(args, "as");
2228 if(type==9) { // (?) .rotate or .arcchange
2229 pivotstr = lu(args, "pivot");
2230 anglestr = lu(args, "angle");
2232 xstr = lu(args, "x");
2233 ystr = lu(args, "y");
2236 luminance = parseMulAdd(luminancestr);
2239 luminance.mul = 256;
2243 if(scalexstr[0]||scaleystr[0])
2244 syntaxerror("scalex/scaley and scale cannot both be set");
2245 scalexstr = scaleystr = scalestr;
2248 if(type == 0 || type == 4) {
2250 character = lu(args, "character");
2251 parameters_clear(&p);
2252 } else if (type == 5) {
2253 character = lu(args, "name");
2254 parameters_clear(&p);
2257 p = s_getParameters(instance);
2262 if(isRelative(xstr)) {
2263 if(type == 0 || type == 4)
2264 syntaxerror("relative x values not allowed for initial put or startclip");
2265 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2267 p.x = parseTwip(xstr);
2271 if(isRelative(ystr)) {
2272 if(type == 0 || type == 4)
2273 syntaxerror("relative y values not allowed for initial put or startclip");
2274 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2276 p.y = parseTwip(ystr);
2280 /* scale, scalex, scaley */
2282 oldbbox = s_getCharBBox(character);
2284 oldbbox = s_getInstanceBBox(instance);
2286 oldwidth = oldbbox.xmax - oldbbox.xmin;
2287 oldheight = oldbbox.ymax - oldbbox.ymin;
2289 if(oldwidth==0) p.scalex = 1.0;
2292 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2296 if(oldheight==0) p.scaley = 1.0;
2299 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2305 if(isRelative(rotatestr)) {
2306 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2308 p.rotate = parseFloat(rotatestr);
2314 if(isRelative(shearstr)) {
2315 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2317 p.shear = parseFloat(shearstr);
2322 if(isPoint(pivotstr))
2323 p.pivot = parsePoint(pivotstr);
2325 p.pivot = getPoint(oldbbox, pivotstr);
2329 p.pin = parsePoint(pinstr);
2331 p.pin = getPoint(oldbbox, pinstr);
2334 /* color transform */
2336 if(rstr[0] || luminancestr[0]) {
2339 r = parseMulAdd(rstr);
2341 r.add = p.cxform.r0;
2342 r.mul = p.cxform.r1;
2344 r = mergeMulAdd(r, luminance);
2345 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2347 if(gstr[0] || luminancestr[0]) {
2350 g = parseMulAdd(gstr);
2352 g.add = p.cxform.g0;
2353 g.mul = p.cxform.g1;
2355 g = mergeMulAdd(g, luminance);
2356 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2358 if(bstr[0] || luminancestr[0]) {
2361 b = parseMulAdd(bstr);
2363 b.add = p.cxform.b0;
2364 b.mul = p.cxform.b1;
2366 b = mergeMulAdd(b, luminance);
2367 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2370 MULADD a = parseMulAdd(astr);
2371 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2375 s_put(instance, character, p);
2377 s_change(instance, p);
2379 s_qchange(instance, p);
2381 s_jump(instance, p);
2383 s_startclip(instance, character, p);
2384 else if(type == 5) {
2386 s_buttonput(character, as, p);
2388 s_buttonput(character, "shape", p);
2393 static int c_put(map_t*args)
2395 c_placement(args, 0);
2398 static int c_change(map_t*args)
2400 c_placement(args, 1);
2403 static int c_qchange(map_t*args)
2405 c_placement(args, 2);
2408 static int c_arcchange(map_t*args)
2410 c_placement(args, 2);
2413 static int c_jump(map_t*args)
2415 c_placement(args, 3);
2418 static int c_startclip(map_t*args)
2420 c_placement(args, 4);
2423 static int c_show(map_t*args)
2425 c_placement(args, 5);
2428 static int c_del(map_t*args)
2430 char*instance = lu(args, "name");
2431 s_delinstance(instance);
2434 static int c_end(map_t*args)
2439 static int c_sprite(map_t*args)
2441 char* name = lu(args, "name");
2445 static int c_frame(map_t*args)
2447 char*framestr = lu(args, "n");
2448 char*cutstr = lu(args, "cut");
2449 char*name = lu(args, "name");
2452 if(strcmp(cutstr, "no"))
2454 if(isRelative(framestr)) {
2455 frame = s_getframe();
2456 if(getSign(framestr)<0)
2457 syntaxerror("relative frame expressions must be positive");
2458 frame += parseInt(getOffset(framestr));
2461 frame = parseInt(framestr);
2462 if(s_getframe() >= frame
2463 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2464 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2466 s_frame(frame, cut, name);
2469 static int c_primitive(map_t*args)
2471 char*name = lu(args, "name");
2472 char*command = lu(args, "commandname");
2473 int width=0, height=0, r=0;
2474 int linewidth = parseTwip(lu(args, "line"));
2475 char*colorstr = lu(args, "color");
2476 RGBA color = parseColor(colorstr);
2477 char*fillstr = lu(args, "fill");
2484 if(!strcmp(command, "circle"))
2486 else if(!strcmp(command, "filled"))
2490 width = parseTwip(lu(args, "width"));
2491 height = parseTwip(lu(args, "height"));
2492 } else if (type==1) {
2493 r = parseTwip(lu(args, "r"));
2494 } else if (type==2) {
2495 outline = lu(args, "outline");
2498 if(!strcmp(fillstr, "fill"))
2500 if(!strcmp(fillstr, "none"))
2502 if(width<0 || height<0 || linewidth<0 || r<0)
2503 syntaxerror("values width, height, line, r must be positive");
2505 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2506 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2507 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2511 static int c_textshape(map_t*args)
2513 char*name = lu(args, "name");
2514 char*text = lu(args, "text");
2515 char*font = lu(args, "font");
2516 float size = parsePxOrPercent(font, lu(args, "size"));
2518 s_textshape(name, font, size, text);
2522 static int c_swf(map_t*args)
2524 char*name = lu(args, "name");
2525 char*filename = lu(args, "filename");
2526 char*command = lu(args, "commandname");
2527 if(!strcmp(command, "shape"))
2528 warning("Please use .swf instead of .shape");
2529 s_includeswf(name, filename);
2533 static int c_font(map_t*args)
2535 char*name = lu(args, "name");
2536 char*filename = lu(args, "filename");
2537 s_font(name, filename);
2541 static int c_sound(map_t*args)
2543 char*name = lu(args, "name");
2544 char*filename = lu(args, "filename");
2545 s_sound(name, filename);
2549 static int c_text(map_t*args)
2551 char*name = lu(args, "name");
2552 char*text = lu(args, "text");
2553 char*font = lu(args, "font");
2554 float size = parsePxOrPercent(font, lu(args, "size"));
2555 RGBA color = parseColor(lu(args, "color"));
2556 s_text(name, font, text, (int)(size*100), color);
2560 static int c_soundtrack(map_t*args)
2565 static int c_quicktime(map_t*args)
2567 char*name = lu(args, "name");
2568 char*url = lu(args, "url");
2569 s_quicktime(name, url);
2573 static int c_image(map_t*args)
2575 char*command = lu(args, "commandname");
2576 char*name = lu(args, "name");
2577 char*filename = lu(args, "filename");
2578 if(!strcmp(command,"jpeg")) {
2579 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2580 s_image(name, "jpeg", filename, quality);
2582 s_image(name, "png", filename, 0);
2587 static int c_outline(map_t*args)
2589 char*name = lu(args, "name");
2590 char*format = lu(args, "format");
2594 syntaxerror("colon (:) expected");
2596 s_outline(name, format, text);
2600 int fakechar(map_t*args)
2602 char*name = lu(args, "name");
2603 s_box(name, 0, 0, black, 20, 0);
2607 static int c_egon(map_t*args) {return fakechar(args);}
2608 static int c_button(map_t*args) {
2609 char*name = lu(args, "name");
2613 static int current_button_flags = 0;
2614 static int c_on_press(map_t*args)
2616 char*position = lu(args, "position");
2618 if(!strcmp(position, "inside")) {
2619 current_button_flags |= BC_OVERUP_OVERDOWN;
2620 } else if(!strcmp(position, "outside")) {
2621 //current_button_flags |= BC_IDLE_OUTDOWN;
2622 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2623 } else if(!strcmp(position, "anywhere")) {
2624 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2627 if(type == RAWDATA) {
2629 s_buttonaction(current_button_flags, action);
2630 current_button_flags = 0;
2636 static int c_on_release(map_t*args)
2638 char*position = lu(args, "position");
2640 if(!strcmp(position, "inside")) {
2641 current_button_flags |= BC_OVERDOWN_OVERUP;
2642 } else if(!strcmp(position, "outside")) {
2643 current_button_flags |= BC_OUTDOWN_IDLE;
2644 } else if(!strcmp(position, "anywhere")) {
2645 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2648 if(type == RAWDATA) {
2650 s_buttonaction(current_button_flags, action);
2651 current_button_flags = 0;
2657 static int c_on_move_in(map_t*args)
2659 char*position = lu(args, "state");
2661 if(!strcmp(position, "pressed")) {
2662 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2663 } else if(!strcmp(position, "not_pressed")) {
2664 current_button_flags |= BC_IDLE_OVERUP;
2665 } else if(!strcmp(position, "any")) {
2666 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2669 if(type == RAWDATA) {
2671 s_buttonaction(current_button_flags, action);
2672 current_button_flags = 0;
2678 static int c_on_move_out(map_t*args)
2680 char*position = lu(args, "state");
2682 if(!strcmp(position, "pressed")) {
2683 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2684 } else if(!strcmp(position, "not_pressed")) {
2685 current_button_flags |= BC_OVERUP_IDLE;
2686 } else if(!strcmp(position, "any")) {
2687 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2690 if(type == RAWDATA) {
2692 s_buttonaction(current_button_flags, action);
2693 current_button_flags = 0;
2699 static int c_on_key(map_t*args)
2701 char*key = lu(args, "key");
2703 if(strlen(key)==1) {
2706 current_button_flags |= 0x4000 + (key[0]*0x200);
2708 syntaxerror("invalid character: %c"+key[0]);
2713 <ctrl-x> = 0x200*(x-'a')
2717 syntaxerror("invalid key: %s",key);
2720 if(type == RAWDATA) {
2722 s_buttonaction(current_button_flags, action);
2723 current_button_flags = 0;
2730 static int c_edittext(map_t*args)
2732 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @autosize=0"},
2733 char*name = lu(args, "name");
2734 char*font = lu(args, "font");
2735 int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2736 int width = parseTwip(lu(args, "width"));
2737 int height = parseTwip(lu(args, "height"));
2738 char*text = lu(args, "text");
2739 RGBA color = parseColor(lu(args, "color"));
2740 int maxlength = parseInt(lu(args, "maxlength"));
2741 char*variable = lu(args, "variable");
2742 char*passwordstr = lu(args, "password");
2743 char*wordwrapstr = lu(args, "wordwrap");
2744 char*multilinestr = lu(args, "multiline");
2745 char*htmlstr = lu(args, "html");
2746 char*noselectstr = lu(args, "noselect");
2747 char*readonlystr = lu(args, "readonly");
2748 char*borderstr = lu(args, "border");
2749 char*autosizestr = lu(args, "autosize");
2750 char*alignstr = lu(args, "align");
2754 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2755 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2756 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2757 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2758 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2759 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2760 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2761 if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2762 if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2763 else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2764 else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2765 else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2766 else syntaxerror("Unknown alignment: %s", alignstr);
2768 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2772 static int c_morphshape(map_t*args) {return fakechar(args);}
2773 static int c_movie(map_t*args) {return fakechar(args);}
2775 static char* readfile(const char*filename)
2777 FILE*fi = fopen(filename, "rb");
2781 syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2782 fseek(fi, 0, SEEK_END);
2784 fseek(fi, 0, SEEK_SET);
2785 text = rfx_alloc(l+1);
2786 fread(text, l, 1, fi);
2792 static int c_action(map_t*args)
2794 char* filename = map_lookup(args, "filename");
2795 if(!filename ||!*filename) {
2797 if(type != RAWDATA) {
2798 syntaxerror("colon (:) expected");
2802 s_action(readfile(filename));
2808 static int c_initaction(map_t*args)
2810 char* character = lu(args, "name");
2811 char* filename = map_lookup(args, "filename");
2812 if(!filename ||!*filename) {
2814 if(type != RAWDATA) {
2815 syntaxerror("colon (:) expected");
2817 s_initaction(character, text);
2819 s_initaction(character, readfile(filename));
2827 command_func_t* func;
2830 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2831 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2832 // "import" type stuff
2833 {"swf", c_swf, "name filename"},
2834 {"shape", c_swf, "name filename"},
2835 {"jpeg", c_image, "name filename quality=80%"},
2836 {"png", c_image, "name filename"},
2837 {"movie", c_movie, "name filename"},
2838 {"sound", c_sound, "name filename"},
2839 {"font", c_font, "name filename"},
2840 {"soundtrack", c_soundtrack, "filename"},
2841 {"quicktime", c_quicktime, "url"},
2843 // generators of primitives
2845 {"point", c_point, "name x=0 y=0"},
2846 {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2847 {"outline", c_outline, "name format=simple"},
2848 {"textshape", c_textshape, "name font size=100% text"},
2850 // character generators
2851 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2852 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2853 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2855 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2856 {"text", c_text, "name text font size=100% color=white"},
2857 {"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 @autosize=0 align="},
2858 {"morphshape", c_morphshape, "name start end"},
2859 {"button", c_button, "name"},
2860 {"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="},
2861 {"on_press", c_on_press, "position=inside"},
2862 {"on_release", c_on_release, "position=anywhere"},
2863 {"on_move_in", c_on_move_in, "state=not_pressed"},
2864 {"on_move_out", c_on_move_out, "state=not_pressed"},
2865 {"on_key", c_on_key, "key=any"},
2868 {"play", c_play, "name loop=0 @nomultiple=0"},
2869 {"stop", c_stop, "name= "},
2870 {"nextframe", c_nextframe, "name"},
2871 {"previousframe", c_previousframe, "name"},
2873 // object placement tags
2874 {"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="},
2875 {"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="},
2876 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2877 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2878 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2879 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2880 {"del", c_del, "name"},
2881 // virtual object placement
2882 {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2884 // commands which start a block
2885 //startclip (see above)
2886 {"sprite", c_sprite, "name"},
2887 {"action", c_action, "filename="},
2888 {"initaction", c_initaction, "name filename="},
2894 static map_t parseArguments(char*command, char*pattern)
2910 string_set(&t1, "commandname");
2911 string_set(&t2, command);
2912 map_put(&result, t1, t2);
2914 if(!pattern || !*pattern)
2921 if(!strncmp("<i> ", x, 3)) {
2923 if(type == COMMAND || type == RAWDATA) {
2925 syntaxerror("character name expected");
2927 name[pos].str = "instance";
2929 value[pos].str = text;
2930 value[pos].len = strlen(text);
2934 if(type == ASSIGNMENT)
2937 name[pos].str = "character";
2939 value[pos].str = text;
2940 value[pos].len = strlen(text);
2948 isboolean[pos] = (x[0] =='@');
2961 name[pos].len = d-x;
2966 name[pos].len = e-x;
2967 value[pos].str = e+1;
2968 value[pos].len = d-e-1;
2976 /* for(t=0;t<len;t++) {
2977 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2978 isboolean[t]?"(boolean)":"");
2983 if(type == RAWDATA || type == COMMAND) {
2988 // first, search for boolean arguments
2989 for(pos=0;pos<len;pos++)
2991 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2993 if(type == ASSIGNMENT)
2995 value[pos].str = text;
2996 value[pos].len = strlen(text);
2997 /*printf("setting boolean parameter %s (to %s)\n",
2998 strdup_n(name[pos], namelen[pos]),
2999 strdup_n(value[pos], valuelen[pos]));*/
3004 // second, search for normal arguments
3006 for(pos=0;pos<len;pos++)
3008 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3009 (type != ASSIGNMENT && !set[pos])) {
3011 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3013 if(type == ASSIGNMENT)
3016 value[pos].str = text;
3017 value[pos].len = strlen(text);
3019 printf("setting parameter %s (to %s)\n",
3020 strdup_n(name[pos].str, name[pos].len),
3021 strdup_n(value[pos].str, value[pos].len));
3027 syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3031 for(t=0;t<len;t++) {
3032 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3035 for(t=0;t<len;t++) {
3036 if(value[t].str && value[t].str[0] == '*') {
3037 //relative default- take value from some other parameter
3039 for(s=0;s<len;s++) {
3040 if(value[s].len == value[t].len-1 &&
3041 !strncmp(&value[t].str[1], value[s].str, value[s].len))
3042 value[t].str = value[s].str;
3045 if(value[t].str == 0) {
3047 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3051 /* ok, now construct the dictionary from the parameters */
3055 map_put(&result, name[t], value[t]);
3059 static void parseArgumentsForCommand(char*command)
3064 msg("<verbose> parse Command: %s (line %d)", command, line);
3066 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3067 if(!strcmp(arguments[t].command, command)) {
3069 /* ugly hack- will be removed soon (once documentation and .sc generating
3070 utilities have been changed) */
3071 if(!strcmp(command, "swf") && !stackpos) {
3072 warning("Please use .flash instead of .swf- this will be mandatory soon");
3077 args = parseArguments(command, arguments[t].arguments);
3083 syntaxerror("command %s not known", command);
3085 // catch missing .flash directives at the beginning of a file
3086 if(strcmp(command, "flash") && !stackpos)
3088 syntaxerror("No movie defined- use .flash first");
3092 printf(".%s\n", command);fflush(stdout);
3093 map_dump(&args, stdout, "\t");fflush(stdout);
3096 (*arguments[nr].func)(&args);
3098 /*if(!strcmp(command, "button") ||
3099 !strcmp(command, "action")) {
3102 if(type == COMMAND) {
3103 if(!strcmp(text, "end"))
3117 int main (int argc,char ** argv)
3120 processargs(argc, argv);
3121 initLog(0,-1,0,0,-1,verbose);
3124 args_callback_usage(argv[0]);
3128 file = generateTokens(filename);
3130 printf("parser returned error.\n");
3136 while(!noMoreTokens()) {
3139 syntaxerror("command expected");
3140 parseArgumentsForCommand(text);