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);
1046 struct {unsigned char r,g,b;char*name;} colors[] =
1047 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1048 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1049 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1050 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1051 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1052 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1053 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1054 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1055 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1056 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1057 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1058 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1062 if(str[0]=='#' && (l==7 || l==9)) {
1063 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1065 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1067 color->r = r; color->g = g; color->b = b; color->a = a;
1070 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1071 if(!strcmp(str, colors[t].name)) {
1076 color->r = r; color->g = g; color->b = b; color->a = a;
1082 RGBA parseColor(char*str)
1085 if(!parseColor2(str, &c))
1086 syntaxerror("Expression '%s' is not a color", str);
1090 typedef struct _muladd {
1095 MULADD parseMulAdd(char*str)
1098 char* str2 = (char*)malloc(strlen(str)+5);
1105 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1106 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1107 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1108 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1109 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1110 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1111 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1112 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1113 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1114 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1116 syntaxerror("'%s' is not a valid color transform expression", str);
1118 m.add = (int)(add*256);
1119 m.mul = (int)(mul*256);
1124 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1126 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1127 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1129 if(a<-32768) a=-32768;
1130 if(a>32767) a=32767;
1131 if(m<-32768) m=-32768;
1132 if(m>32767) m=32767;
1138 float parsePercent(char*str)
1140 int l = strlen(str);
1144 return atoi(str)/100.0;
1146 syntaxerror("Expression '%s' is not a percentage", str);
1149 int isPercent(char*str)
1151 return str[strlen(str)-1]=='%';
1153 int parseNewSize(char*str, int size)
1156 return parsePercent(str)*size;
1158 return (int)(atof(str)*20);
1161 int isColor(char*str)
1164 return parseColor2(str, &c);
1167 static char* lu(map_t* args, char*name)
1169 char* value = map_lookup(args, name);
1171 syntaxerror("internal error 2: value %s should be set", name);
1176 static int c_swf(map_t*args)
1178 char* name = lu(args, "name");
1179 char* compressstr = lu(args, "compress");
1180 SRECT bbox = parseBox(lu(args, "bbox"));
1181 int version = parseInt(lu(args, "version"));
1182 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1184 if(!strcmp(name, "!default!") || override_outputname)
1187 if(!strcmp(compressstr, "default"))
1188 compress = version==6;
1189 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1191 else if(!strcmp(compressstr, "no"))
1193 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1195 s_swf(name, bbox, version, fps, compress);
1198 int isRelative(char*str)
1200 return !strncmp(str, "<plus>", 6) ||
1201 !strncmp(str, "<minus>", 7);
1203 char* getOffset(char*str)
1205 if(!strncmp(str, "<plus>", 6))
1207 if(!strncmp(str, "<minus>", 7))
1209 syntaxerror("internal error (347)");
1212 int getSign(char*str)
1214 if(!strncmp(str, "<plus>", 6))
1216 if(!strncmp(str, "<minus>", 7))
1218 syntaxerror("internal error (348)");
1221 static dictionary_t points;
1222 static mem_t mpoints;
1223 int points_initialized = 0;
1225 SPOINT getPoint(SRECT r, char*name)
1228 if(!strcmp(name, "center")) {
1230 p.x = (r.xmin + r.xmax)/2;
1231 p.y = (r.ymin + r.ymax)/2;
1235 if(points_initialized)
1236 l = (int)dictionary_lookup(&points, name);
1238 syntaxerror("Invalid point: \"%s\".", name);
1241 return *(SPOINT*)&mpoints.buffer[l];
1243 static int c_point(map_t*args)
1245 char*name = lu(args, "name");
1249 if(!points_initialized) {
1250 dictionary_init(&points);
1252 points_initialized = 1;
1254 p.x = parseTwip(lu(args, "x"));
1255 p.y = parseTwip(lu(args, "y"));
1256 pos = mem_put(&mpoints, &p, sizeof(p));
1257 string_set(&s1, name);
1259 dictionary_put(&points, s1, (void*)pos);
1262 static int c_placement(map_t*args, int type)
1264 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1267 char* luminancestr = lu(args, "luminance");
1268 char* scalestr = lu(args, "scale");
1269 char* scalexstr = lu(args, "scalex");
1270 char* scaleystr = lu(args, "scaley");
1271 char* rotatestr = lu(args, "rotate");
1272 char* shearstr = lu(args, "shear");
1273 char* xstr="", *pivotstr="";
1274 char* ystr="", *anglestr="";
1275 char*above = lu(args, "above"); /*FIXME*/
1276 char*below = lu(args, "below");
1277 char* rstr = lu(args, "red");
1278 char* gstr = lu(args, "green");
1279 char* bstr = lu(args, "blue");
1280 char* astr = lu(args, "alpha");
1281 char* pinstr = lu(args, "pin");
1290 pivotstr = lu(args, "pivot");
1291 anglestr = lu(args, "angle");
1293 xstr = lu(args, "x");
1294 ystr = lu(args, "y");
1297 luminance = parseMulAdd(luminancestr);
1300 luminance.mul = 256;
1304 if(scalexstr[0]||scaleystr[0])
1305 syntaxerror("scalex/scaley and scale cannot both be set");
1306 scalexstr = scaleystr = scalestr;
1309 if(type == 0 || type == 4) {
1311 character = lu(args, "character");
1312 parameters_clear(&p);
1314 p = s_getParameters(instance);
1319 if(isRelative(xstr)) {
1320 if(type == 0 || type == 4)
1321 syntaxerror("relative x values not allowed for initial put or startclip");
1322 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1324 p.x = parseTwip(xstr);
1328 if(isRelative(ystr)) {
1329 if(type == 0 || type == 4)
1330 syntaxerror("relative y values not allowed for initial put or startclip");
1331 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1333 p.y = parseTwip(ystr);
1337 /* scale, scalex, scaley */
1339 oldbbox = s_getCharBBox(character);
1341 oldbbox = s_getInstanceBBox(instance);
1343 oldwidth = oldbbox.xmax - oldbbox.xmin;
1344 oldheight = oldbbox.ymax - oldbbox.ymin;
1346 if(oldwidth==0) p.scalex = 1.0;
1349 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1353 if(oldheight==0) p.scaley = 1.0;
1356 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1362 if(isRelative(rotatestr)) {
1363 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1365 p.rotate = parseFloat(rotatestr);
1371 if(isRelative(shearstr)) {
1372 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1374 p.shear = parseFloat(shearstr);
1379 if(isPoint(pivotstr))
1380 p.pivot = parsePoint(pivotstr);
1382 p.pivot = getPoint(oldbbox, pivotstr);
1386 p.pin = parsePoint(pinstr);
1388 p.pin = getPoint(oldbbox, pinstr);
1391 /* color transform */
1393 if(rstr[0] || luminancestr[0]) {
1396 r = parseMulAdd(rstr);
1398 r.add = p.cxform.r0;
1399 r.mul = p.cxform.r1;
1401 r = mergeMulAdd(r, luminance);
1402 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1404 if(gstr[0] || luminancestr[0]) {
1407 g = parseMulAdd(gstr);
1409 g.add = p.cxform.g0;
1410 g.mul = p.cxform.g1;
1412 g = mergeMulAdd(g, luminance);
1413 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1415 if(bstr[0] || luminancestr[0]) {
1418 b = parseMulAdd(bstr);
1420 b.add = p.cxform.b0;
1421 b.mul = p.cxform.b1;
1423 b = mergeMulAdd(b, luminance);
1424 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1427 MULADD a = parseMulAdd(astr);
1428 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1432 s_put(instance, character, p);
1434 s_change(instance, p);
1436 s_qchange(instance, p);
1438 s_jump(instance, p);
1440 s_startclip(instance, character, p);
1443 static int c_put(map_t*args)
1445 c_placement(args, 0);
1448 static int c_change(map_t*args)
1450 c_placement(args, 1);
1453 static int c_qchange(map_t*args)
1455 c_placement(args, 2);
1458 static int c_arcchange(map_t*args)
1460 c_placement(args, 2);
1463 static int c_jump(map_t*args)
1465 c_placement(args, 3);
1468 static int c_startclip(map_t*args)
1470 c_placement(args, 4);
1473 static int c_del(map_t*args)
1475 char*instance = lu(args, "name");
1476 s_delinstance(instance);
1479 static int c_end(map_t*args)
1484 static int c_sprite(map_t*args)
1486 char* name = lu(args, "name");
1490 static int c_frame(map_t*args)
1492 char*framestr = lu(args, "n");
1494 if(isRelative(framestr)) {
1495 frame = s_getframe();
1496 if(getSign(framestr)<0)
1497 syntaxerror("relative frame expressions must be positive");
1498 frame += parseInt(getOffset(framestr));
1501 frame = parseInt(framestr);
1502 if(s_getframe() >= frame
1503 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1504 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1509 static int c_primitive(map_t*args)
1511 char*name = lu(args, "name");
1512 char*command = lu(args, "commandname");
1513 int width=0, height=0, r=0;
1514 int linewidth = parseTwip(lu(args, "line"));
1515 char*colorstr = lu(args, "color");
1516 RGBA color = parseColor(colorstr);
1517 char*fillstr = lu(args, "fill");
1523 if(!strcmp(command, "circle"))
1525 else if(!strcmp(command, "textshape"))
1529 width = parseTwip(lu(args, "width"));
1530 height = parseTwip(lu(args, "height"));
1531 } else if (type==1) {
1532 r = parseTwip(lu(args, "r"));
1533 } else if (type==2) {
1534 text = lu(args, "text");
1535 font = lu(args, "font");
1538 if(!strcmp(fillstr, "fill"))
1540 if(!strcmp(fillstr, "none"))
1542 if(width<0 || height<0 || linewidth<0 || r<0)
1543 syntaxerror("values width, height, line, r must be positive");
1544 if(!dofill || isColor(fillstr)) {
1546 fill = parseColor(fillstr);
1548 /* FIXME - texture fill */
1549 fill.r = fill.g = 0;
1550 fill.b = fill.a = 255;
1551 warning("texture fill not supported yet. Filling with black.");
1553 if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1554 else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1555 else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1559 static int c_shape(map_t*args)
1561 char*name = lu(args, "name");
1562 char*filename = lu(args, "filename");
1563 s_shape(name, filename);
1567 static int c_font(map_t*args)
1569 char*name = lu(args, "name");
1570 char*filename = lu(args, "filename");
1571 s_font(name, filename);
1575 static int c_text(map_t*args)
1577 char*name = lu(args, "name");
1578 char*text = lu(args, "text");
1579 char*font = lu(args, "font");
1580 float size = parsePercent(lu(args, "size"));
1581 RGBA color = parseColor(lu(args, "color"));
1582 s_text(name, font, text, (int)(size*100), color);
1586 int fakechar(map_t*args)
1588 char*name = lu(args, "name");
1589 s_box(name, 0, 0, black, 20, black, 0);
1592 static int c_circle(map_t*args) {return fakechar(args);}
1594 static int c_egon(map_t*args) {return fakechar(args);}
1595 static int c_button(map_t*args) {return fakechar(args);}
1596 static int c_edittext(map_t*args) {return fakechar(args);}
1598 static int c_morphshape(map_t*args) {return fakechar(args);}
1599 static int c_image(map_t*args) {return fakechar(args);}
1600 static int c_movie(map_t*args) {return fakechar(args);}
1601 static int c_sound(map_t*args) {return fakechar(args);}
1603 static int c_play(map_t*args) {return 0;}
1604 static int c_stop(map_t*args) {return 0;}
1606 static int c_soundtrack(map_t*args) {return 0;}
1607 static int c_buttonsounds(map_t*args) {return 0;}
1608 static int c_buttonput(map_t*args) {return 0;}
1609 static int c_texture(map_t*args) {return 0;}
1610 static int c_action(map_t*args) {return 0;}
1614 command_func_t* func;
1617 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1618 {"frame", c_frame, "n=<plus>1"},
1620 // "import" type stuff
1621 {"shape", c_shape, "name filename"},
1622 {"morphshape", c_morphshape, "name start end"},
1623 {"jpeg", c_image, "name filename quality=80%"},
1624 {"png", c_image, "name filename"},
1625 {"movie", c_movie, "name filename"},
1626 {"sound", c_sound, "name filename"},
1627 {"font", c_font, "name filename"},
1628 {"soundtrack", c_soundtrack, "filename"},
1630 // character generators
1631 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1632 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1633 {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1634 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1635 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1636 {"text", c_text, "name text font size=100% color=white"},
1637 {"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"},
1639 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1642 {"play", c_play, "sound loop=0 @nomultiple=0"},
1643 {"stop", c_stop, "sound"},
1645 // object placement tags
1646 {"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="},
1647 {"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="},
1648 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1649 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1650 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1651 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1652 {"del", c_del, "name"},
1653 // virtual object placement
1654 {"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="},
1655 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1656 {"point", c_point, "name x=0 y=0"},
1658 // commands which start a block
1659 //startclip (see above)
1660 {"sprite", c_sprite, "name"},
1661 {"action", c_action, ""},
1667 static map_t parseArguments(char*command, char*pattern)
1683 string_set(&t1, "commandname");
1684 string_set(&t2, command);
1685 map_put(&result, t1, t2);
1687 if(!pattern || !*pattern)
1694 if(!strncmp("<i> ", x, 3)) {
1696 if(type == COMMAND || type == LABEL) {
1698 syntaxerror("character name expected");
1700 name[pos].str = "instance";
1702 value[pos].str = text;
1703 value[pos].len = strlen(text);
1707 if(type == ASSIGNMENT)
1710 name[pos].str = "character";
1712 value[pos].str = text;
1713 value[pos].len = strlen(text);
1721 isboolean[pos] = (x[0] =='@');
1734 name[pos].len = d-x;
1739 name[pos].len = e-x;
1740 value[pos].str = e+1;
1741 value[pos].len = d-e-1;
1749 /* for(t=0;t<len;t++) {
1750 printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1751 isboolean[t]?"(boolean)":"");
1756 if(type == LABEL || type == COMMAND) {
1761 // first, search for boolean arguments
1762 for(pos=0;pos<len;pos++)
1764 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1766 if(type == ASSIGNMENT)
1768 value[pos].str = text;
1769 value[pos].len = strlen(text);
1770 /*printf("setting boolean parameter %s (to %s)\n",
1771 strndup(name[pos], namelen[pos]),
1772 strndup(value[pos], valuelen[pos]));*/
1777 // second, search for normal arguments
1779 for(pos=0;pos<len;pos++)
1781 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1782 (type != ASSIGNMENT && !set[pos])) {
1784 syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1786 if(type == ASSIGNMENT)
1789 value[pos].str = text;
1790 value[pos].len = strlen(text);
1792 printf("setting parameter %s (to %s)\n",
1793 strndup(name[pos].str, name[pos].len),
1794 strndup(value[pos].str, value[pos].len));
1800 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1804 for(t=0;t<len;t++) {
1805 printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1808 for(t=0;t<len;t++) {
1809 if(value[t].str && value[t].str[0] == '*') {
1810 //relative default- take value from some other parameter
1812 for(s=0;s<len;s++) {
1813 if(value[s].len == value[t].len-1 &&
1814 !strncmp(&value[t].str[1], value[s].str, value[s].len))
1815 value[t].str = value[s].str;
1818 if(value[t].str == 0) {
1820 syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1824 /* ok, now construct the dictionary from the parameters */
1828 map_put(&result, name[t], value[t]);
1832 static void parseArgumentsForCommand(char*command)
1837 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1838 if(!strcmp(arguments[t].command, command)) {
1839 args = parseArguments(command, arguments[t].arguments);
1844 syntaxerror("command %s not known", command);
1847 printf(".%s\n", command);fflush(stdout);
1848 map_dump(&args, stdout, "\t");fflush(stdout);
1851 (*arguments[nr].func)(&args);
1853 if(!strcmp(command, "button") ||
1854 !strcmp(command, "action")) {
1857 if(type == COMMAND) {
1858 if(!strcmp(text, "end"))
1872 int main (int argc,char ** argv)
1875 processargs(argc, argv);
1876 initLog(0,-1,0,0,-1,verbose);
1879 args_callback_usage(argv[0]);
1882 file = generateTokens(filename);
1884 printf("parser returned error.\n");
1891 while(!noMoreTokens()) {
1894 syntaxerror("command expected");
1895 parseArgumentsForCommand(text);