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;
31 static int override_outputname = 0;
33 static struct options_t options[] =
41 int args_callback_option(char*name,char*val)
43 if(!strcmp(name, "V")) {
44 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
47 else if(!strcmp(name, "o")) {
49 override_outputname = 1;
52 else if(!strcmp(name, "v")) {
57 printf("Unknown option: -%s\n", name);
62 int args_callback_longoption(char*name,char*val)
64 return args_long2shortoption(options, name, val);
66 void args_callback_usage(char*name)
68 printf("Usage: %s [-o filename] file.wav\n", name);
69 printf("\t-v , --verbose\t\t\t Be more verbose\n");
70 printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
71 printf("\t-V , --version\t\t\t Print program version and exit\n");
73 int args_callback_command(char*name,char*val)
76 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
83 static struct token_t* file;
92 static void syntaxerror(char*format, ...)
96 va_start(arglist, format);
97 vsprintf(buf, format, arglist);
99 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
103 static void warning(char*format, ...)
107 va_start(arglist, format);
108 vsprintf(buf, format, arglist);
110 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
113 static void readToken()
115 type = file[pos].type;
117 syntaxerror("unexpected end of file");
119 text = file[pos].text;
120 textlen = strlen(text);
121 line = file[pos].line;
122 column = file[pos].column;
124 //printf("---> %d(%s) %s\n", type, type_names[type], text);
127 static void pushBack()
130 if(!pos) syntaxerror("internal error 3");
135 textlen = strlen(text);
138 column = file[p].column;
141 static int noMoreTokens()
143 if(file[pos].type == END)
148 // ------------------------------ swf routines ----------------------------
152 int type; //0=swf, 1=sprite, 2=clip
158 /* for sprites (1): */
164 dictionary_t oldinstances;
168 static int stackpos = 0;
170 static dictionary_t characters;
171 static char idmap[65536];
172 static TAG*tag = 0; //current tag
174 static int id; //current character id
175 static int currentframe; //current frame in current level
176 static SRECT currentrect; //current bounding box in current level
177 static U16 currentdepth;
178 static dictionary_t instances;
179 static dictionary_t fonts;
181 typedef struct _parameters {
183 float scalex, scaley;
191 typedef struct _character {
197 typedef struct _instance {
198 character_t*character;
200 parameters_t parameters;
201 TAG* lastTag; //last tag which set the object
202 U16 lastFrame; //frame lastTag is in
205 static void character_init(character_t*c)
207 memset(c, 0, sizeof(character_t));
209 static character_t* character_new()
212 c = (character_t*)malloc(sizeof(character_t));
216 static void instance_init(instance_t*i)
218 memset(i, 0, sizeof(instance_t));
220 static instance_t* instance_new()
223 c = (instance_t*)malloc(sizeof(instance_t));
228 static void incrementid()
232 syntaxerror("Out of character ids.");
237 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
239 character_t* c = character_new();
241 c->definingTag = ctag;
244 if(dictionary_lookup(&characters, name))
245 syntaxerror("character %s defined twice", name);
246 dictionary_put2(&characters, name, c);
248 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
250 swf_SetString(tag, name);
251 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
254 swf_SetString(tag, name);
256 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
258 instance_t* i = instance_new();
261 //swf_GetMatrix(0, &i->matrix);
262 if(dictionary_lookup(&instances, name))
263 syntaxerror("object %s defined twice", name);
264 dictionary_put2(&instances, name, i);
268 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)
271 p->scalex = scalex; p->scaley = scaley;
272 p->pin = pin; p->pivot = pivot;
273 p->rotate = rotate; p->cxform = cxform;
277 static void parameters_clear(parameters_t*p)
280 p->scalex = 1.0; p->scaley = 1.0;
281 p->pin.x = 1; p->pin.y = 0;
282 p->pivot.x = 0; p->pivot.y = 0;
285 swf_GetCXForm(0, &p->cxform, 1);
288 static void makeMatrix(MATRIX*m, parameters_t*p)
297 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
298 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
299 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
300 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
302 m->sx = (int)(sx*65536+0.5);
303 m->r1 = (int)(r1*65536+0.5);
304 m->r0 = (int)(r0*65536+0.5);
305 m->sy = (int)(sy*65536+0.5);
309 h = swf_TurnPoint(p->pin, m);
314 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
319 r = swf_TurnRect(i->character->size, &m);
320 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
321 currentrect.xmax == 0 && currentrect.ymax == 0)
324 swf_ExpandRect2(¤trect, &r);
328 void s_swf(char*name, SRECT r, int version, int fps, int compress)
330 SWF*swf = (SWF*)malloc(sizeof(SWF));
334 syntaxerror(".swf blocks can't be nested");
336 memset(swf, 0, sizeof(swf));
337 swf->fileVersion = version;
339 swf->frameRate = fps;
340 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
341 swf->compressed = compress;
342 rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
343 swf_SetRGB(tag,&rgb);
345 if(stackpos==sizeof(stack)/sizeof(stack[0]))
346 syntaxerror("too many levels of recursion");
348 dictionary_init(&characters);
349 dictionary_init(&instances);
350 dictionary_init(&fonts);
352 memset(&stack[stackpos], 0, sizeof(stack[0]));
353 stack[stackpos].type = 0;
354 stack[stackpos].filename = strdup(name);
355 stack[stackpos].swf = swf;
356 stack[stackpos].oldframe = -1;
361 memset(¤trect, 0, sizeof(currentrect));
364 memset(idmap, 0, sizeof(idmap));
368 void s_sprite(char*name)
370 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
371 swf_SetU16(tag, id); //id
372 swf_SetU16(tag, 0); //frames
374 memset(&stack[stackpos], 0, sizeof(stack[0]));
375 stack[stackpos].type = 1;
376 stack[stackpos].oldframe = currentframe;
377 stack[stackpos].olddepth = currentdepth;
378 stack[stackpos].oldrect = currentrect;
379 stack[stackpos].oldinstances = instances;
380 stack[stackpos].tag = tag;
381 stack[stackpos].id = id;
382 stack[stackpos].name = strdup(name);
384 /* FIXME: those four fields should be bundled together */
385 dictionary_init(&instances);
388 memset(¤trect, 0, sizeof(currentrect));
394 static void s_endSprite()
396 SRECT r = currentrect;
399 /* TODO: before clearing, prepend "<spritename>." to names and
400 copy into old instances dict */
401 dictionary_clear(&instances);
403 currentframe = stack[stackpos].oldframe;
404 currentrect = stack[stackpos].oldrect;
405 currentdepth = stack[stackpos].olddepth;
406 instances = stack[stackpos].oldinstances;
407 tag = swf_InsertTag(tag, ST_END);
409 tag = stack[stackpos].tag;
412 syntaxerror("internal error(7)");
414 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
415 free(stack[stackpos].name);
418 static void s_endSWF()
425 swf = stack[stackpos].swf;
426 filename = stack[stackpos].filename;
428 tag = swf_InsertTag(tag, ST_SHOWFRAME);
429 tag = swf_InsertTag(tag, ST_END);
431 swf_OptimizeTagOrder(swf);
433 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
434 swf->movieSize = currentrect; /* "autocrop" */
436 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
438 syntaxerror("couldn't create output file %s", filename);
441 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
443 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
447 dictionary_clear(&instances);
448 dictionary_clear(&characters);
449 dictionary_clear(&fonts);
458 if(stack[stackpos-1].type == 0)
459 syntaxerror("End of file encountered in .swf block");
460 if(stack[stackpos-1].type == 1)
461 syntaxerror("End of file encountered in .sprite block");
462 if(stack[stackpos-1].type == 2)
463 syntaxerror("End of file encountered in .clip block");
475 for(t=currentframe;t<nr;t++) {
476 tag = swf_InsertTag(tag, ST_SHOWFRAME);
481 RGBA black={r:0,g:0,b:0,a:0};
482 void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill)
487 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
489 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
491 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
493 r.xmin = -linewidth-linewidth/2;
494 r.ymin = -linewidth-linewidth/2;
495 r.xmax = width+linewidth+linewidth/2;
496 r.ymax = height+linewidth+linewidth/2;
498 swf_SetShapeHeader(tag,s);
499 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
500 swf_ShapeSetLine(tag,s,width,0);
501 swf_ShapeSetLine(tag,s,0,height);
502 swf_ShapeSetLine(tag,s,-width,0);
503 swf_ShapeSetLine(tag,s,0,-height);
504 swf_ShapeSetEnd(tag);
507 s_addcharacter(name, id, tag, r);
511 void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill)
516 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
518 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
520 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
522 rect.xmin = -linewidth-linewidth/2;
523 rect.ymin = -linewidth-linewidth/2;
524 rect.xmax = 2*r+linewidth+linewidth/2;
525 rect.ymax = 2*r+linewidth+linewidth/2;
527 swf_SetRect(tag,&rect);
528 swf_SetShapeHeader(tag,s);
529 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
530 swf_ShapeSetCircle(tag, s, r,r,r,r);
531 swf_ShapeSetEnd(tag);
534 s_addcharacter(name, id, tag, rect);
538 void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill)
544 U8*text = (U8*)_text;
547 font = dictionary_lookup(&fonts, fontname);
549 syntaxerror("font \"%s\" not known!", fontname);
551 syntaxerror("textshapes must be filled", fontname);
553 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
554 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
555 s_box(name, 0, 0, black, 20, black, 0);
558 g = font->ascii2glyph[text[0]];
559 rect = font->layout->bounds[g];
562 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
564 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
566 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
568 swf_SetRect(tag, &rect);
569 swf_SetShapeStyles(tag, s);
570 swf_SetSimpleShape(tag, font->glyph[g].shape);
573 s_addcharacter(name, id, tag, rect);
576 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
581 font = dictionary_lookup(&fonts, fontname);
583 syntaxerror("font \"%s\" not known!", fontname);
585 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
587 if(!font->numchars) {
588 s_box(name, 0, 0, black, 20, black, 0);
591 r = swf_SetDefineText(tag, font, &color, text, size);
593 s_addcharacter(name, id, tag, r);
597 void dumpSWF(SWF*swf)
599 TAG* tag = swf->firstTag;
600 printf("vvvvvvvvvvvvvvvvvvvvv\n");
602 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
605 printf("^^^^^^^^^^^^^^^^^^^^^\n");
608 void s_font(char*name, char*filename)
613 f = open(filename,O_RDONLY);
615 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
616 font = (SWFFONT*)malloc(sizeof(SWFFONT));
617 memset(font, 0, sizeof(SWFFONT));
618 dictionary_put2(&fonts, name, font);
622 if (swf_ReadSWF(f,&swf)>=0) {
623 swf_FontExtract(&swf, 0x4e46, &font);
628 syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
633 /* fix the layout. Only needed for old fonts */
635 for(t=0;t<font->numchars;t++) {
636 font->glyph[t].advance = 0;
639 swf_FontCreateLayout(font);
643 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
644 swf_FontSetDefine2(tag, font);
647 if(dictionary_lookup(&fonts, name))
648 syntaxerror("font %s defined twice", name);
649 dictionary_put2(&fonts, name, font);
652 void s_shape(char*name, char*filename)
660 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
661 f = open(filename,O_RDONLY);
663 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
664 s_box(name, 0, 0, black, 20, black, 0);
667 if (swf_ReadSWF(f,&swf)<0) {
668 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
669 s_box(name, 0, 0, black, 20, black, 0);
674 /* FIXME: The following sets the bounding Box for the character.
675 It is wrong for two reasons:
676 a) It may be too small (in case objects in the movie clip at the borders)
677 b) it may be too big (because the poor movie never got autocropped)
681 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
685 swf_Relocate(&swf, idmap);
691 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
692 if(cutout[t] == ftag->id) {
696 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
698 if(ftag->id == ST_END)
702 /* We simply dump all tags right after the sprite
703 header, relying on the fact that swf_OptimizeTagOrder() will
704 sort things out for us later.
705 We also rely on the fact that the imported SWF is well-formed.
707 tag = swf_InsertTag(tag, ftag->id);
708 swf_SetBlock(tag, ftag->data, ftag->len);
712 syntaxerror("Included file %s contains errors", filename);
713 tag = swf_InsertTag(tag, ST_END);
717 s_addcharacter(name, id, tag, r);
720 SRECT s_getCharBBox(char*name)
722 character_t* c = dictionary_lookup(&characters, name);
723 if(!c) syntaxerror("character '%s' unknown(2)", name);
726 SRECT s_getInstanceBBox(char*name)
728 instance_t * i = dictionary_lookup(&instances, name);
730 if(!i) syntaxerror("instance '%s' unknown(4)", name);
732 if(!c) syntaxerror("internal error(5)");
735 parameters_t s_getParameters(char*name)
737 instance_t * i = dictionary_lookup(&instances, name);
738 if(!i) syntaxerror("instance '%s' unknown(10)", name);
739 return i->parameters;
741 void s_startclip(char*instance, char*character, parameters_t p)
743 character_t* c = dictionary_lookup(&characters, character);
747 syntaxerror("character %s not known", character);
749 i = s_addinstance(instance, c, currentdepth);
751 m = s_instancepos(i, &p);
753 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
754 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
755 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
757 i->lastFrame= currentframe;
759 stack[stackpos].tag = tag;
760 stack[stackpos].type = 2;
769 swf_SetTagPos(stack[stackpos].tag, 0);
770 swf_GetPlaceObject(stack[stackpos].tag, &p);
771 p.clipdepth = currentdepth;
772 swf_ClearTag(stack[stackpos].tag);
773 swf_SetPlaceObject(stack[stackpos].tag, &p);
777 void s_put(char*instance, char*character, parameters_t p)
779 character_t* c = dictionary_lookup(&characters, character);
783 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
786 i = s_addinstance(instance, c, currentdepth);
788 m = s_instancepos(i, &p);
790 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
791 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
793 i->lastFrame = currentframe;
797 void s_jump(char*instance, parameters_t p)
799 instance_t* i = dictionary_lookup(&instances, instance);
802 syntaxerror("instance %s not known", instance);
806 m = s_instancepos(i, &p);
808 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
809 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
811 i->lastFrame = currentframe;
814 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
820 ratio = (float)pos/(float)num;
822 p.x = (p2->x-p1->x)*ratio + p1->x;
823 p.y = (p2->y-p1->y)*ratio + p1->y;
824 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
825 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
826 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
827 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
829 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
830 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
831 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
832 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
834 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
835 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
836 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
837 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
839 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
840 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
841 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
842 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
846 void s_change(char*instance, parameters_t p2)
848 instance_t* i = dictionary_lookup(&instances, instance);
852 int frame, allframes;
854 syntaxerror("instance %s not known", instance);
858 allframes = currentframe - i->lastFrame - 1;
860 warning(".change ignored. can only .put/.change an object once per frame.");
864 m = s_instancepos(i, &p2);
865 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
866 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
869 /* o.k., we got the start and end point set. Now iterate though all the
870 tags in between, inserting object changes after each new frame */
873 if(!t) syntaxerror("internal error(6)");
875 while(frame < allframes) {
876 if(t->id == ST_SHOWFRAME) {
881 p = s_interpolate(&p1, &p2, frame, allframes);
882 m = s_instancepos(i, &p); //needed?
883 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
884 i->lastFrame = currentframe;
885 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
887 if(frame == allframes)
892 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
896 void s_delinstance(char*instance)
898 instance_t* i = dictionary_lookup(&instances, instance);
900 syntaxerror("instance %s not known", instance);
902 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
903 swf_SetU16(tag, i->depth);
904 dictionary_del(&instances, instance);
907 void s_qchange(char*instance, parameters_t p)
914 syntaxerror(".end unexpected");
915 if(stack[stackpos-1].type == 0)
917 else if(stack[stackpos-1].type == 1)
919 else if(stack[stackpos-1].type == 2)
921 else syntaxerror("internal error 1");
924 // ------------------------------------------------------------------------
926 typedef int command_func_t(map_t*args);
928 SRECT parseBox(char*str)
931 float xmin, xmax, ymin, ymax;
932 char*x = strchr(str, 'x');
934 if(!strcmp(str, "autocrop")) {
935 r.xmin = r.ymin = r.xmax = r.ymax = 0;
939 d1 = strchr(x+1, ':');
941 d2 = strchr(d1+1, ':');
943 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
948 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
954 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
959 r.xmin = (SCOORD)(xmin*20);
960 r.ymin = (SCOORD)(ymin*20);
961 r.xmax = (SCOORD)(xmax*20);
962 r.ymax = (SCOORD)(ymax*20);
965 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
968 float parseFloat(char*str)
972 int parseInt(char*str)
977 if(str[0]=='+' || str[0]=='-')
981 if(str[t]<'0' || str[t]>'9')
982 syntaxerror("Not an Integer: \"%s\"", str);
985 int parseTwip(char*str)
987 char*dot = strchr(str, '.');
991 return parseInt(str)*20;
995 for(s=str;s<dot-1;s++)
997 syntaxerror("Not a coordinate: \"%s\"", str);
1000 syntaxerror("Not a coordinate: \"%s\"", str);
1002 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1003 warning("precision loss: %s converted to twip", str);
1008 return atoi(str)*20;
1010 return atoi(str)*20+atoi(dot)*2;
1012 return atoi(str)*20+atoi(dot)/5;
1017 int isPoint(char*str)
1019 if(strchr(str, '('))
1025 SPOINT parsePoint(char*str)
1029 int l = strlen(str);
1030 char*comma = strchr(str, ',');
1031 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1032 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1033 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1034 p.x = parseTwip(tmp);
1035 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1036 p.y = parseTwip(tmp);
1040 int parseColor2(char*str, RGBA*color)
1042 int l = strlen(str);
1045 char*names[8] = {"black", "blue", "green", "cyan",
1046 "red", "magenta", "yellow", "white"};
1049 if(str[0]=='#' && (l==7 || l==9)) {
1050 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1052 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1054 color->r = r; color->g = g; color->b = b; color->a = a;
1058 if(!strcmp(str, names[t])) {
1065 color->r = r; color->g = g; color->b = b; color->a = a;
1071 RGBA parseColor(char*str)
1074 if(!parseColor2(str, &c))
1075 syntaxerror("Expression '%s' is not a color", str);
1079 typedef struct _muladd {
1084 MULADD parseMulAdd(char*str)
1087 char* str2 = (char*)malloc(strlen(str)+5);
1094 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1095 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1096 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1097 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1098 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1099 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1100 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1101 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1102 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1103 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1105 syntaxerror("'%s' is not a valid color transform expression", str);
1107 m.add = (int)(add*256);
1108 m.mul = (int)(mul*256);
1113 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1115 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1116 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1118 if(a<-32768) a=-32768;
1119 if(a>32767) a=32767;
1120 if(m<-32768) m=-32768;
1121 if(m>32767) m=32767;
1127 float parsePercent(char*str)
1129 int l = strlen(str);
1133 return atoi(str)/100.0;
1135 syntaxerror("Expression '%s' is not a percentage", str);
1138 int isPercent(char*str)
1140 return str[strlen(str)-1]=='%';
1142 int parseNewSize(char*str, int size)
1145 return parsePercent(str)*size;
1147 return (int)(atof(str)*20);
1150 int isColor(char*str)
1153 return parseColor2(str, &c);
1156 static char* lu(map_t* args, char*name)
1158 char* value = map_lookup(args, name);
1160 syntaxerror("internal error 2: value %s should be set", name);
1165 static int c_swf(map_t*args)
1167 char* name = lu(args, "name");
1168 char* compressstr = lu(args, "compress");
1169 SRECT bbox = parseBox(lu(args, "bbox"));
1170 int version = parseInt(lu(args, "version"));
1171 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1173 if(!strcmp(name, "!default!") || override_outputname)
1176 if(!strcmp(compressstr, "default"))
1177 compress = version==6;
1178 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1180 else if(!strcmp(compressstr, "no"))
1182 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1184 s_swf(name, bbox, version, fps, compress);
1187 int isRelative(char*str)
1189 return !strncmp(str, "<plus>", 6) ||
1190 !strncmp(str, "<minus>", 7);
1192 char* getOffset(char*str)
1194 if(!strncmp(str, "<plus>", 6))
1196 if(!strncmp(str, "<minus>", 7))
1198 syntaxerror("internal error (347)");
1201 int getSign(char*str)
1203 if(!strncmp(str, "<plus>", 6))
1205 if(!strncmp(str, "<minus>", 7))
1207 syntaxerror("internal error (348)");
1210 static dictionary_t points;
1211 static mem_t mpoints;
1212 int points_initialized = 0;
1214 SPOINT getPoint(SRECT r, char*name)
1217 if(!strcmp(name, "center")) {
1219 p.x = (r.xmin + r.xmax)/2;
1220 p.y = (r.ymin + r.ymax)/2;
1224 if(points_initialized)
1225 l = (int)dictionary_lookup(&points, name);
1227 syntaxerror("Invalid point: \"%s\".", name);
1230 return *(SPOINT*)&mpoints.buffer[l];
1232 static int c_point(map_t*args)
1234 char*name = lu(args, "name");
1238 if(!points_initialized) {
1239 dictionary_init(&points);
1241 points_initialized = 1;
1243 p.x = parseTwip(lu(args, "x"));
1244 p.y = parseTwip(lu(args, "y"));
1245 pos = mem_put(&mpoints, &p, sizeof(p));
1246 string_set(&s1, name);
1248 dictionary_put(&points, s1, (void*)pos);
1251 static int c_placement(map_t*args, int type)
1253 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1256 char* luminancestr = lu(args, "luminance");
1257 char* scalestr = lu(args, "scale");
1258 char* scalexstr = lu(args, "scalex");
1259 char* scaleystr = lu(args, "scaley");
1260 char* rotatestr = lu(args, "rotate");
1261 char* shearstr = lu(args, "shear");
1262 char* xstr="", *pivotstr="";
1263 char* ystr="", *anglestr="";
1264 char*above = lu(args, "above"); /*FIXME*/
1265 char*below = lu(args, "below");
1266 char* rstr = lu(args, "red");
1267 char* gstr = lu(args, "green");
1268 char* bstr = lu(args, "blue");
1269 char* astr = lu(args, "alpha");
1270 char* pinstr = lu(args, "pin");
1279 pivotstr = lu(args, "pivot");
1280 anglestr = lu(args, "angle");
1282 xstr = lu(args, "x");
1283 ystr = lu(args, "y");
1286 luminance = parseMulAdd(luminancestr);
1289 luminance.mul = 256;
1293 if(scalexstr[0]||scaleystr[0])
1294 syntaxerror("scalex/scaley and scale cannot both be set");
1295 scalexstr = scaleystr = scalestr;
1298 if(type == 0 || type == 4) {
1300 character = lu(args, "character");
1301 parameters_clear(&p);
1303 p = s_getParameters(instance);
1308 if(isRelative(xstr)) {
1309 if(type == 0 || type == 4)
1310 syntaxerror("relative x values not allowed for initial put or startclip");
1311 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1313 p.x = parseTwip(xstr);
1317 if(isRelative(ystr)) {
1318 if(type == 0 || type == 4)
1319 syntaxerror("relative y values not allowed for initial put or startclip");
1320 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1322 p.y = parseTwip(ystr);
1326 /* scale, scalex, scaley */
1328 oldbbox = s_getCharBBox(character);
1330 oldbbox = s_getInstanceBBox(instance);
1332 oldwidth = oldbbox.xmax - oldbbox.xmin;
1333 oldheight = oldbbox.ymax - oldbbox.ymin;
1335 if(oldwidth==0) p.scalex = 1.0;
1338 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1342 if(oldheight==0) p.scaley = 1.0;
1345 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1351 if(isRelative(rotatestr)) {
1352 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1354 p.rotate = parseFloat(rotatestr);
1360 if(isRelative(shearstr)) {
1361 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1363 p.shear = parseFloat(shearstr);
1368 if(isPoint(pivotstr))
1369 p.pivot = parsePoint(pivotstr);
1371 p.pivot = getPoint(oldbbox, pivotstr);
1375 p.pin = parsePoint(pinstr);
1377 p.pin = getPoint(oldbbox, pinstr);
1380 /* color transform */
1382 if(rstr[0] || luminancestr[0]) {
1385 r = parseMulAdd(rstr);
1387 r.add = p.cxform.r0;
1388 r.mul = p.cxform.r1;
1390 r = mergeMulAdd(r, luminance);
1391 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1393 if(gstr[0] || luminancestr[0]) {
1396 g = parseMulAdd(gstr);
1398 g.add = p.cxform.g0;
1399 g.mul = p.cxform.g1;
1401 g = mergeMulAdd(g, luminance);
1402 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1404 if(bstr[0] || luminancestr[0]) {
1407 b = parseMulAdd(bstr);
1409 b.add = p.cxform.b0;
1410 b.mul = p.cxform.b1;
1412 b = mergeMulAdd(b, luminance);
1413 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1416 MULADD a = parseMulAdd(astr);
1417 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1421 s_put(instance, character, p);
1423 s_change(instance, p);
1425 s_qchange(instance, p);
1427 s_jump(instance, p);
1429 s_startclip(instance, character, p);
1432 static int c_put(map_t*args)
1434 c_placement(args, 0);
1437 static int c_change(map_t*args)
1439 c_placement(args, 1);
1442 static int c_qchange(map_t*args)
1444 c_placement(args, 2);
1447 static int c_arcchange(map_t*args)
1449 c_placement(args, 2);
1452 static int c_jump(map_t*args)
1454 c_placement(args, 3);
1457 static int c_startclip(map_t*args)
1459 c_placement(args, 4);
1462 static int c_del(map_t*args)
1464 char*instance = lu(args, "name");
1465 s_delinstance(instance);
1468 static int c_end(map_t*args)
1473 static int c_sprite(map_t*args)
1475 char* name = lu(args, "name");
1479 static int c_frame(map_t*args)
1481 char*framestr = lu(args, "n");
1483 if(isRelative(framestr)) {
1484 frame = s_getframe();
1485 if(getSign(framestr)<0)
1486 syntaxerror("relative frame expressions must be positive");
1487 frame += parseInt(getOffset(framestr));
1490 frame = parseInt(framestr);
1491 if(s_getframe() >= frame
1492 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1493 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1498 static int c_primitive(map_t*args)
1500 char*name = lu(args, "name");
1501 char*command = lu(args, "commandname");
1502 int width=0, height=0, r=0;
1503 int linewidth = parseTwip(lu(args, "line"));
1504 char*colorstr = lu(args, "color");
1505 RGBA color = parseColor(colorstr);
1506 char*fillstr = lu(args, "fill");
1512 if(!strcmp(command, "circle"))
1514 else if(!strcmp(command, "textshape"))
1518 width = parseTwip(lu(args, "width"));
1519 height = parseTwip(lu(args, "height"));
1520 } else if (type==1) {
1521 r = parseTwip(lu(args, "r"));
1522 } else if (type==2) {
1523 text = lu(args, "text");
1524 font = lu(args, "font");
1527 if(!strcmp(fillstr, "fill"))
1529 if(!strcmp(fillstr, "none"))
1531 if(width<0 || height<0 || linewidth<0 || r<0)
1532 syntaxerror("values width, height, line, r must be positive");
1533 if(!dofill || isColor(fillstr)) {
1535 fill = parseColor(fillstr);
1537 /* FIXME - texture fill */
1538 fill.r = fill.g = 0;
1539 fill.b = fill.a = 255;
1540 warning("texture fill not supported yet. Filling with black.");
1542 if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1543 else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1544 else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1548 static int c_shape(map_t*args)
1550 char*name = lu(args, "name");
1551 char*filename = lu(args, "filename");
1552 s_shape(name, filename);
1556 static int c_font(map_t*args)
1558 char*name = lu(args, "name");
1559 char*filename = lu(args, "filename");
1560 s_font(name, filename);
1564 static int c_text(map_t*args)
1566 char*name = lu(args, "name");
1567 char*text = lu(args, "text");
1568 char*font = lu(args, "font");
1569 float size = parsePercent(lu(args, "size"));
1570 RGBA color = parseColor(lu(args, "color"));
1571 s_text(name, font, text, (int)(size*100), color);
1575 int fakechar(map_t*args)
1577 char*name = lu(args, "name");
1578 s_box(name, 0, 0, black, 20, black, 0);
1581 static int c_circle(map_t*args) {return fakechar(args);}
1583 static int c_egon(map_t*args) {return fakechar(args);}
1584 static int c_button(map_t*args) {return fakechar(args);}
1585 static int c_edittext(map_t*args) {return fakechar(args);}
1587 static int c_morphshape(map_t*args) {return fakechar(args);}
1588 static int c_image(map_t*args) {return fakechar(args);}
1589 static int c_movie(map_t*args) {return fakechar(args);}
1590 static int c_sound(map_t*args) {return fakechar(args);}
1592 static int c_play(map_t*args) {return 0;}
1593 static int c_stop(map_t*args) {return 0;}
1595 static int c_soundtrack(map_t*args) {return 0;}
1596 static int c_buttonsounds(map_t*args) {return 0;}
1597 static int c_buttonput(map_t*args) {return 0;}
1598 static int c_texture(map_t*args) {return 0;}
1599 static int c_action(map_t*args) {return 0;}
1603 command_func_t* func;
1606 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1607 {"frame", c_frame, "n=<plus>1"},
1609 // "import" type stuff
1610 {"shape", c_shape, "name filename"},
1611 {"morphshape", c_morphshape, "name start end"},
1612 {"jpeg", c_image, "name filename quality=80%"},
1613 {"png", c_image, "name filename"},
1614 {"movie", c_movie, "name filename"},
1615 {"sound", c_sound, "name filename"},
1616 {"font", c_font, "name filename"},
1617 {"soundtrack", c_soundtrack, "filename"},
1619 // character generators
1620 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1621 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1622 {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1623 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1624 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1625 {"text", c_text, "name text font size=100% color=white"},
1626 {"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"},
1628 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1631 {"play", c_play, "sound loop=0 @nomultiple=0"},
1632 {"stop", c_stop, "sound"},
1634 // object placement tags
1635 {"put", c_put, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1636 {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1637 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1638 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1639 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1640 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1641 {"del", c_del, "name"},
1642 // virtual object placement
1643 {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% shear=0 rotate=0 above= below="},
1644 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1645 {"point", c_point, "name x=0 y=0"},
1647 // commands which start a block
1648 //startclip (see above)
1649 {"sprite", c_sprite, "name"},
1650 {"action", c_action, ""},
1656 static map_t parseArguments(char*command, char*pattern)
1672 string_set(&t1, "commandname");
1673 string_set(&t2, command);
1674 map_put(&result, t1, t2);
1676 if(!pattern || !*pattern)
1683 if(!strncmp("<i> ", x, 3)) {
1685 if(type == COMMAND || type == LABEL) {
1687 syntaxerror("character name expected");
1689 name[pos].str = "instance";
1691 value[pos].str = text;
1692 value[pos].len = strlen(text);
1696 if(type == ASSIGNMENT)
1699 name[pos].str = "character";
1701 value[pos].str = text;
1702 value[pos].len = strlen(text);
1710 isboolean[pos] = (x[0] =='@');
1723 name[pos].len = d-x;
1728 name[pos].len = e-x;
1729 value[pos].str = e+1;
1730 value[pos].len = d-e-1;
1738 /* for(t=0;t<len;t++) {
1739 printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1740 isboolean[t]?"(boolean)":"");
1745 if(type == LABEL || type == COMMAND) {
1750 // first, search for boolean arguments
1751 for(pos=0;pos<len;pos++)
1753 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1755 if(type == ASSIGNMENT)
1757 value[pos].str = text;
1758 value[pos].len = strlen(text);
1759 /*printf("setting boolean parameter %s (to %s)\n",
1760 strndup(name[pos], namelen[pos]),
1761 strndup(value[pos], valuelen[pos]));*/
1766 // second, search for normal arguments
1768 for(pos=0;pos<len;pos++)
1770 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1771 (type != ASSIGNMENT && !set[pos])) {
1773 syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1775 if(type == ASSIGNMENT)
1778 value[pos].str = text;
1779 value[pos].len = strlen(text);
1781 printf("setting parameter %s (to %s)\n",
1782 strndup(name[pos].str, name[pos].len),
1783 strndup(value[pos].str, value[pos].len));
1789 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1793 for(t=0;t<len;t++) {
1794 printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1797 for(t=0;t<len;t++) {
1798 if(value[t].str && value[t].str[0] == '*') {
1799 //relative default- take value from some other parameter
1801 for(s=0;s<len;s++) {
1802 if(value[s].len == value[t].len-1 &&
1803 !strncmp(&value[t].str[1], value[s].str, value[s].len))
1804 value[t].str = value[s].str;
1807 if(value[t].str == 0) {
1809 syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1813 /* ok, now construct the dictionary from the parameters */
1817 map_put(&result, name[t], value[t]);
1821 static void parseArgumentsForCommand(char*command)
1826 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1827 if(!strcmp(arguments[t].command, command)) {
1828 args = parseArguments(command, arguments[t].arguments);
1833 syntaxerror("command %s not known", command);
1836 printf(".%s\n", command);fflush(stdout);
1837 map_dump(&args, stdout, "\t");fflush(stdout);
1840 (*arguments[nr].func)(&args);
1842 if(!strcmp(command, "button") ||
1843 !strcmp(command, "action")) {
1846 if(type == COMMAND) {
1847 if(!strcmp(text, "end"))
1861 int main (int argc,char ** argv)
1864 processargs(argc, argv);
1865 initLog(0,-1,0,0,-1,verbose);
1868 args_callback_usage(argv[0]);
1871 file = generateTokens(filename);
1873 printf("parser returned error.\n");
1880 while(!noMoreTokens()) {
1883 syntaxerror("command expected");
1884 parseArgumentsForCommand(text);