added HAVE_FREETYPE_FT2BUILD_H.
[swftools.git] / src / swfc.c
blob5189e72aaffc054e5dead31026f50b7dd711efd1
1 /* swfc.c
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 */
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <memory.h>
27 #include <errno.h>
28 #include <math.h>
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/drawer.h"
32 #include "../lib/log.h"
33 #include "../lib/args.h"
34 #include "../lib/q.h"
35 #include "parser.h"
36 #include "wav.h"
38 //#define DEBUG
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[] = {
46 {"h", "help"},
47 {"V", "version"},
48 {"v", "verbose"},
49 {"o", "output"},
50 {0,0}
53 int args_callback_option(char*name,char*val)
55 if(!strcmp(name, "V")) {
56 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
57 exit(0);
59 else if(!strcmp(name, "o")) {
60 outputname = val;
61 override_outputname = 1;
62 return 1;
64 else if(!strcmp(name, "v")) {
65 verbose ++;
66 return 0;
68 else {
69 printf("Unknown option: -%s\n", name);
70 exit(1);
72 return 0;
74 int args_callback_longoption(char*name,char*val)
76 return args_long2shortoption(options, name, val);
78 void args_callback_usage(char *name)
80 printf("\n");
81 printf("Usage: %s [-o file.swf] file.sc\n", name);
82 printf("\n");
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");
87 printf("\n");
89 int args_callback_command(char*name,char*val)
91 if(filename) {
92 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
93 filename, name);
95 filename = name;
96 return 0;
99 static struct token_t* file;
101 static int pos;
102 static char*text;
103 static int textlen;
104 static int type;
105 static int line;
106 static int column;
108 static void syntaxerror(char*format, ...)
110 char buf[1024];
111 va_list arglist;
112 va_start(arglist, format);
113 vsprintf(buf, format, arglist);
114 va_end(arglist);
115 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
116 exit(1);
119 static void warning(char*format, ...)
121 char buf[1024];
122 va_list arglist;
123 va_start(arglist, format);
124 vsprintf(buf, format, arglist);
125 va_end(arglist);
126 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
129 static void readToken()
131 type = file[pos].type;
132 if(type == END) {
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;
139 pos++;
140 //printf("---> %d(%s) %s\n", type, type_names[type], text);
143 static void pushBack()
145 int p;
146 if(!pos) syntaxerror("internal error 3");
147 pos--;
148 p = pos;
149 if(p) p--;
150 text = file[p].text;
151 textlen = strlen(text);
152 type = file[p].type;
153 line = file[p].line;
154 column = file[p].column;
157 static int noMoreTokens()
159 if(file[pos].type == END)
160 return 1;
161 return 0;
164 // ------------------------------ swf routines ----------------------------
165 struct _character;
166 static struct level
168 int type; //0=swf, 1=sprite, 2=clip, 3=button
170 /* for swf (0): */
171 SWF*swf;
172 char*filename;
174 /* for sprites (1): */
175 TAG*tag;
176 U16 id;
177 char*name;
178 U16 olddepth;
179 int oldframe;
180 dictionary_t oldinstances;
181 SRECT oldrect;
182 TAG* cut;
184 } stack[256];
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 {
203 int x,y;
204 float scalex, scaley;
205 CXFORM cxform;
206 float rotate;
207 float shear;
208 SPOINT pivot;
209 SPOINT pin;
210 } parameters_t;
212 typedef struct _character {
213 TAG*definingTag;
214 U16 id;
215 SRECT size;
216 } character_t;
218 typedef struct _instance {
219 character_t*character;
220 U16 depth;
221 parameters_t parameters;
222 TAG* lastTag; //last tag which set the object
223 U16 lastFrame; //frame lastTag is in
224 } instance_t;
226 typedef struct _outline {
227 SHAPE* shape;
228 SRECT bbox;
229 } outline_t;
231 typedef struct _gradient {
232 GRADIENT gradient;
233 char radial;
234 } gradient_t;
236 static void character_init(character_t*c)
238 memset(c, 0, sizeof(character_t));
240 static character_t* character_new()
242 character_t*c;
243 c = (character_t*)malloc(sizeof(character_t));
244 character_init(c);
245 return c;
247 static void instance_init(instance_t*i)
249 memset(i, 0, sizeof(instance_t));
251 static instance_t* instance_new()
253 instance_t*c;
254 c = (instance_t*)malloc(sizeof(instance_t));
255 instance_init(c);
256 return c;
259 static void incrementid()
261 while(idmap[++id]) {
262 if(id==65535)
263 syntaxerror("Out of character ids.");
265 idmap[id] = 1;
268 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
270 character_t* c = character_new();
272 c->definingTag = ctag;
273 c->id = id;
274 c->size = r;
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);
280 swf_SetU16(tag, id);
281 swf_SetString(tag, name);
282 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
283 swf_SetU16(tag, 1);
284 swf_SetU16(tag, id);
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;
291 c->id = id;
292 c->size = r;
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();
301 i->character = c;
302 i->depth = depth;
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);
307 return 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)
312 p->x = x; p->y = y;
313 p->scalex = scalex; p->scaley = scaley;
314 p->pin = pin; p->pivot = pivot;
315 p->rotate = rotate; p->cxform = cxform;
316 p->shear = shear;
319 static void parameters_clear(parameters_t*p)
321 p->x = 0; p->y = 0;
322 p->scalex = 1.0; p->scaley = 1.0;
323 p->pin.x = 0; //1??
324 p->pin.y = 0;
325 p->pivot.x = 0; p->pivot.y = 0;
326 p->rotate = 0;
327 p->shear = 0;
328 swf_GetCXForm(0, &p->cxform, 1);
331 static void makeMatrix(MATRIX*m, parameters_t*p)
333 SPOINT h;
334 float sx,r1,r0,sy;
336 /* /sx r1\ /x\
337 * \r0 sy/ \y/
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);
350 m->tx = m->ty = 0;
352 h = swf_TurnPoint(p->pin, m);
353 m->tx = p->x - h.x;
354 m->ty = p->y - h.y;
357 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
359 MATRIX m;
360 SRECT r;
361 makeMatrix(&m, p);
362 r = swf_TurnRect(rect, &m);
363 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
364 currentrect.xmax == 0 && currentrect.ymax == 0)
365 currentrect = r;
366 else
367 swf_ExpandRect2(&currentrect, &r);
368 return m;
371 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
373 SWF*swf = (SWF*)malloc(sizeof(SWF));
375 if(stackpos)
376 syntaxerror(".swf blocks can't be nested");
378 memset(swf, 0, sizeof(swf));
379 swf->fileVersion = version;
380 swf->movieSize = r;
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;
402 stackpos++;
403 id = 0;
405 currentframe = 0;
406 memset(&currentrect, 0, sizeof(currentrect));
407 currentdepth = 1;
409 memset(idmap, 0, sizeof(idmap));
410 incrementid();
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);
431 currentframe = 0;
432 currentdepth = 1;
433 memset(&currentrect, 0, sizeof(currentrect));
435 stackpos++;
436 incrementid();
439 typedef struct _buttonrecord
441 U16 id;
442 MATRIX matrix;
443 CXFORM cxform;
444 char set;
445 } buttonrecord_t;
447 typedef struct _button
449 int endofshapes;
450 int nr_actions;
451 buttonrecord_t records[4];
452 } button_t;
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(&currentrect, 0, sizeof(currentrect));
472 stackpos++;
473 incrementid();
475 void s_buttonput(char*character, char*as, parameters_t p)
477 character_t* c = dictionary_lookup(&characters, character);
478 MATRIX m;
479 int flags = 0;
480 char*o = as,*s = as;
481 buttonrecord_t r;
482 if(!stackpos || (stack[stackpos-1].type != 3)) {
483 syntaxerror(".show may only appear in .button");
485 if(!c) {
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);
494 r.id = c->id;
495 r.matrix = m;
496 r.cxform = p.cxform;
497 r.set = 1;
499 while(1) {
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));
508 if(!*s)
509 break;
510 s++;
513 static void setbuttonrecords(TAG*tag)
515 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
516 if(!mybutton.endofshapes) {
517 int t;
519 if(!mybutton.records[3].set) {
520 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
523 for(t=0;t<4;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)
535 ActionTAG* a = 0;
536 if(flags==0) {
537 return;
539 setbuttonrecords(stack[stackpos-1].tag);
541 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
542 if(!a) {
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++;
550 swf_ActionFree(a);
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()
568 SRECT r;
569 setbuttonrecords(stack[stackpos-1].tag);
570 setactionend(stack[stackpos-1].tag);
571 stackpos--;
573 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
575 r = currentrect;
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;
587 while(from!=to) {
588 TAG*next = from->next;
589 swf_DeleteTag(from);
590 from = next;
592 save->next = 0;
593 return save;
596 static void s_endSprite()
598 SRECT r = currentrect;
600 if(stack[stackpos].cut)
601 tag = removeFromTo(stack[stackpos].cut, tag);
603 stackpos--;
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;
617 swf_FoldSprite(tag);
618 if(tag->next != 0)
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()
627 int fi;
628 SWF* swf;
629 char*filename;
631 if(stack[stackpos].cut)
632 tag = removeFromTo(stack[stackpos].cut, tag);
634 stackpos--;
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);
655 if(fi<0) {
656 syntaxerror("couldn't create output file %s", filename);
658 if(swf->compressed)
659 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
660 else
661 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
663 close(fi);
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);
673 swf_FreeTags(swf);
674 free(swf);
675 free(filename);
678 void s_close()
680 if(stackpos) {
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");
690 int s_getframe()
692 return currentframe;
695 void s_frame(int nr, int cut, char*name)
697 int t;
698 TAG*now = tag;
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);
708 if(cut) {
709 if(now == tag) {
710 syntaxerror("Can't cut, frame empty");
712 stack[stackpos].cut = tag;
715 currentframe = nr;
718 int parseColor2(char*str, RGBA*color);
720 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
722 RGBA color;
723 character_t*image;
724 gradient_t*gradient;
725 if(texture[0] == '#') {
726 parseColor2(texture, &color);
727 return swf_ShapeAddSolidFillStyle(s, &color);
728 } else if((image = dictionary_lookup(&images, texture))) {
729 MATRIX m;
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;
733 m.tx = r->xmin;
734 m.ty = r->ymin;
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))) {
738 MATRIX m;
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);
747 } else {
748 syntaxerror("not a color/fillstyle: %s", texture);
749 return 0;
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)
756 SRECT r,r2;
757 SHAPE* s;
758 int ls1,fs1=0;
759 r2.xmin = 0;
760 r2.ymin = 0;
761 r2.xmax = width;
762 r2.ymax = height;
763 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
764 swf_ShapeNew(&s);
765 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
767 if(texture)
768 fs1 = addFillStyle(s, &r2, texture);
770 swf_SetU16(tag,id);
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;
775 swf_SetRect(tag,&r);
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);
783 swf_ShapeFree(s);
785 s_addcharacter(name, id, tag, r);
786 incrementid();
789 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
791 SRECT rect,r2;
792 SHAPE* s;
793 int ls1,fs1=0;
794 outline_t* outline;
795 outline = dictionary_lookup(&outlines, outlinename);
796 if(!outline) {
797 syntaxerror("outline %s not defined", outlinename);
799 r2 = outline->bbox;
801 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
802 swf_ShapeNew(&s);
803 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
804 if(texture)
805 fs1 = addFillStyle(s, &r2, texture);
806 else
807 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
808 swf_SetU16(tag,id);
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);
818 swf_ShapeFree(s);
820 s_addcharacter(name, id, tag, rect);
821 incrementid();
824 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
826 SRECT rect,r2;
827 SHAPE* s;
828 int ls1,fs1=0;
829 r2.xmin = r2.ymin = 0;
830 r2.xmax = 2*r;
831 r2.ymax = 2*r;
833 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
834 swf_ShapeNew(&s);
835 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
836 if(texture)
837 fs1 = addFillStyle(s, &r2, texture);
838 swf_SetU16(tag,id);
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);
849 swf_ShapeFree(s);
851 s_addcharacter(name, id, tag, rect);
852 incrementid();
855 void s_textshape(char*name, char*fontname, float size, char*_text)
857 int g;
858 U8*text = (U8*)_text;
859 outline_t* outline;
861 SWFFONT*font;
862 font = dictionary_lookup(&fonts, fontname);
863 if(!font)
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);
869 return;
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];
879 drawer_t draw;
880 swf_Shape11DrawerInit(&draw, 0);
881 swf_DrawText(&draw, font, (int)(size*100), _text);
882 draw.finish(&draw);
883 outline->shape = swf_ShapeDrawerToShape(&draw);
884 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
885 draw.dealloc(&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)
895 SRECT r;
896 MATRIX _m,*m=0;
897 SWFFONT*font;
898 font = dictionary_lookup(&fonts, fontname);
899 if(!font)
900 syntaxerror("font \"%s\" not known!", fontname);
902 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
903 swf_SetU16(tag, id);
904 if(!font->numchars) {
905 s_box(name, 0, 0, black, 20, 0);
906 return;
908 r = swf_SetDefineText(tag, font, &color, text, size);
910 s_addcharacter(name, id, tag, r);
911 incrementid();
914 void s_edittext(char*name, char*fontname, int size, int width, int height, char*text, RGBA*color, int maxlength, char*variable, int flags)
916 SWFFONT*font;
917 EditTextLayout layout;
918 SRECT r;
920 font = dictionary_lookup(&fonts, fontname);
921 if(!font)
922 syntaxerror("font \"%s\" not known!", fontname);
923 tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
924 swf_SetU16(tag, id);
925 layout.align = 0;
926 layout.leftmargin = 0;
927 layout.rightmargin = 0;
928 layout.indent = 0;
929 layout.leading = 0;
930 r.xmin = 0;
931 r.ymin = 0;
932 r.xmax = width;
933 r.ymax = height;
934 swf_SetEditText(tag, flags|ET_USEOUTLINES, r, text, color, maxlength, font->id, size, &layout, variable);
936 s_addcharacter(name, id, tag, r);
937 incrementid();
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 */
948 SRECT r;
949 int imageID = id;
950 int width, height;
951 if(type=="png") {
952 warning("image type \"png\" not supported yet!");
953 s_box(name, 0, 0, black, 20, 0);
954 return;
956 if(type=="jpeg") {
957 #ifndef HAVE_LIBJPEG
958 warning("no jpeg support compiled in");
959 s_box(name, 0, 0, black, 20, 0);
960 return;
961 #else
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);
971 r.xmin = 0;
972 r.ymin = 0;
973 r.xmax = width*20;
974 r.ymax = height*20;
976 s_addimage(name, id, tag, r);
977 incrementid();
978 #endif
981 /* step 2: the character */
982 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
983 swf_SetU16(tag, id);
984 swf_ShapeSetBitmapRect(tag, imageID, width, height);
986 s_addcharacter(name, id, tag, r);
987 incrementid();
990 void dumpSWF(SWF*swf)
992 TAG* tag = swf->firstTag;
993 printf("vvvvvvvvvvvvvvvvvvvvv\n");
994 while(tag) {
995 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
996 tag = tag->next;
998 printf("^^^^^^^^^^^^^^^^^^^^^\n");
1001 void s_font(char*name, char*filename)
1003 SWFFONT* font;
1004 font = swf_LoadFont(filename);
1006 if(font == 0) {
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);
1011 return;
1014 if(0)
1016 /* fix the layout. Only needed for old fonts */
1017 int t;
1018 for(t=0;t<font->numchars;t++) {
1019 font->glyph[t].advance = 0;
1021 font->layout = 0;
1022 swf_FontCreateLayout(font);
1024 /* just in case this thing is used in .edittext later on */
1025 swf_FontPrepareForEditText(font);
1027 font->id = id;
1028 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1029 swf_FontSetDefine2(tag, font);
1030 incrementid();
1032 if(dictionary_lookup(&fonts, name))
1033 syntaxerror("font %s defined twice", name);
1034 dictionary_put2(&fonts, name, font);
1039 typedef struct _sound_t
1041 U16 id;
1042 TAG*tag;
1043 } sound_t;
1045 void s_sound(char*name, char*filename)
1047 struct WAV wav, wav2;
1048 sound_t* sound;
1049 U16*samples;
1050 int numsamples;
1052 if(!readWAV(filename, &wav)) {
1053 warning("Couldn't read wav file \"%s\"", filename);
1054 samples = 0;
1055 numsamples = 0;
1056 } else {
1057 convertWAV2mono(&wav, &wav2, 44100);
1058 samples = (U16*)wav2.data;
1059 numsamples = wav2.size/2;
1060 free(wav.data);
1063 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1064 swf_SetU16(tag, id); //id
1065 swf_SetSoundDefine(tag, samples, numsamples);
1067 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1068 sound->tag = tag;
1069 sound->id = id;
1071 if(dictionary_lookup(&sounds, name))
1072 syntaxerror("sound %s defined twice", name);
1073 dictionary_put2(&sounds, name, sound);
1075 incrementid();
1077 if(samples)
1078 free(samples);
1081 static char* gradient_getToken(const char**p)
1083 const char*start;
1084 char*result;
1085 while(**p && strchr(" \t\n\r", **p)) {
1086 (*p)++;
1088 start = *p;
1089 while(**p && !strchr(" \t\n\r", **p)) {
1090 (*p)++;
1092 result = malloc((*p)-start+1);
1093 memcpy(result,start,(*p)-start+1);
1094 result[(*p)-start] = 0;
1095 return result;
1098 float parsePercent(char*str);
1099 RGBA parseColor(char*str);
1101 GRADIENT parseGradient(const char*str)
1103 GRADIENT gradient;
1104 const char* p = str;
1105 memset(&gradient, 0, sizeof(GRADIENT));
1106 while(*p) {
1107 char*posstr,*colorstr;
1108 float pos;
1109 RGBA color;
1110 posstr = gradient_getToken(&p);
1111 if(!*posstr)
1112 break;
1113 pos = parsePercent(posstr);
1114 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1115 colorstr = gradient_getToken(&p);
1116 color = parseColor(colorstr);
1117 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1118 warning("gradient record too big- max size is 8, rest ignored");
1119 break;
1121 gradient.ratios[gradient.num] = (int)(pos*255.0);
1122 gradient.rgba[gradient.num] = color;
1123 gradient.num++;
1124 free(posstr);
1125 free(colorstr);
1127 return gradient;
1130 void s_gradient(char*name, const char*text, int radial)
1132 gradient_t* gradient;
1133 gradient = malloc(sizeof(gradient_t));
1134 memset(gradient, 0, sizeof(gradient_t));
1135 gradient->gradient = parseGradient(text);
1136 gradient->radial = radial;
1138 if(dictionary_lookup(&gradients, name))
1139 syntaxerror("gradient %s defined twice", name);
1140 dictionary_put2(&gradients, name, gradient);
1143 void s_action(const char*text)
1145 ActionTAG* a = 0;
1146 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1147 if(!a) {
1148 syntaxerror("Couldn't compile ActionScript");
1151 tag = swf_InsertTag(tag, ST_DOACTION);
1153 swf_ActionSet(tag, a);
1155 swf_ActionFree(a);
1158 int s_swf3action(char*name, char*action)
1160 ActionTAG* a = 0;
1161 instance_t* object = dictionary_lookup(&instances, name);
1162 if(!object) {
1163 return 0;
1165 a = action_SetTarget(0, name);
1166 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1167 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1168 else if(!strcmp(action, "stop")) a = action_Stop(a);
1169 else if(!strcmp(action, "play")) a = action_Play(a);
1170 a = action_SetTarget(a, "");
1171 a = action_End(a);
1173 tag = swf_InsertTag(tag, ST_DOACTION);
1174 swf_ActionSet(tag, a);
1175 swf_ActionFree(a);
1176 return 1;
1179 void s_outline(char*name, char*format, char*source)
1181 outline_t* outline;
1183 drawer_t draw;
1184 SHAPE* shape;
1185 SHAPE2* shape2;
1186 SRECT bounds;
1188 swf_Shape11DrawerInit(&draw, 0);
1189 draw_string(&draw, source);
1190 draw.finish(&draw);
1191 shape = swf_ShapeDrawerToShape(&draw);
1192 //shape2 = swf_ShapeToShape2(shape);
1193 //bounds = swf_GetShapeBoundingBox(shape2);
1194 //swf_Shape2Free(shape2);
1195 bounds = swf_ShapeDrawerGetBBox(&draw);
1196 draw.dealloc(&draw);
1198 outline = (outline_t*)malloc(sizeof(outline_t));
1199 memset(outline, 0, sizeof(outline_t));
1200 outline->shape = shape;
1201 outline->bbox = bounds;
1203 if(dictionary_lookup(&outlines, name))
1204 syntaxerror("outline %s defined twice", name);
1205 dictionary_put2(&outlines, name, outline);
1208 int s_playsound(char*name, int loops, int nomultiple, int stop)
1210 sound_t* sound = dictionary_lookup(&sounds, name);
1211 SOUNDINFO info;
1212 if(!sound)
1213 return 0;
1215 tag = swf_InsertTag(tag, ST_STARTSOUND);
1216 swf_SetU16(tag, sound->id); //id
1217 memset(&info, 0, sizeof(info));
1218 info.stop = stop;
1219 info.loops = loops;
1220 info.nomultiple = nomultiple;
1221 swf_SetSoundInfo(tag, &info);
1222 return 1;
1225 void s_includeswf(char*name, char*filename)
1227 int f;
1228 SWF swf;
1229 TAG* ftag;
1230 SRECT r;
1231 TAG* s;
1232 int level = 0;
1233 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1234 f = open(filename,O_RDONLY);
1235 if (f<0) {
1236 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1237 s_box(name, 0, 0, black, 20, 0);
1238 return;
1240 if (swf_ReadSWF(f,&swf)<0) {
1241 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1242 s_box(name, 0, 0, black, 20, 0);
1243 return;
1245 close(f);
1247 /* FIXME: The following sets the bounding Box for the character.
1248 It is wrong for two reasons:
1249 a) It may be too small (in case objects in the movie clip at the borders)
1250 b) it may be too big (because the poor movie never got autocropped)
1252 r = swf.movieSize;
1254 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1255 swf_SetU16(tag, id);
1256 swf_SetU16(tag, 0);
1258 swf_Relocate(&swf, idmap);
1260 ftag = swf.firstTag;
1261 level = 1;
1262 while(ftag) {
1263 int t;
1264 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1265 if(cutout[t] == ftag->id) {
1266 ftag = ftag->next;
1267 continue;
1269 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1270 level++;
1271 if(ftag->id == ST_END)
1272 level--;
1273 if(!level)
1274 break;
1275 /* We simply dump all tags right after the sprite
1276 header, relying on the fact that swf_OptimizeTagOrder() will
1277 sort things out for us later.
1278 We also rely on the fact that the imported SWF is well-formed.
1280 tag = swf_InsertTag(tag, ftag->id);
1281 swf_SetBlock(tag, ftag->data, ftag->len);
1282 ftag = ftag->next;
1284 if(!ftag)
1285 syntaxerror("Included file %s contains errors", filename);
1286 tag = swf_InsertTag(tag, ST_END);
1288 swf_FreeTags(&swf);
1290 s_addcharacter(name, id, tag, r);
1291 incrementid();
1293 SRECT s_getCharBBox(char*name)
1295 character_t* c = dictionary_lookup(&characters, name);
1296 if(!c) syntaxerror("character '%s' unknown(2)", name);
1297 return c->size;
1299 SRECT s_getInstanceBBox(char*name)
1301 instance_t * i = dictionary_lookup(&instances, name);
1302 character_t * c;
1303 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1304 c = i->character;
1305 if(!c) syntaxerror("internal error(5)");
1306 return c->size;
1308 parameters_t s_getParameters(char*name)
1310 instance_t * i = dictionary_lookup(&instances, name);
1311 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1312 return i->parameters;
1314 void s_startclip(char*instance, char*character, parameters_t p)
1316 character_t* c = dictionary_lookup(&characters, character);
1317 instance_t* i;
1318 MATRIX m;
1319 if(!c) {
1320 syntaxerror("character %s not known", character);
1322 i = s_addinstance(instance, c, currentdepth);
1323 i->parameters = p;
1324 m = s_instancepos(i->character->size, &p);
1326 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1327 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1328 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1329 i->lastTag = tag;
1330 i->lastFrame= currentframe;
1332 stack[stackpos].tag = tag;
1333 stack[stackpos].type = 2;
1334 stackpos++;
1336 currentdepth++;
1338 void s_endClip()
1340 SWFPLACEOBJECT p;
1341 stackpos--;
1342 swf_SetTagPos(stack[stackpos].tag, 0);
1343 swf_GetPlaceObject(stack[stackpos].tag, &p);
1344 p.clipdepth = currentdepth;
1345 p.name = 0;
1346 swf_ClearTag(stack[stackpos].tag);
1347 swf_SetPlaceObject(stack[stackpos].tag, &p);
1348 currentdepth++;
1351 void s_put(char*instance, char*character, parameters_t p)
1353 character_t* c = dictionary_lookup(&characters, character);
1354 instance_t* i;
1355 MATRIX m;
1356 if(!c) {
1357 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1360 i = s_addinstance(instance, c, currentdepth);
1361 i->parameters = p;
1362 m = s_instancepos(i->character->size, &p);
1364 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1365 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1366 i->lastTag = tag;
1367 i->lastFrame = currentframe;
1368 currentdepth++;
1371 void s_jump(char*instance, parameters_t p)
1373 instance_t* i = dictionary_lookup(&instances, instance);
1374 MATRIX m;
1375 if(!i) {
1376 syntaxerror("instance %s not known", instance);
1379 i->parameters = p;
1380 m = s_instancepos(i->character->size, &p);
1382 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1383 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1384 i->lastTag = tag;
1385 i->lastFrame = currentframe;
1388 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1390 parameters_t p;
1391 float ratio;
1392 if(num==0 || num==1)
1393 return *p1;
1394 ratio = (float)pos/(float)num;
1396 p.x = (p2->x-p1->x)*ratio + p1->x;
1397 p.y = (p2->y-p1->y)*ratio + p1->y;
1398 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1399 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1400 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1401 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1403 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1404 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1405 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1406 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1408 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1409 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1410 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1411 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1413 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1414 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1415 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1416 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1417 return p;
1420 void s_change(char*instance, parameters_t p2)
1422 instance_t* i = dictionary_lookup(&instances, instance);
1423 MATRIX m;
1424 parameters_t p1;
1425 TAG*t;
1426 int frame, allframes;
1427 if(!i) {
1428 syntaxerror("instance %s not known", instance);
1430 p1 = i->parameters;
1432 allframes = currentframe - i->lastFrame - 1;
1433 if(allframes < 0) {
1434 warning(".change ignored. can only .put/.change an object once per frame.");
1435 return;
1438 m = s_instancepos(i->character->size, &p2);
1439 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1440 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1441 i->parameters = p2;
1443 /* o.k., we got the start and end point set. Now iterate though all the
1444 tags in between, inserting object changes after each new frame */
1445 t = i->lastTag;
1446 i->lastTag = tag;
1447 if(!t) syntaxerror("internal error(6)");
1448 frame = 0;
1449 while(frame < allframes) {
1450 if(t->id == ST_SHOWFRAME) {
1451 parameters_t p;
1452 MATRIX m;
1453 TAG*lt;
1454 frame ++;
1455 p = s_interpolate(&p1, &p2, frame, allframes);
1456 m = s_instancepos(i->character->size, &p); //needed?
1457 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1458 i->lastFrame = currentframe;
1459 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1460 t = lt;
1461 if(frame == allframes)
1462 break;
1464 t = t->next;
1465 if(!t)
1466 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1470 void s_delinstance(char*instance)
1472 instance_t* i = dictionary_lookup(&instances, instance);
1473 if(!i) {
1474 syntaxerror("instance %s not known", instance);
1476 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1477 swf_SetU16(tag, i->depth);
1478 dictionary_del(&instances, instance);
1481 void s_qchange(char*instance, parameters_t p)
1485 void s_end()
1487 if(!stackpos)
1488 syntaxerror(".end unexpected");
1489 if(stack[stackpos-1].type == 0)
1490 s_endSWF();
1491 else if(stack[stackpos-1].type == 1)
1492 s_endSprite();
1493 else if(stack[stackpos-1].type == 2)
1494 s_endClip();
1495 else if(stack[stackpos-1].type == 3)
1496 s_endButton();
1497 else syntaxerror("internal error 1");
1500 // ------------------------------------------------------------------------
1502 typedef int command_func_t(map_t*args);
1504 SRECT parseBox(char*str)
1506 SRECT r;
1507 float xmin, xmax, ymin, ymax;
1508 char*x = strchr(str, 'x');
1509 char*d1=0,*d2=0;
1510 if(!strcmp(str, "autocrop")) {
1511 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1512 return r;
1514 if(!x) goto error;
1515 d1 = strchr(x+1, ':');
1516 if(d1)
1517 d2 = strchr(d1+1, ':');
1518 if(!d1) {
1519 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1520 goto error;
1521 xmin = ymin = 0;
1523 else if(d1 && !d2) {
1524 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1525 goto error;
1526 xmax += xmin;
1527 ymin = 0;
1529 else {
1530 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1531 goto error;
1532 xmax += xmin;
1533 ymax += ymin;
1535 r.xmin = (SCOORD)(xmin*20);
1536 r.ymin = (SCOORD)(ymin*20);
1537 r.xmax = (SCOORD)(xmax*20);
1538 r.ymax = (SCOORD)(ymax*20);
1539 return r;
1540 error:
1541 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1542 return r;
1544 float parseFloat(char*str)
1546 return atof(str);
1548 int parseInt(char*str)
1550 int t;
1551 int l=strlen(str);
1552 int s=0;
1553 if(str[0]=='+' || str[0]=='-')
1554 s++;
1556 for(t=s;t<l;t++)
1557 if(str[t]<'0' || str[t]>'9')
1558 syntaxerror("Not an Integer: \"%s\"", str);
1559 return atoi(str);
1561 int parseTwip(char*str)
1563 char*dot;
1564 int sign=1;
1565 if(str[0]=='+' || str[0]=='-') {
1566 if(str[0]=='-')
1567 sign = -1;
1568 str++;
1570 dot = strchr(str, '.');
1571 if(!dot) {
1572 int l=strlen(str);
1573 int t;
1574 return sign*parseInt(str)*20;
1575 } else {
1576 int l=strlen(++dot);
1577 char*s;
1578 for(s=str;s<dot-1;s++)
1579 if(*s<'0' || *s>'9')
1580 syntaxerror("Not a coordinate: \"%s\"", str);
1581 for(s=dot;*s;s++) {
1582 if(*s<'0' || *s>'9')
1583 syntaxerror("Not a coordinate: \"%s\"", str);
1585 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1586 warning("precision loss: %s converted to twip", str);
1587 dot[2] = 0;
1588 l=2;
1590 if(l==0)
1591 return sign*atoi(str)*20;
1592 if(l==1)
1593 return sign*atoi(str)*20+atoi(dot)*2;
1594 if(l==2)
1595 return sign*atoi(str)*20+atoi(dot)/5;
1597 return 0;
1600 int isPoint(char*str)
1602 if(strchr(str, '('))
1603 return 1;
1604 else
1605 return 0;
1608 SPOINT parsePoint(char*str)
1610 SPOINT p;
1611 char tmp[80];
1612 int l = strlen(str);
1613 char*comma = strchr(str, ',');
1614 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1615 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1616 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1617 p.x = parseTwip(tmp);
1618 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1619 p.y = parseTwip(tmp);
1620 return p;
1623 int parseColor2(char*str, RGBA*color)
1625 int l = strlen(str);
1626 int r,g,b,a;
1627 int t;
1629 struct {unsigned char r,g,b;char*name;} colors[] =
1630 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1631 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1632 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1633 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1634 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1635 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1636 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1637 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1638 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1639 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1640 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1641 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1643 a=255;r=g=b=0;
1645 if(str[0]=='#' && (l==7 || l==9)) {
1646 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1647 return 0;
1648 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1649 return 0;
1650 color->r = r; color->g = g; color->b = b; color->a = a;
1651 return 1;
1653 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1654 if(!strcmp(str, colors[t].name)) {
1655 r = colors[t].r;
1656 g = colors[t].g;
1657 b = colors[t].b;
1658 a = 255;
1659 color->r = r; color->g = g; color->b = b; color->a = a;
1660 return 1;
1662 return 0;
1665 RGBA parseColor(char*str)
1667 RGBA c;
1668 if(!parseColor2(str, &c))
1669 syntaxerror("Expression '%s' is not a color", str);
1670 return c;
1673 typedef struct _muladd {
1674 S16 mul;
1675 S16 add;
1676 } MULADD;
1678 MULADD parseMulAdd(char*str)
1680 float add, mul;
1681 char* str2 = (char*)malloc(strlen(str)+5);
1682 int i;
1683 MULADD m;
1684 strcpy(str2, str);
1685 strcat(str2, " 0");
1686 add = 0;
1687 mul = 1.0;
1688 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1689 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1690 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1691 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1692 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1693 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1694 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1695 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1696 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1697 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1698 else {
1699 syntaxerror("'%s' is not a valid color transform expression", str);
1701 m.add = (int)(add*256);
1702 m.mul = (int)(mul*256);
1703 free(str2);
1704 return m;
1707 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1709 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1710 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1711 MULADD r;
1712 if(a<-32768) a=-32768;
1713 if(a>32767) a=32767;
1714 if(m<-32768) m=-32768;
1715 if(m>32767) m=32767;
1716 r.add = a;
1717 r.mul = (int)m;
1718 return r;
1721 float parsePercent(char*str)
1723 int l = strlen(str);
1724 if(!l)
1725 return 1.0;
1726 if(str[l-1]=='%') {
1727 return atoi(str)/100.0;
1729 syntaxerror("Expression '%s' is not a percentage", str);
1730 return 0;
1732 int isPercent(char*str)
1734 return str[strlen(str)-1]=='%';
1736 int parseNewSize(char*str, int size)
1738 if(isPercent(str))
1739 return parsePercent(str)*size;
1740 else
1741 return (int)(atof(str)*20);
1744 int isColor(char*str)
1746 RGBA c;
1747 return parseColor2(str, &c);
1750 static char* lu(map_t* args, char*name)
1752 char* value = map_lookup(args, name);
1753 if(!value) {
1754 map_dump(args, stdout, "");
1755 syntaxerror("internal error 2: value %s should be set", name);
1757 return value;
1760 static int c_flash(map_t*args)
1762 char* name = lu(args, "name");
1763 char* compressstr = lu(args, "compress");
1764 SRECT bbox = parseBox(lu(args, "bbox"));
1765 int version = parseInt(lu(args, "version"));
1766 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1767 int compress = 0;
1768 RGBA color = parseColor(lu(args, "background"));
1769 if(!strcmp(name, "!default!") || override_outputname)
1770 name = outputname;
1772 if(!strcmp(compressstr, "default"))
1773 compress = version==6;
1774 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1775 compress = 1;
1776 else if(!strcmp(compressstr, "no"))
1777 compress = 0;
1778 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1780 s_swf(name, bbox, version, fps, compress, color);
1781 return 0;
1783 int isRelative(char*str)
1785 return !strncmp(str, "<plus>", 6) ||
1786 !strncmp(str, "<minus>", 7);
1788 char* getOffset(char*str)
1790 if(!strncmp(str, "<plus>", 6))
1791 return str+6;
1792 if(!strncmp(str, "<minus>", 7))
1793 return str+7;
1794 syntaxerror("internal error (347)");
1795 return 0;
1797 int getSign(char*str)
1799 if(!strncmp(str, "<plus>", 6))
1800 return 1;
1801 if(!strncmp(str, "<minus>", 7))
1802 return -1;
1803 syntaxerror("internal error (348)");
1804 return 0;
1806 static dictionary_t points;
1807 static mem_t mpoints;
1808 int points_initialized = 0;
1810 SPOINT getPoint(SRECT r, char*name)
1812 int l=0;
1813 if(!strcmp(name, "center")) {
1814 SPOINT p;
1815 p.x = (r.xmin + r.xmax)/2;
1816 p.y = (r.ymin + r.ymax)/2;
1817 return p;
1820 if(points_initialized)
1821 l = (int)dictionary_lookup(&points, name);
1822 if(l==0) {
1823 syntaxerror("Invalid point: \"%s\".", name);
1825 l--;
1826 return *(SPOINT*)&mpoints.buffer[l];
1828 static int c_gradient(map_t*args)
1830 char*name = lu(args, "name");
1831 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1833 readToken();
1834 if(type != RAWDATA)
1835 syntaxerror("colon (:) expected");
1837 s_gradient(name, text, radial);
1838 return 0;
1840 static int c_point(map_t*args)
1842 char*name = lu(args, "name");
1843 int pos;
1844 string_t s1;
1845 SPOINT p;
1846 if(!points_initialized) {
1847 dictionary_init(&points);
1848 mem_init(&mpoints);
1849 points_initialized = 1;
1851 p.x = parseTwip(lu(args, "x"));
1852 p.y = parseTwip(lu(args, "y"));
1853 pos = mem_put(&mpoints, &p, sizeof(p));
1854 string_set(&s1, name);
1855 pos++;
1856 dictionary_put(&points, s1, (void*)pos);
1857 return 0;
1859 static int c_play(map_t*args)
1861 char*name = lu(args, "name");
1862 char*loop = lu(args, "loop");
1863 char*nomultiple = lu(args, "nomultiple");
1864 int nm = 0;
1865 if(!strcmp(nomultiple, "nomultiple"))
1866 nm = 1;
1867 else
1868 nm = parseInt(nomultiple);
1870 if(s_playsound(name, parseInt(loop), nm, 0)) {
1871 return 0;
1872 } else if(s_swf3action(name, "play")) {
1873 return 0;
1875 return 0;
1878 static int c_stop(map_t*args)
1880 char*name = lu(args, "name");
1882 if(s_playsound(name, 0,0,1)) {
1883 return 0;
1884 } else if(s_swf3action(name, "stop")) {
1885 return 0;
1887 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1888 return 0;
1891 static int c_nextframe(map_t*args)
1893 char*name = lu(args, "name");
1895 if(s_swf3action(name, "nextframe")) {
1896 return 0;
1898 syntaxerror("I don't know anything about movie \"%s\"", name);
1899 return 0;
1902 static int c_previousframe(map_t*args)
1904 char*name = lu(args, "name");
1906 if(s_swf3action(name, "previousframe")) {
1907 return 0;
1909 syntaxerror("I don't know anything about movie \"%s\"", name);
1910 return 0;
1913 static int c_placement(map_t*args, int type)
1915 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1916 char*character = 0;
1918 char* luminancestr = lu(args, "luminance");
1919 char* scalestr = lu(args, "scale");
1920 char* scalexstr = lu(args, "scalex");
1921 char* scaleystr = lu(args, "scaley");
1922 char* rotatestr = lu(args, "rotate");
1923 char* shearstr = lu(args, "shear");
1924 char* xstr="", *pivotstr="";
1925 char* ystr="", *anglestr="";
1926 char*above = lu(args, "above"); /*FIXME*/
1927 char*below = lu(args, "below");
1928 char* rstr = lu(args, "red");
1929 char* gstr = lu(args, "green");
1930 char* bstr = lu(args, "blue");
1931 char* astr = lu(args, "alpha");
1932 char* pinstr = lu(args, "pin");
1933 char* as = map_lookup(args, "as");
1934 MULADD r,g,b,a;
1935 float oldwidth;
1936 float oldheight;
1937 SRECT oldbbox;
1938 MULADD luminance;
1939 parameters_t p;
1941 if(type==9) { // (?) .rotate or .arcchange
1942 pivotstr = lu(args, "pivot");
1943 anglestr = lu(args, "angle");
1944 } else {
1945 xstr = lu(args, "x");
1946 ystr = lu(args, "y");
1948 if(luminancestr[0])
1949 luminance = parseMulAdd(luminancestr);
1950 else {
1951 luminance.add = 0;
1952 luminance.mul = 256;
1955 if(scalestr[0]) {
1956 if(scalexstr[0]||scaleystr[0])
1957 syntaxerror("scalex/scaley and scale cannot both be set");
1958 scalexstr = scaleystr = scalestr;
1961 if(type == 0 || type == 4) {
1962 // put or startclip
1963 character = lu(args, "character");
1964 parameters_clear(&p);
1965 } else if (type == 5) {
1966 character = lu(args, "name");
1967 parameters_clear(&p);
1968 // button's show
1969 } else {
1970 p = s_getParameters(instance);
1973 /* x,y position */
1974 if(xstr[0]) {
1975 if(isRelative(xstr)) {
1976 if(type == 0 || type == 4)
1977 syntaxerror("relative x values not allowed for initial put or startclip");
1978 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1979 } else {
1980 p.x = parseTwip(xstr);
1983 if(ystr[0]) {
1984 if(isRelative(ystr)) {
1985 if(type == 0 || type == 4)
1986 syntaxerror("relative y values not allowed for initial put or startclip");
1987 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1988 } else {
1989 p.y = parseTwip(ystr);
1993 /* scale, scalex, scaley */
1994 if(character) {
1995 oldbbox = s_getCharBBox(character);
1996 } else {
1997 oldbbox = s_getInstanceBBox(instance);
1999 oldwidth = oldbbox.xmax - oldbbox.xmin;
2000 oldheight = oldbbox.ymax - oldbbox.ymin;
2001 if(scalexstr[0]) {
2002 if(oldwidth==0) p.scalex = 1.0;
2003 else {
2004 if(scalexstr[0])
2005 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2008 if(scaleystr[0]) {
2009 if(oldheight==0) p.scaley = 1.0;
2010 else {
2011 if(scaleystr[0])
2012 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2016 /* rotation */
2017 if(rotatestr[0]) {
2018 if(isRelative(rotatestr)) {
2019 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2020 } else {
2021 p.rotate = parseFloat(rotatestr);
2025 /* shearing */
2026 if(shearstr[0]) {
2027 if(isRelative(shearstr)) {
2028 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2029 } else {
2030 p.shear = parseFloat(shearstr);
2034 if(pivotstr[0]) {
2035 if(isPoint(pivotstr))
2036 p.pivot = parsePoint(pivotstr);
2037 else
2038 p.pivot = getPoint(oldbbox, pivotstr);
2040 if(pinstr[0]) {
2041 if(isPoint(pinstr))
2042 p.pin = parsePoint(pinstr);
2043 else
2044 p.pin = getPoint(oldbbox, pinstr);
2047 /* color transform */
2049 if(rstr[0] || luminancestr[0]) {
2050 MULADD r;
2051 if(rstr[0])
2052 r = parseMulAdd(rstr);
2053 else {
2054 r.add = p.cxform.r0;
2055 r.mul = p.cxform.r1;
2057 r = mergeMulAdd(r, luminance);
2058 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2060 if(gstr[0] || luminancestr[0]) {
2061 MULADD g;
2062 if(gstr[0])
2063 g = parseMulAdd(gstr);
2064 else {
2065 g.add = p.cxform.g0;
2066 g.mul = p.cxform.g1;
2068 g = mergeMulAdd(g, luminance);
2069 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2071 if(bstr[0] || luminancestr[0]) {
2072 MULADD b;
2073 if(bstr[0])
2074 b = parseMulAdd(bstr);
2075 else {
2076 b.add = p.cxform.b0;
2077 b.mul = p.cxform.b1;
2079 b = mergeMulAdd(b, luminance);
2080 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2082 if(astr[0]) {
2083 MULADD a = parseMulAdd(astr);
2084 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2087 if(type == 0)
2088 s_put(instance, character, p);
2089 else if(type == 1)
2090 s_change(instance, p);
2091 else if(type == 2)
2092 s_qchange(instance, p);
2093 else if(type == 3)
2094 s_jump(instance, p);
2095 else if(type == 4)
2096 s_startclip(instance, character, p);
2097 else if(type == 5) {
2098 if(as && as[0]) {
2099 s_buttonput(character, as, p);
2100 } else {
2101 s_buttonput(character, "shape", p);
2104 return 0;
2106 static int c_put(map_t*args)
2108 c_placement(args, 0);
2109 return 0;
2111 static int c_change(map_t*args)
2113 c_placement(args, 1);
2114 return 0;
2116 static int c_qchange(map_t*args)
2118 c_placement(args, 2);
2119 return 0;
2121 static int c_arcchange(map_t*args)
2123 c_placement(args, 2);
2124 return 0;
2126 static int c_jump(map_t*args)
2128 c_placement(args, 3);
2129 return 0;
2131 static int c_startclip(map_t*args)
2133 c_placement(args, 4);
2134 return 0;
2136 static int c_show(map_t*args)
2138 c_placement(args, 5);
2139 return 0;
2141 static int c_del(map_t*args)
2143 char*instance = lu(args, "name");
2144 s_delinstance(instance);
2145 return 0;
2147 static int c_end(map_t*args)
2149 s_end();
2150 return 0;
2152 static int c_sprite(map_t*args)
2154 char* name = lu(args, "name");
2155 s_sprite(name);
2156 return 0;
2158 static int c_frame(map_t*args)
2160 char*framestr = lu(args, "n");
2161 char*cutstr = lu(args, "cut");
2162 char*name = lu(args, "name");
2163 int frame;
2164 int cut = 0;
2165 if(strcmp(cutstr, "no"))
2166 cut = 1;
2167 if(isRelative(framestr)) {
2168 frame = s_getframe();
2169 if(getSign(framestr)<0)
2170 syntaxerror("relative frame expressions must be positive");
2171 frame += parseInt(getOffset(framestr));
2173 else {
2174 frame = parseInt(framestr);
2175 if(s_getframe() >= frame
2176 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2177 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2179 s_frame(frame, cut, name);
2180 return 0;
2182 static int c_primitive(map_t*args)
2184 char*name = lu(args, "name");
2185 char*command = lu(args, "commandname");
2186 int width=0, height=0, r=0;
2187 int linewidth = parseTwip(lu(args, "line"));
2188 char*colorstr = lu(args, "color");
2189 RGBA color = parseColor(colorstr);
2190 char*fillstr = lu(args, "fill");
2191 int dofill = 1;
2192 int type=0;
2193 char* font;
2194 char* text;
2195 char* outline=0;
2196 RGBA fill;
2197 if(!strcmp(command, "circle"))
2198 type = 1;
2199 else if(!strcmp(command, "filled"))
2200 type = 2;
2202 if(type==0) {
2203 width = parseTwip(lu(args, "width"));
2204 height = parseTwip(lu(args, "height"));
2205 } else if (type==1) {
2206 r = parseTwip(lu(args, "r"));
2207 } else if (type==2) {
2208 outline = lu(args, "outline");
2211 if(!strcmp(fillstr, "fill"))
2212 fillstr = colorstr;
2213 if(!strcmp(fillstr, "none"))
2214 fillstr = 0;
2215 if(width<0 || height<0 || linewidth<0 || r<0)
2216 syntaxerror("values width, height, line, r must be positive");
2218 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2219 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2220 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2221 return 0;
2224 static int c_textshape(map_t*args)
2226 char*name = lu(args, "name");
2227 char*text = lu(args, "text");
2228 char*font = lu(args, "font");
2229 float size = parsePercent(lu(args, "size"));
2231 s_textshape(name, font, size, text);
2232 return 0;
2235 static int c_swf(map_t*args)
2237 char*name = lu(args, "name");
2238 char*filename = lu(args, "filename");
2239 char*command = lu(args, "commandname");
2240 if(!strcmp(command, "shape"))
2241 warning("Please use .swf instead of .shape");
2242 s_includeswf(name, filename);
2243 return 0;
2246 static int c_font(map_t*args)
2248 char*name = lu(args, "name");
2249 char*filename = lu(args, "filename");
2250 s_font(name, filename);
2251 return 0;
2254 static int c_sound(map_t*args)
2256 char*name = lu(args, "name");
2257 char*filename = lu(args, "filename");
2258 s_sound(name, filename);
2259 return 0;
2262 static int c_text(map_t*args)
2264 char*name = lu(args, "name");
2265 char*text = lu(args, "text");
2266 char*font = lu(args, "font");
2267 float size = parsePercent(lu(args, "size"));
2268 RGBA color = parseColor(lu(args, "color"));
2269 s_text(name, font, text, (int)(size*100), color);
2270 return 0;
2273 static int c_soundtrack(map_t*args)
2275 return 0;
2278 static int c_image(map_t*args)
2280 char*command = lu(args, "commandname");
2281 char*name = lu(args, "name");
2282 char*filename = lu(args, "filename");
2283 if(!strcmp(command,"jpeg")) {
2284 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2285 s_image(name, "jpeg", filename, quality);
2286 } else {
2287 s_image(name, "png", filename, 0);
2289 return 0;
2292 static int c_outline(map_t*args)
2294 char*name = lu(args, "name");
2295 char*format = lu(args, "format");
2297 readToken();
2298 if(type != RAWDATA)
2299 syntaxerror("colon (:) expected");
2301 s_outline(name, format, text);
2302 return 0;
2305 int fakechar(map_t*args)
2307 char*name = lu(args, "name");
2308 s_box(name, 0, 0, black, 20, 0);
2309 return 0;
2312 static int c_egon(map_t*args) {return fakechar(args);}
2313 static int c_button(map_t*args) {
2314 char*name = lu(args, "name");
2315 s_button(name);
2316 return 0;
2318 static int current_button_flags = 0;
2319 static int c_on_press(map_t*args)
2321 char*position = lu(args, "position");
2322 char*action = "";
2323 if(!strcmp(position, "inside")) {
2324 current_button_flags |= BC_OVERUP_OVERDOWN;
2325 } else if(!strcmp(position, "outside")) {
2326 //current_button_flags |= BC_IDLE_OUTDOWN;
2327 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2328 } else if(!strcmp(position, "anywhere")) {
2329 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2331 readToken();
2332 if(type == RAWDATA) {
2333 action = text;
2334 s_buttonaction(current_button_flags, action);
2335 current_button_flags = 0;
2337 else
2338 pushBack();
2339 return 0;
2341 static int c_on_release(map_t*args)
2343 char*position = lu(args, "position");
2344 char*action = "";
2345 if(!strcmp(position, "inside")) {
2346 current_button_flags |= BC_OVERDOWN_OVERUP;
2347 } else if(!strcmp(position, "outside")) {
2348 current_button_flags |= BC_OUTDOWN_IDLE;
2349 } else if(!strcmp(position, "anywhere")) {
2350 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2352 readToken();
2353 if(type == RAWDATA) {
2354 action = text;
2355 s_buttonaction(current_button_flags, action);
2356 current_button_flags = 0;
2358 else
2359 pushBack();
2360 return 0;
2362 static int c_on_move_in(map_t*args)
2364 char*position = lu(args, "state");
2365 char*action = "";
2366 if(!strcmp(position, "pressed")) {
2367 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2368 } else if(!strcmp(position, "not_pressed")) {
2369 current_button_flags |= BC_IDLE_OVERUP;
2370 } else if(!strcmp(position, "any")) {
2371 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2373 readToken();
2374 if(type == RAWDATA) {
2375 action = text;
2376 s_buttonaction(current_button_flags, action);
2377 current_button_flags = 0;
2379 else
2380 pushBack();
2381 return 0;
2383 static int c_on_move_out(map_t*args)
2385 char*position = lu(args, "state");
2386 char*action = "";
2387 if(!strcmp(position, "pressed")) {
2388 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2389 } else if(!strcmp(position, "not_pressed")) {
2390 current_button_flags |= BC_OVERUP_IDLE;
2391 } else if(!strcmp(position, "any")) {
2392 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2394 readToken();
2395 if(type == RAWDATA) {
2396 action = text;
2397 s_buttonaction(current_button_flags, action);
2398 current_button_flags = 0;
2400 else
2401 pushBack();
2402 return 0;
2404 static int c_on_key(map_t*args)
2406 char*key = lu(args, "key");
2407 char*action = "";
2408 if(strlen(key)==1) {
2409 /* ascii */
2410 if(key[0]>=32) {
2411 current_button_flags |= 0x4000 + (key[0]*0x200);
2412 } else {
2413 syntaxerror("invalid character: %c"+key[0]);
2414 return 1;
2416 } else {
2417 /* TODO:
2418 <ctrl-x> = 0x200*(x-'a')
2419 esc = = 0x3600
2420 space = = 0x4000;
2422 syntaxerror("invalid key: %s",key);
2424 readToken();
2425 if(type == RAWDATA) {
2426 action = text;
2427 s_buttonaction(current_button_flags, action);
2428 current_button_flags = 0;
2430 else
2431 pushBack();
2432 return 0;
2435 static int c_edittext(map_t*args)
2437 //"name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
2438 char*name = lu(args, "name");
2439 char*font = lu(args, "font");
2440 int size = (int)(1024*parsePercent(lu(args, "size")));
2441 int width = parseTwip(lu(args, "width"));
2442 int height = parseTwip(lu(args, "height"));
2443 char*text = lu(args, "text");
2444 RGBA color = parseColor(lu(args, "color"));
2445 int maxlength = parseInt(lu(args, "maxlength"));
2446 char*variable = lu(args, "variable");
2447 char*passwordstr = lu(args, "password");
2448 char*wordwrapstr = lu(args, "wordwrap");
2449 char*multilinestr = lu(args, "multiline");
2450 char*htmlstr = lu(args, "html");
2451 char*noselectstr = lu(args, "noselect");
2452 char*readonlystr = lu(args, "readonly");
2453 char*borderstr = lu(args, "border");
2455 int flags = 0;
2456 if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2457 if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2458 if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2459 if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2460 if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2461 if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2462 if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2464 s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags);
2465 return 0;
2468 static int c_morphshape(map_t*args) {return fakechar(args);}
2469 static int c_movie(map_t*args) {return fakechar(args);}
2471 static int c_texture(map_t*args) {return 0;}
2473 static int c_action(map_t*args)
2475 readToken();
2476 if(type != RAWDATA) {
2477 syntaxerror("colon (:) expected");
2480 s_action(text);
2482 return 0;
2485 static struct {
2486 char*command;
2487 command_func_t* func;
2488 char*arguments;
2489 } arguments[] =
2490 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2491 {"frame", c_frame, "n=<plus>1 name= @cut=no"},
2492 // "import" type stuff
2493 {"swf", c_swf, "name filename"},
2494 {"shape", c_swf, "name filename"},
2495 {"jpeg", c_image, "name filename quality=80%"},
2496 {"png", c_image, "name filename"},
2497 {"movie", c_movie, "name filename"},
2498 {"sound", c_sound, "name filename"},
2499 {"font", c_font, "name filename"},
2500 {"soundtrack", c_soundtrack, "filename"},
2502 // generators of primitives
2504 {"point", c_point, "name x=0 y=0"},
2505 {"gradient", c_gradient, "name @radial=0"},
2506 {"outline", c_outline, "name format=simple"},
2507 {"textshape", c_textshape, "name font size=100% text"},
2509 // character generators
2510 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2511 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2512 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2514 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2515 {"text", c_text, "name text font size=100% color=white"},
2516 {"edittext", c_edittext, "name font size=100% width height text="" color=white maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0 @border=0"},
2517 {"morphshape", c_morphshape, "name start end"},
2518 {"button", c_button, "name"},
2519 {"show", c_show, "name x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below= as="},
2520 {"on_press", c_on_press, "position=inside"},
2521 {"on_release", c_on_release, "position=anywhere"},
2522 {"on_move_in", c_on_move_in, "state=not_pressed"},
2523 {"on_move_out", c_on_move_out, "state=not_pressed"},
2524 {"on_key", c_on_key, "key=any"},
2526 // control tags
2527 {"play", c_play, "name loop=0 @nomultiple=0"},
2528 {"stop", c_stop, "name"},
2529 {"nextframe", c_nextframe, "name"},
2530 {"previousframe", c_previousframe, "name"},
2532 // object placement tags
2533 {"put", c_put, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2534 {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2535 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2536 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2537 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2538 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2539 {"del", c_del, "name"},
2540 // virtual object placement
2541 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2543 // commands which start a block
2544 //startclip (see above)
2545 {"sprite", c_sprite, "name"},
2546 {"action", c_action, ""},
2548 {"end", c_end, ""}
2552 static map_t parseArguments(char*command, char*pattern)
2554 char*x;
2555 char*d,*e;
2557 string_t name[64];
2558 string_t value[64];
2559 int set[64];
2560 int isboolean[64];
2561 int pos;
2562 int len;
2563 int t;
2564 string_t t1,t2;
2565 map_t result;
2566 map_init(&result);
2568 string_set(&t1, "commandname");
2569 string_set(&t2, command);
2570 map_put(&result, t1, t2);
2572 if(!pattern || !*pattern)
2573 return result;
2575 x = pattern;
2577 pos = 0;
2579 if(!strncmp("<i> ", x, 3)) {
2580 readToken();
2581 if(type == COMMAND || type == RAWDATA) {
2582 pushBack();
2583 syntaxerror("character name expected");
2585 name[pos].str = "instance";
2586 name[pos].len = 8;
2587 value[pos].str = text;
2588 value[pos].len = strlen(text);
2589 set[pos] = 1;
2590 pos++;
2592 if(type == ASSIGNMENT)
2593 readToken();
2595 name[pos].str = "character";
2596 name[pos].len = 9;
2597 value[pos].str = text;
2598 value[pos].len = strlen(text);
2599 set[pos] = 1;
2600 pos++;
2602 x+=4;
2605 while(*x) {
2606 isboolean[pos] = (x[0] =='@');
2607 if(isboolean[pos])
2608 x++;
2610 d = strchr(x, ' ');
2611 e = strchr(x, '=');
2612 if(!d)
2613 d=&x[strlen(x)];
2614 set[pos] = 0;
2616 if(!e || d<e) {
2617 // no default
2618 name[pos].str = x;
2619 name[pos].len = d-x;
2620 value[pos].str = 0;
2621 value[pos].len = 0;
2622 } else {
2623 name[pos].str = x;
2624 name[pos].len = e-x;
2625 value[pos].str = e+1;
2626 value[pos].len = d-e-1;
2628 pos++;
2629 if(!*d) break;
2630 x=d+1;
2632 len = pos;
2634 /* for(t=0;t<len;t++) {
2635 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2636 isboolean[t]?"(boolean)":"");
2639 while(1) {
2640 readToken();
2641 if(type == RAWDATA || type == COMMAND) {
2642 pushBack();
2643 break;
2646 // first, search for boolean arguments
2647 for(pos=0;pos<len;pos++)
2649 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2650 set[pos] = 1;
2651 if(type == ASSIGNMENT)
2652 readToken();
2653 value[pos].str = text;
2654 value[pos].len = strlen(text);
2655 /*printf("setting boolean parameter %s (to %s)\n",
2656 strdup_n(name[pos], namelen[pos]),
2657 strdup_n(value[pos], valuelen[pos]));*/
2658 break;
2662 // second, search for normal arguments
2663 if(pos==len)
2664 for(pos=0;pos<len;pos++)
2666 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2667 (type != ASSIGNMENT && !set[pos])) {
2668 if(set[pos]) {
2669 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2671 if(type == ASSIGNMENT)
2672 readToken();
2673 set[pos] = 1;
2674 value[pos].str = text;
2675 value[pos].len = strlen(text);
2676 #if 0//def DEBUG
2677 printf("setting parameter %s (to %s)\n",
2678 strdup_n(name[pos].str, name[pos].len),
2679 strdup_n(value[pos].str, value[pos].len));
2680 #endif
2681 break;
2684 if(pos==len) {
2685 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2688 #if 0//def DEBUG
2689 for(t=0;t<len;t++) {
2690 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2692 #endif
2693 for(t=0;t<len;t++) {
2694 if(value[t].str && value[t].str[0] == '*') {
2695 //relative default- take value from some other parameter
2696 int s;
2697 for(s=0;s<len;s++) {
2698 if(value[s].len == value[t].len-1 &&
2699 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2700 value[t].str = value[s].str;
2703 if(value[t].str == 0) {
2704 pushBack();
2705 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2709 /* ok, now construct the dictionary from the parameters */
2711 for(t=0;t<len;t++)
2713 map_put(&result, name[t], value[t]);
2715 return result;
2717 static void parseArgumentsForCommand(char*command)
2719 int t;
2720 map_t args;
2721 int nr = -1;
2722 msg("<verbose> parse Command: %s (line %d)", command, line);
2724 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2725 if(!strcmp(arguments[t].command, command)) {
2727 /* ugly hack- will be removed soon (once documentation and .sc generating
2728 utilities have been changed) */
2729 if(!strcmp(command, "swf") && !stackpos) {
2730 warning("Please use .flash instead of .swf- this will be mandatory soon");
2731 command = "flash";
2732 t = 0;
2735 args = parseArguments(command, arguments[t].arguments);
2736 nr = t;
2737 break;
2740 if(nr<0)
2741 syntaxerror("command %s not known", command);
2743 // catch missing .flash directives at the beginning of a file
2744 if(strcmp(command, "flash") && !stackpos)
2746 syntaxerror("No movie defined- use .flash first");
2749 #ifdef DEBUG
2750 printf(".%s\n", command);fflush(stdout);
2751 map_dump(&args, stdout, "\t");fflush(stdout);
2752 #endif
2754 (*arguments[nr].func)(&args);
2756 /*if(!strcmp(command, "button") ||
2757 !strcmp(command, "action")) {
2758 while(1) {
2759 readToken();
2760 if(type == COMMAND) {
2761 if(!strcmp(text, "end"))
2762 break;
2763 else {
2764 pushBack();
2765 break;
2771 map_clear(&args);
2772 return;
2775 int main (int argc,char ** argv)
2777 int t;
2778 processargs(argc, argv);
2779 initLog(0,-1,0,0,-1,verbose);
2781 if(!filename) {
2782 args_callback_usage(argv[0]);
2783 exit(1);
2786 file = generateTokens(filename);
2787 if(!file) {
2788 printf("parser returned error.\n");
2789 return 1;
2791 pos=0;
2792 t=0;
2794 while(!noMoreTokens()) {
2795 readToken();
2796 if(type != COMMAND)
2797 syntaxerror("command expected");
2798 parseArgumentsForCommand(text);
2801 s_close();
2802 freeTokens(file);
2804 return 0;