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 file is distributed under the GPL, see file COPYING for details */
16 #define logf logarithmf // logf is also used by ../lib/log.h
19 #include "../config.h"
20 #include "../lib/rfxswf.h"
21 #include "../lib/log.h"
22 #include "../lib/args.h"
28 static char * filename = 0;
29 static char * outputname = "output.swf";
30 static int verbose = 2;
32 static struct options_t options[] =
40 int args_callback_option(char*name,char*val)
42 if(!strcmp(name, "V")) {
43 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
46 else if(!strcmp(name, "o")) {
50 else if(!strcmp(name, "v")) {
55 printf("Unknown option: -%s\n", name);
60 int args_callback_longoption(char*name,char*val)
62 return args_long2shortoption(options, name, val);
64 void args_callback_usage(char*name)
66 printf("Usage: %s [-o filename] file.wav\n", name);
67 printf("\t-v , --verbose\t\t\t Be more verbose\n");
68 printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
69 printf("\t-V , --version\t\t\t Print program version and exit\n");
71 int args_callback_command(char*name,char*val)
74 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
81 static struct token_t* file;
90 static void syntaxerror(char*format, ...)
94 va_start(arglist, format);
95 vsprintf(buf, format, arglist);
97 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
101 static void warning(char*format, ...)
105 va_start(arglist, format);
106 vsprintf(buf, format, arglist);
108 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
111 static void readToken()
113 type = file[pos].type;
115 syntaxerror("unexpected end of file");
117 text = file[pos].text;
118 textlen = strlen(text);
119 line = file[pos].line;
120 column = file[pos].column;
122 //printf("---> %d(%s) %s\n", type, type_names[type], text);
125 static void pushBack()
128 if(!pos) syntaxerror("internal error 3");
133 textlen = strlen(text);
136 column = file[p].column;
139 static int noMoreTokens()
141 if(file[pos].type == END)
146 // ------------------------------ swf routines ----------------------------
150 int type; //0=swf, 1=sprite, 2=clip
156 /* for sprites (1): */
162 dictionary_t oldinstances;
166 static int stackpos = 0;
168 static dictionary_t characters;
169 static char idmap[65536];
170 static TAG*tag = 0; //current tag
172 static int id; //current character id
173 static int currentframe; //current frame in current level
174 static SRECT currentrect; //current bounding box in current level
175 static U16 currentdepth;
176 static dictionary_t instances;
177 static dictionary_t fonts;
179 typedef struct _parameters {
181 float scalex, scaley;
188 typedef struct _character {
194 typedef struct _instance {
195 character_t*character;
197 parameters_t parameters;
198 TAG* lastTag; //last tag which set the object
199 U16 lastFrame; //frame lastTag is in
202 static void character_init(character_t*c)
204 memset(c, 0, sizeof(character_t));
206 static character_t* character_new()
209 c = (character_t*)malloc(sizeof(character_t));
213 static void instance_init(instance_t*i)
215 memset(i, 0, sizeof(instance_t));
217 static instance_t* instance_new()
220 c = (instance_t*)malloc(sizeof(instance_t));
225 static void incrementid()
229 syntaxerror("Out of character ids.");
234 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
236 character_t* c = character_new();
238 c->definingTag = ctag;
241 if(dictionary_lookup(&characters, name))
242 syntaxerror("character %s defined twice", name);
243 dictionary_put2(&characters, name, c);
245 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
247 swf_SetString(tag, name);
248 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
251 swf_SetString(tag, name);
253 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
255 instance_t* i = instance_new();
258 //swf_GetMatrix(0, &i->matrix);
259 if(dictionary_lookup(&instances, name))
260 syntaxerror("object %s defined twice", name);
261 dictionary_put2(&instances, name, i);
265 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, SPOINT pivot, SPOINT pin, CXFORM cxform)
268 p->scalex = scalex; p->scaley = scaley;
269 p->pin = pin; p->pivot = pivot;
270 p->rotate = rotate; p->cxform = cxform;
273 static void parameters_clear(parameters_t*p)
276 p->scalex = 1.0; p->scaley = 1.0;
277 p->pin.x = 1; p->pin.y = 0;
278 p->pivot.x = 0; p->pivot.y = 0;
280 swf_GetCXForm(0, &p->cxform, 1);
283 static void makeMatrix(MATRIX*m, parameters_t*p)
286 m->sx = p->scalex*cos(p->rotate/360*2*3.14159265358979)*65535;
287 m->r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)*65535;
288 m->r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979)*65535;
289 m->sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)*65535;
292 h = swf_TurnPoint(p->pin, m);
297 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
302 r = swf_TurnRect(i->character->size, &m);
303 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
304 currentrect.xmax == 0 && currentrect.ymax == 0)
307 swf_ExpandRect2(¤trect, &r);
311 void s_swf(char*name, SRECT r, int version, int fps, int compress)
313 SWF*swf = (SWF*)malloc(sizeof(SWF));
317 syntaxerror(".swf blocks can't be nested");
319 memset(swf, 0, sizeof(swf));
320 swf->fileVersion = version;
322 swf->frameRate = fps;
323 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
324 swf->compressed = compress;
325 rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
326 swf_SetRGB(tag,&rgb);
328 if(stackpos==sizeof(stack)/sizeof(stack[0]))
329 syntaxerror("too many levels of recursion");
331 dictionary_init(&characters);
332 dictionary_init(&instances);
333 dictionary_init(&fonts);
335 memset(&stack[stackpos], 0, sizeof(stack[0]));
336 stack[stackpos].type = 0;
337 stack[stackpos].filename = strdup(name);
338 stack[stackpos].swf = swf;
339 stack[stackpos].oldframe = -1;
344 memset(¤trect, 0, sizeof(currentrect));
347 memset(idmap, 0, sizeof(idmap));
351 void s_sprite(char*name)
353 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
354 swf_SetU16(tag, id); //id
355 swf_SetU16(tag, 0); //frames
357 memset(&stack[stackpos], 0, sizeof(stack[0]));
358 stack[stackpos].type = 1;
359 stack[stackpos].oldframe = currentframe;
360 stack[stackpos].olddepth = currentdepth;
361 stack[stackpos].oldrect = currentrect;
362 stack[stackpos].oldinstances = instances;
363 stack[stackpos].tag = tag;
364 stack[stackpos].id = id;
365 stack[stackpos].name = strdup(name);
367 /* FIXME: those four fields should be bundled together */
368 dictionary_init(&instances);
371 memset(¤trect, 0, sizeof(currentrect));
377 static void s_endSprite()
379 SRECT r = currentrect;
382 /* TODO: before clearing, prepend "<spritename>." to names and
383 copy into old instances dict */
384 dictionary_clear(&instances);
386 currentframe = stack[stackpos].oldframe;
387 currentrect = stack[stackpos].oldrect;
388 currentdepth = stack[stackpos].olddepth;
389 instances = stack[stackpos].oldinstances;
390 tag = swf_InsertTag(tag, ST_END);
392 tag = stack[stackpos].tag;
395 syntaxerror("internal error(7)");
397 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
398 free(stack[stackpos].name);
401 static void s_endSWF()
408 swf = stack[stackpos].swf;
409 filename = stack[stackpos].filename;
411 tag = swf_InsertTag(tag, ST_SHOWFRAME);
412 tag = swf_InsertTag(tag, ST_END);
414 swf_OptimizeTagOrder(swf);
416 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
417 swf->movieSize = currentrect; /* "autocrop" */
419 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
421 syntaxerror("couldn't create output file %s", filename);
424 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
426 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
430 dictionary_clear(&instances);
431 dictionary_clear(&characters);
432 dictionary_clear(&fonts);
441 if(stack[stackpos-1].type == 0)
442 syntaxerror("End of file encountered in .swf block");
443 if(stack[stackpos-1].type == 1)
444 syntaxerror("End of file encountered in .sprite block");
445 if(stack[stackpos-1].type == 2)
446 syntaxerror("End of file encountered in .clip block");
458 for(t=currentframe;t<nr;t++) {
459 tag = swf_InsertTag(tag, ST_SHOWFRAME);
464 RGBA black={r:0,g:0,b:0,a:0};
465 void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill)
470 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
472 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
474 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
476 r.xmin = -linewidth-linewidth/2;
477 r.ymin = -linewidth-linewidth/2;
478 r.xmax = width+linewidth+linewidth/2;
479 r.ymax = height+linewidth+linewidth/2;
481 swf_SetShapeHeader(tag,s);
482 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
483 swf_ShapeSetLine(tag,s,width,0);
484 swf_ShapeSetLine(tag,s,0,height);
485 swf_ShapeSetLine(tag,s,-width,0);
486 swf_ShapeSetLine(tag,s,0,-height);
487 swf_ShapeSetEnd(tag);
490 s_addcharacter(name, id, tag, r);
494 void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill)
499 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
501 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
503 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
505 rect.xmin = -linewidth-linewidth/2;
506 rect.ymin = -linewidth-linewidth/2;
507 rect.xmax = 2*r+linewidth+linewidth/2;
508 rect.ymax = 2*r+linewidth+linewidth/2;
510 swf_SetRect(tag,&rect);
511 swf_SetShapeHeader(tag,s);
512 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
513 swf_ShapeSetCircle(tag, s, r,r,r,r);
514 swf_ShapeSetEnd(tag);
517 s_addcharacter(name, id, tag, rect);
521 void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill)
527 U8*text = (U8*)_text;
530 font = dictionary_lookup(&fonts, fontname);
532 syntaxerror("font \"%s\" not known!", fontname);
534 syntaxerror("textshapes must be filled", fontname);
536 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
537 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
538 s_box(name, 0, 0, black, 20, black, 0);
541 g = font->ascii2glyph[text[0]];
542 rect = font->layout->bounds[g];
545 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
547 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
549 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
551 swf_SetRect(tag, &rect);
552 swf_SetShapeStyles(tag, s);
553 swf_SetSimpleShape(tag, font->glyph[g].shape);
556 s_addcharacter(name, id, tag, rect);
559 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
564 font = dictionary_lookup(&fonts, fontname);
566 syntaxerror("font \"%s\" not known!", fontname);
568 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
570 if(!font->numchars) {
571 s_box(name, 0, 0, black, 20, black, 0);
574 r = swf_SetDefineText(tag, font, &color, text, size);
576 s_addcharacter(name, id, tag, r);
580 void dumpSWF(SWF*swf)
582 TAG* tag = swf->firstTag;
583 printf("vvvvvvvvvvvvvvvvvvvvv\n");
585 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
588 printf("^^^^^^^^^^^^^^^^^^^^^\n");
591 void s_font(char*name, char*filename)
596 f = open(filename,O_RDONLY);
598 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
599 font = (SWFFONT*)malloc(sizeof(SWFFONT));
600 memset(font, 0, sizeof(SWFFONT));
601 dictionary_put2(&fonts, name, font);
605 if (swf_ReadSWF(f,&swf)>=0) {
606 swf_FontExtract(&swf, 0x4e46, &font);
611 syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
616 /* fix the layout. Only needed for old fonts */
618 for(t=0;t<font->numchars;t++) {
619 font->glyph[t].advance = 0;
622 swf_FontCreateLayout(font);
626 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
627 swf_FontSetDefine2(tag, font);
630 if(dictionary_lookup(&fonts, name))
631 syntaxerror("font %s defined twice", name);
632 dictionary_put2(&fonts, name, font);
635 void s_shape(char*name, char*filename)
643 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
644 f = open(filename,O_RDONLY);
646 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
647 s_box(name, 0, 0, black, 20, black, 0);
650 if (swf_ReadSWF(f,&swf)<0) {
651 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
652 s_box(name, 0, 0, black, 20, black, 0);
657 /* FIXME: The following sets the bounding Box for the character.
658 It is wrong for two reasons:
659 a) It may be too small (in case objects in the movie clip at the borders)
660 b) it may be too big (because the poor movie never got autocropped)
664 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
668 swf_Relocate(&swf, idmap);
674 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
675 if(cutout[t] == ftag->id) {
679 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
681 if(ftag->id == ST_END)
685 /* We simply dump all tags right after the sprite
686 header, relying on the fact that swf_OptimizeTagOrder() will
687 sort things out for us later.
688 We also rely on the fact that the imported SWF is well-formed.
690 tag = swf_InsertTag(tag, ftag->id);
691 swf_SetBlock(tag, ftag->data, ftag->len);
695 syntaxerror("Included file %s contains errors", filename);
696 tag = swf_InsertTag(tag, ST_END);
700 s_addcharacter(name, id, tag, r);
703 SRECT s_getCharBBox(char*name)
705 character_t* c = dictionary_lookup(&characters, name);
706 if(!c) syntaxerror("character '%s' unknown(2)", name);
709 SRECT s_getInstanceBBox(char*name)
711 instance_t * i = dictionary_lookup(&instances, name);
713 if(!i) syntaxerror("instance '%s' unknown(4)", name);
715 if(!c) syntaxerror("internal error(5)");
718 parameters_t s_getParameters(char*name)
720 instance_t * i = dictionary_lookup(&instances, name);
721 if(!i) syntaxerror("instance '%s' unknown(10)", name);
722 return i->parameters;
724 void s_startclip(char*instance, char*character, parameters_t p)
726 character_t* c = dictionary_lookup(&characters, character);
730 syntaxerror("character %s not known", character);
732 i = s_addinstance(instance, c, currentdepth);
734 m = s_instancepos(i, &p);
736 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
737 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
738 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
740 i->lastFrame= currentframe;
742 stack[stackpos].tag = tag;
743 stack[stackpos].type = 2;
752 swf_SetTagPos(stack[stackpos].tag, 0);
753 swf_GetPlaceObject(stack[stackpos].tag, &p);
754 p.clipdepth = currentdepth;
755 swf_ClearTag(stack[stackpos].tag);
756 swf_SetPlaceObject(stack[stackpos].tag, &p);
760 void s_put(char*instance, char*character, parameters_t p)
762 character_t* c = dictionary_lookup(&characters, character);
766 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
769 i = s_addinstance(instance, c, currentdepth);
771 m = s_instancepos(i, &p);
773 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
774 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
776 i->lastFrame = currentframe;
780 void s_jump(char*instance, parameters_t p)
782 instance_t* i = dictionary_lookup(&instances, instance);
785 syntaxerror("instance %s not known", instance);
789 m = s_instancepos(i, &p);
791 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
792 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
794 i->lastFrame = currentframe;
797 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
803 ratio = (float)pos/(float)num;
805 p.x = (p2->x-p1->x)*ratio + p1->x;
806 p.y = (p2->y-p1->y)*ratio + p1->y;
807 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
808 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
809 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
811 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
812 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
813 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
814 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
816 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
817 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
818 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
819 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
821 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
822 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
823 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
824 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
828 void s_change(char*instance, parameters_t p2)
830 instance_t* i = dictionary_lookup(&instances, instance);
834 int frame, allframes;
836 syntaxerror("instance %s not known", instance);
840 allframes = currentframe - i->lastFrame - 1;
842 warning(".change ignored. can only .put/.change an object once per frame.");
846 m = s_instancepos(i, &p2);
847 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
848 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
851 /* o.k., we got the start and end point set. Now iterate though all the
852 tags in between, inserting object changes after each new frame */
855 if(!t) syntaxerror("internal error(6)");
857 while(frame < allframes) {
858 if(t->id == ST_SHOWFRAME) {
863 p = s_interpolate(&p1, &p2, frame, allframes);
864 m = s_instancepos(i, &p); //needed?
865 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
866 i->lastFrame = currentframe;
867 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
869 if(frame == allframes)
874 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
878 void s_delinstance(char*instance)
880 instance_t* i = dictionary_lookup(&instances, instance);
882 syntaxerror("instance %s not known", instance);
884 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
885 swf_SetU16(tag, i->depth);
886 dictionary_del(&instances, instance);
889 void s_qchange(char*instance, parameters_t p)
896 syntaxerror(".end unexpected");
897 if(stack[stackpos-1].type == 0)
899 else if(stack[stackpos-1].type == 1)
901 else if(stack[stackpos-1].type == 2)
903 else syntaxerror("internal error 1");
906 // ------------------------------------------------------------------------
908 typedef int command_func_t(map_t*args);
910 SRECT parseBox(char*str)
913 float xmin, xmax, ymin, ymax;
914 char*x = strchr(str, 'x');
916 if(!strcmp(str, "autocrop")) {
917 r.xmin = r.ymin = r.xmax = r.ymax = 0;
921 d1 = strchr(x+1, ':');
923 d2 = strchr(d1+1, ':');
925 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
930 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
936 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
941 r.xmin = (SCOORD)(xmin*20);
942 r.ymin = (SCOORD)(ymin*20);
943 r.xmax = (SCOORD)(xmax*20);
944 r.ymax = (SCOORD)(ymax*20);
947 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
950 float parseFloat(char*str)
954 int parseInt(char*str)
959 if(str[0]=='+' || str[0]=='-')
963 if(str[t]<'0' || str[t]>'9')
964 syntaxerror("Not an Integer: \"%s\"", str);
967 int parseTwip(char*str)
969 char*dot = strchr(str, '.');
973 return parseInt(str)*20;
977 for(s=str;s<dot-1;s++)
979 syntaxerror("Not a coordinate: \"%s\"", str);
982 syntaxerror("Not a coordinate: \"%s\"", str);
984 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
985 warning("precision loss: %s converted to twip", str);
992 return atoi(str)*20+atoi(dot)*2;
994 return atoi(str)*20+atoi(dot)/5;
999 int isPoint(char*str)
1001 if(strchr(str, '('))
1007 SPOINT parsePoint(char*str)
1011 int l = strlen(str);
1012 char*comma = strchr(str, ',');
1013 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1014 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1015 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1016 p.x = parseTwip(tmp);
1017 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1018 p.y = parseTwip(tmp);
1022 int parseColor2(char*str, RGBA*color)
1024 int l = strlen(str);
1027 char*names[8] = {"black", "blue", "green", "cyan",
1028 "red", "violet", "yellow", "white"};
1031 if(str[0]=='#' && (l==7 || l==9)) {
1032 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1034 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1036 color->r = r; color->g = g; color->b = b; color->a = a;
1040 if(!strcmp(str, names[t])) {
1047 color->r = r; color->g = g; color->b = b; color->a = a;
1053 RGBA parseColor(char*str)
1056 if(!parseColor2(str, &c))
1057 syntaxerror("Expression '%s' is not a color", str);
1061 typedef struct _muladd {
1066 MULADD parseMulAdd(char*str)
1069 char* str2 = (char*)malloc(strlen(str)+5);
1076 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1077 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1078 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1079 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1080 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1081 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1082 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1083 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1084 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1085 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1087 syntaxerror("'%s' is not a valid color transform expression", str);
1089 m.add = (int)(add*256);
1090 m.mul = (int)(mul*256);
1095 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1097 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1098 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1100 if(a<-32768) a=-32768;
1101 if(a>32767) a=32767;
1102 if(m<-32768) m=-32768;
1103 if(m>32767) m=32767;
1109 float parsePercent(char*str)
1111 int l = strlen(str);
1115 return atoi(str)/100.0;
1117 syntaxerror("Expression '%s' is not a percentage", str);
1120 int isPercent(char*str)
1122 return str[strlen(str)-1]=='%';
1124 int parseNewSize(char*str, int size)
1127 return parsePercent(str)*size;
1129 return (int)(atof(str)*20);
1132 int isColor(char*str)
1135 return parseColor2(str, &c);
1138 static char* lu(map_t* args, char*name)
1140 char* value = map_lookup(args, name);
1142 syntaxerror("internal error 2: value %s should be set", name);
1147 static int c_swf(map_t*args)
1149 char* name = lu(args, "name");
1150 char* compressstr = lu(args, "compress");
1151 SRECT bbox = parseBox(lu(args, "bbox"));
1152 int version = parseInt(lu(args, "version"));
1153 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1155 if(!strcmp(name, "!default!"))
1158 if(!strcmp(compressstr, "default"))
1159 compress = version==6;
1160 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1162 else if(!strcmp(compressstr, "no"))
1164 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1166 s_swf(name, bbox, version, fps, compress);
1169 int isRelative(char*str)
1171 return !strncmp(str, "<plus>", 6) ||
1172 !strncmp(str, "<minus>", 7);
1174 char* getOffset(char*str)
1176 if(!strncmp(str, "<plus>", 6))
1178 if(!strncmp(str, "<minus>", 7))
1180 syntaxerror("internal error (347)");
1183 int getSign(char*str)
1185 if(!strncmp(str, "<plus>", 6))
1187 if(!strncmp(str, "<minus>", 7))
1189 syntaxerror("internal error (348)");
1192 static dictionary_t points;
1193 static mem_t mpoints;
1194 int points_initialized = 0;
1196 SPOINT getPoint(SRECT r, char*name)
1199 if(!strcmp(name, "center")) {
1201 p.x = (r.xmin + r.xmax)/2;
1202 p.y = (r.ymin + r.ymax)/2;
1206 l = (int)dictionary_lookup(&points, name);
1208 syntaxerror("Couldn't find point \"%s\".", name);
1211 return *(SPOINT*)&mpoints.buffer[l];
1213 static int c_point(map_t*args)
1215 char*name = lu(args, "name");
1219 if(!points_initialized) {
1220 dictionary_init(&points);
1222 points_initialized = 1;
1224 p.x = parseTwip(lu(args, "x"));
1225 p.y = parseTwip(lu(args, "y"));
1226 pos = mem_put(&mpoints, &p, sizeof(p));
1227 string_set(&s1, name);
1229 dictionary_put(&points, s1, (void*)pos);
1232 static int c_placement(map_t*args, int type)
1234 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1237 char* luminancestr = lu(args, "luminance");
1238 char* scalestr = lu(args, "scale");
1239 char* scalexstr = lu(args, "scalex");
1240 char* scaleystr = lu(args, "scaley");
1241 char* rotatestr = lu(args, "rotate");
1242 char* xstr="", *pivotstr="";
1243 char* ystr="", *anglestr="";
1244 char*above = lu(args, "above"); /*FIXME*/
1245 char*below = lu(args, "below");
1246 char* rstr = lu(args, "red");
1247 char* gstr = lu(args, "green");
1248 char* bstr = lu(args, "blue");
1249 char* astr = lu(args, "alpha");
1250 char* pinstr = lu(args, "pin");
1259 pivotstr = lu(args, "pivot");
1260 anglestr = lu(args, "angle");
1262 xstr = lu(args, "x");
1263 ystr = lu(args, "y");
1266 luminance = parseMulAdd(luminancestr);
1269 luminance.mul = 256;
1273 if(scalexstr[0]||scaleystr[0])
1274 syntaxerror("scalex/scaley and scale cannot both be set");
1275 scalexstr = scaleystr = scalestr;
1278 if(type == 0 || type == 4) {
1280 character = lu(args, "character");
1281 parameters_clear(&p);
1283 p = s_getParameters(instance);
1288 if(isRelative(xstr)) {
1289 if(type == 0 || type == 4)
1290 syntaxerror("relative x values not allowed for initial put or startclip");
1291 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1293 p.x = parseTwip(xstr);
1297 if(isRelative(ystr)) {
1298 if(type == 0 || type == 4)
1299 syntaxerror("relative y values not allowed for initial put or startclip");
1300 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1302 p.y = parseTwip(ystr);
1306 /* scale, scalex, scaley */
1308 oldbbox = s_getCharBBox(character);
1310 oldbbox = s_getInstanceBBox(instance);
1312 oldwidth = oldbbox.xmax - oldbbox.xmin;
1313 oldheight = oldbbox.ymax - oldbbox.ymin;
1315 if(oldwidth==0) p.scalex = 1.0;
1318 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1322 if(oldheight==0) p.scaley = 1.0;
1325 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1331 if(isRelative(rotatestr)) {
1332 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1334 p.rotate = parseFloat(rotatestr);
1339 if(isPoint(pivotstr))
1340 p.pivot = parsePoint(pivotstr);
1342 p.pivot = getPoint(oldbbox, pivotstr);
1346 p.pin = parsePoint(pinstr);
1348 p.pin = getPoint(oldbbox, pinstr);
1351 /* color transform */
1353 if(rstr[0] || luminancestr[0]) {
1356 r = parseMulAdd(rstr);
1358 r.add = p.cxform.r0;
1359 r.mul = p.cxform.r1;
1361 r = mergeMulAdd(r, luminance);
1362 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1364 if(gstr[0] || luminancestr[0]) {
1367 g = parseMulAdd(gstr);
1369 g.add = p.cxform.g0;
1370 g.mul = p.cxform.g1;
1372 g = mergeMulAdd(g, luminance);
1373 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1375 if(bstr[0] || luminancestr[0]) {
1378 b = parseMulAdd(bstr);
1380 b.add = p.cxform.b0;
1381 b.mul = p.cxform.b1;
1383 b = mergeMulAdd(b, luminance);
1384 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1387 MULADD a = parseMulAdd(astr);
1388 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1392 s_put(instance, character, p);
1394 s_change(instance, p);
1396 s_qchange(instance, p);
1398 s_jump(instance, p);
1400 s_startclip(instance, character, p);
1403 static int c_put(map_t*args)
1405 c_placement(args, 0);
1408 static int c_change(map_t*args)
1410 c_placement(args, 1);
1413 static int c_qchange(map_t*args)
1415 c_placement(args, 2);
1418 static int c_arcchange(map_t*args)
1420 c_placement(args, 2);
1423 static int c_jump(map_t*args)
1425 c_placement(args, 3);
1428 static int c_startclip(map_t*args)
1430 c_placement(args, 4);
1433 static int c_del(map_t*args)
1435 char*instance = lu(args, "name");
1436 s_delinstance(instance);
1439 static int c_end(map_t*args)
1444 static int c_sprite(map_t*args)
1446 char* name = lu(args, "name");
1450 static int c_frame(map_t*args)
1452 char*framestr = lu(args, "n");
1454 if(framestr[0]=='+') {
1455 frame = s_getframe();
1456 frame += parseInt(framestr+1);
1459 frame = parseInt(framestr);
1460 if(s_getframe() >= frame
1461 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1462 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1467 static int c_primitive(map_t*args)
1469 char*name = lu(args, "name");
1470 char*command = lu(args, "commandname");
1471 int width=0, height=0, r=0;
1472 int linewidth = parseTwip(lu(args, "line"));
1473 char*colorstr = lu(args, "color");
1474 RGBA color = parseColor(colorstr);
1475 char*fillstr = lu(args, "fill");
1481 if(!strcmp(command, "circle"))
1483 else if(!strcmp(command, "textshape"))
1487 width = parseTwip(lu(args, "width"));
1488 height = parseTwip(lu(args, "height"));
1489 } else if (type==1) {
1490 r = parseTwip(lu(args, "r"));
1491 } else if (type==2) {
1492 text = lu(args, "text");
1493 font = lu(args, "font");
1496 if(!strcmp(fillstr, "fill"))
1498 if(!strcmp(fillstr, "none"))
1500 if(width<0 || height<0 || linewidth<0 || r<0)
1501 syntaxerror("values width, height, line, r must be positive");
1502 if(!dofill || isColor(fillstr)) {
1504 fill = parseColor(fillstr);
1506 /* FIXME - texture fill */
1507 fill.r = fill.g = 0;
1508 fill.b = fill.a = 255;
1509 warning("texture fill not supported yet. Filling with black.");
1511 if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1512 else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1513 else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1517 static int c_shape(map_t*args)
1519 char*name = lu(args, "name");
1520 char*filename = lu(args, "filename");
1521 s_shape(name, filename);
1525 static int c_font(map_t*args)
1527 char*name = lu(args, "name");
1528 char*filename = lu(args, "filename");
1529 s_font(name, filename);
1533 static int c_text(map_t*args)
1535 char*name = lu(args, "name");
1536 char*text = lu(args, "text");
1537 char*font = lu(args, "font");
1538 float size = parsePercent(lu(args, "size"));
1539 RGBA color = parseColor(lu(args, "color"));
1540 s_text(name, font, text, (int)(size*100), color);
1544 int fakechar(map_t*args)
1546 char*name = lu(args, "name");
1547 s_box(name, 0, 0, black, 20, black, 0);
1550 static int c_circle(map_t*args) {return fakechar(args);}
1552 static int c_egon(map_t*args) {return fakechar(args);}
1553 static int c_button(map_t*args) {return fakechar(args);}
1554 static int c_edittext(map_t*args) {return fakechar(args);}
1556 static int c_morphshape(map_t*args) {return fakechar(args);}
1557 static int c_image(map_t*args) {return fakechar(args);}
1558 static int c_movie(map_t*args) {return fakechar(args);}
1559 static int c_sound(map_t*args) {return fakechar(args);}
1561 static int c_play(map_t*args) {return 0;}
1562 static int c_stop(map_t*args) {return 0;}
1564 static int c_soundtrack(map_t*args) {return 0;}
1565 static int c_buttonsounds(map_t*args) {return 0;}
1566 static int c_buttonput(map_t*args) {return 0;}
1567 static int c_texture(map_t*args) {return 0;}
1568 static int c_action(map_t*args) {return 0;}
1572 command_func_t* func;
1575 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1576 {"frame", c_frame, "n=+1"},
1578 // "import" type stuff
1579 {"shape", c_shape, "name filename"},
1580 {"morphshape", c_morphshape, "name start end"},
1581 {"jpeg", c_image, "name filename quality=80%"},
1582 {"png", c_image, "name filename"},
1583 {"movie", c_movie, "name filename"},
1584 {"sound", c_sound, "name filename"},
1585 {"font", c_font, "name filename"},
1586 {"soundtrack", c_soundtrack, "filename"},
1588 // character generators
1589 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1590 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1591 {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1592 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1593 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1594 {"text", c_text, "name text font size=100% color=white"},
1595 {"edittext", c_edittext, "name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
1597 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1600 {"play", c_play, "sound loop=0 @nomultiple=0"},
1601 {"stop", c_stop, "sound"},
1603 // object placement tags
1604 {"put", c_put, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1605 {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1606 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1607 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1608 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1609 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1610 {"del", c_del, "name"},
1611 // virtual object placement
1612 {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% rotate=0 above= below="},
1613 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% rotate=0"},
1614 {"point", c_point, "name x=0 y=0"},
1616 // commands which start a block
1617 //startclip (see above)
1618 {"sprite", c_sprite, "name"},
1619 {"action", c_action, ""},
1625 static map_t parseArguments(char*command, char*pattern)
1641 string_set(&t1, "commandname");
1642 string_set(&t2, command);
1643 map_put(&result, t1, t2);
1645 if(!pattern || !*pattern)
1652 if(!strncmp("<i> ", x, 3)) {
1654 if(type == COMMAND || type == LABEL) {
1656 syntaxerror("character name expected");
1658 name[pos].str = "instance";
1660 value[pos].str = text;
1661 value[pos].len = strlen(text);
1665 if(type == ASSIGNMENT)
1668 name[pos].str = "character";
1670 value[pos].str = text;
1671 value[pos].len = strlen(text);
1679 isboolean[pos] = (x[0] =='@');
1692 name[pos].len = d-x;
1697 name[pos].len = e-x;
1698 value[pos].str = e+1;
1699 value[pos].len = d-e-1;
1707 /* for(t=0;t<len;t++) {
1708 printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1709 isboolean[t]?"(boolean)":"");
1714 if(type == LABEL || type == COMMAND) {
1719 // first, search for boolean arguments
1720 for(pos=0;pos<len;pos++)
1722 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1724 if(type == ASSIGNMENT)
1726 value[pos].str = text;
1727 value[pos].len = strlen(text);
1728 /*printf("setting boolean parameter %s (to %s)\n",
1729 strndup(name[pos], namelen[pos]),
1730 strndup(value[pos], valuelen[pos]));*/
1735 // second, search for normal arguments
1737 for(pos=0;pos<len;pos++)
1739 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1740 (type != ASSIGNMENT && !set[pos])) {
1742 syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1744 if(type == ASSIGNMENT)
1747 value[pos].str = text;
1748 value[pos].len = strlen(text);
1750 printf("setting parameter %s (to %s)\n",
1751 strndup(name[pos].str, name[pos].len),
1752 strndup(value[pos].str, value[pos].len));
1758 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1762 for(t=0;t<len;t++) {
1763 printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1766 for(t=0;t<len;t++) {
1767 if(value[t].str && value[t].str[0] == '*') {
1768 //relative default- take value from some other parameter
1770 for(s=0;s<len;s++) {
1771 if(value[s].len == value[t].len-1 &&
1772 !strncmp(&value[t].str[1], value[s].str, value[s].len))
1773 value[t].str = value[s].str;
1776 if(value[t].str == 0) {
1778 syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1782 /* ok, now construct the dictionary from the parameters */
1786 map_put(&result, name[t], value[t]);
1790 static void parseArgumentsForCommand(char*command)
1795 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1796 if(!strcmp(arguments[t].command, command)) {
1797 args = parseArguments(command, arguments[t].arguments);
1802 syntaxerror("command %s not known", command);
1805 printf(".%s\n", command);fflush(stdout);
1806 map_dump(&args, stdout, "\t");fflush(stdout);
1809 (*arguments[nr].func)(&args);
1811 if(!strcmp(command, "button") ||
1812 !strcmp(command, "action")) {
1815 if(type == COMMAND) {
1816 if(!strcmp(text, "end"))
1830 int main (int argc,char ** argv)
1833 processargs(argc, argv);
1834 initLog(0,-1,0,0,-1,verbose);
1837 args_callback_usage(argv[0]);
1840 file = generateTokens(filename);
1842 printf("parser returned error.\n");
1849 while(!noMoreTokens()) {
1852 syntaxerror("command expected");
1853 parseArgumentsForCommand(text);