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;
189 typedef struct _character {
195 typedef struct _instance {
196 character_t*character;
198 parameters_t parameters;
199 TAG* lastTag; //last tag which set the object
200 U16 lastFrame; //frame lastTag is in
203 static void character_init(character_t*c)
205 memset(c, 0, sizeof(character_t));
207 static character_t* character_new()
210 c = (character_t*)malloc(sizeof(character_t));
214 static void instance_init(instance_t*i)
216 memset(i, 0, sizeof(instance_t));
218 static instance_t* instance_new()
221 c = (instance_t*)malloc(sizeof(instance_t));
226 static void incrementid()
230 syntaxerror("Out of character ids.");
235 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
237 character_t* c = character_new();
239 c->definingTag = ctag;
242 if(dictionary_lookup(&characters, name))
243 syntaxerror("character %s defined twice", name);
244 dictionary_put2(&characters, name, c);
246 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
248 swf_SetString(tag, name);
249 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
252 swf_SetString(tag, name);
254 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
256 instance_t* i = instance_new();
259 //swf_GetMatrix(0, &i->matrix);
260 if(dictionary_lookup(&instances, name))
261 syntaxerror("object %s defined twice", name);
262 dictionary_put2(&instances, name, i);
266 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)
269 p->scalex = scalex; p->scaley = scaley;
270 p->pin = pin; p->pivot = pivot;
271 p->rotate = rotate; p->cxform = cxform;
275 static void parameters_clear(parameters_t*p)
278 p->scalex = 1.0; p->scaley = 1.0;
279 p->pin.x = 1; p->pin.y = 0;
280 p->pivot.x = 0; p->pivot.y = 0;
283 swf_GetCXForm(0, &p->cxform, 1);
286 static void makeMatrix(MATRIX*m, parameters_t*p)
295 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
296 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
297 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
298 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
300 m->sx = (int)(sx*65536+0.5);
301 m->r1 = (int)(r1*65536+0.5);
302 m->r0 = (int)(r0*65536+0.5);
303 m->sy = (int)(sy*65536+0.5);
307 h = swf_TurnPoint(p->pin, m);
312 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
317 r = swf_TurnRect(i->character->size, &m);
318 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
319 currentrect.xmax == 0 && currentrect.ymax == 0)
322 swf_ExpandRect2(¤trect, &r);
326 void s_swf(char*name, SRECT r, int version, int fps, int compress)
328 SWF*swf = (SWF*)malloc(sizeof(SWF));
332 syntaxerror(".swf blocks can't be nested");
334 memset(swf, 0, sizeof(swf));
335 swf->fileVersion = version;
337 swf->frameRate = fps;
338 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
339 swf->compressed = compress;
340 rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
341 swf_SetRGB(tag,&rgb);
343 if(stackpos==sizeof(stack)/sizeof(stack[0]))
344 syntaxerror("too many levels of recursion");
346 dictionary_init(&characters);
347 dictionary_init(&instances);
348 dictionary_init(&fonts);
350 memset(&stack[stackpos], 0, sizeof(stack[0]));
351 stack[stackpos].type = 0;
352 stack[stackpos].filename = strdup(name);
353 stack[stackpos].swf = swf;
354 stack[stackpos].oldframe = -1;
359 memset(¤trect, 0, sizeof(currentrect));
362 memset(idmap, 0, sizeof(idmap));
366 void s_sprite(char*name)
368 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
369 swf_SetU16(tag, id); //id
370 swf_SetU16(tag, 0); //frames
372 memset(&stack[stackpos], 0, sizeof(stack[0]));
373 stack[stackpos].type = 1;
374 stack[stackpos].oldframe = currentframe;
375 stack[stackpos].olddepth = currentdepth;
376 stack[stackpos].oldrect = currentrect;
377 stack[stackpos].oldinstances = instances;
378 stack[stackpos].tag = tag;
379 stack[stackpos].id = id;
380 stack[stackpos].name = strdup(name);
382 /* FIXME: those four fields should be bundled together */
383 dictionary_init(&instances);
386 memset(¤trect, 0, sizeof(currentrect));
392 static void s_endSprite()
394 SRECT r = currentrect;
397 /* TODO: before clearing, prepend "<spritename>." to names and
398 copy into old instances dict */
399 dictionary_clear(&instances);
401 currentframe = stack[stackpos].oldframe;
402 currentrect = stack[stackpos].oldrect;
403 currentdepth = stack[stackpos].olddepth;
404 instances = stack[stackpos].oldinstances;
405 tag = swf_InsertTag(tag, ST_END);
407 tag = stack[stackpos].tag;
410 syntaxerror("internal error(7)");
412 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
413 free(stack[stackpos].name);
416 static void s_endSWF()
423 swf = stack[stackpos].swf;
424 filename = stack[stackpos].filename;
426 tag = swf_InsertTag(tag, ST_SHOWFRAME);
427 tag = swf_InsertTag(tag, ST_END);
429 swf_OptimizeTagOrder(swf);
431 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
432 swf->movieSize = currentrect; /* "autocrop" */
434 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
436 syntaxerror("couldn't create output file %s", filename);
439 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
441 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
445 dictionary_clear(&instances);
446 dictionary_clear(&characters);
447 dictionary_clear(&fonts);
456 if(stack[stackpos-1].type == 0)
457 syntaxerror("End of file encountered in .swf block");
458 if(stack[stackpos-1].type == 1)
459 syntaxerror("End of file encountered in .sprite block");
460 if(stack[stackpos-1].type == 2)
461 syntaxerror("End of file encountered in .clip block");
473 for(t=currentframe;t<nr;t++) {
474 tag = swf_InsertTag(tag, ST_SHOWFRAME);
479 RGBA black={r:0,g:0,b:0,a:0};
480 void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill)
485 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
487 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
489 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
491 r.xmin = -linewidth-linewidth/2;
492 r.ymin = -linewidth-linewidth/2;
493 r.xmax = width+linewidth+linewidth/2;
494 r.ymax = height+linewidth+linewidth/2;
496 swf_SetShapeHeader(tag,s);
497 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
498 swf_ShapeSetLine(tag,s,width,0);
499 swf_ShapeSetLine(tag,s,0,height);
500 swf_ShapeSetLine(tag,s,-width,0);
501 swf_ShapeSetLine(tag,s,0,-height);
502 swf_ShapeSetEnd(tag);
505 s_addcharacter(name, id, tag, r);
509 void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill)
514 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
516 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
518 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
520 rect.xmin = -linewidth-linewidth/2;
521 rect.ymin = -linewidth-linewidth/2;
522 rect.xmax = 2*r+linewidth+linewidth/2;
523 rect.ymax = 2*r+linewidth+linewidth/2;
525 swf_SetRect(tag,&rect);
526 swf_SetShapeHeader(tag,s);
527 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
528 swf_ShapeSetCircle(tag, s, r,r,r,r);
529 swf_ShapeSetEnd(tag);
532 s_addcharacter(name, id, tag, rect);
536 void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill)
542 U8*text = (U8*)_text;
545 font = dictionary_lookup(&fonts, fontname);
547 syntaxerror("font \"%s\" not known!", fontname);
549 syntaxerror("textshapes must be filled", fontname);
551 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
552 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
553 s_box(name, 0, 0, black, 20, black, 0);
556 g = font->ascii2glyph[text[0]];
557 rect = font->layout->bounds[g];
560 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
562 fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
564 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
566 swf_SetRect(tag, &rect);
567 swf_SetShapeStyles(tag, s);
568 swf_SetSimpleShape(tag, font->glyph[g].shape);
571 s_addcharacter(name, id, tag, rect);
574 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
579 font = dictionary_lookup(&fonts, fontname);
581 syntaxerror("font \"%s\" not known!", fontname);
583 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
585 if(!font->numchars) {
586 s_box(name, 0, 0, black, 20, black, 0);
589 r = swf_SetDefineText(tag, font, &color, text, size);
591 s_addcharacter(name, id, tag, r);
595 void dumpSWF(SWF*swf)
597 TAG* tag = swf->firstTag;
598 printf("vvvvvvvvvvvvvvvvvvvvv\n");
600 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
603 printf("^^^^^^^^^^^^^^^^^^^^^\n");
606 void s_font(char*name, char*filename)
611 f = open(filename,O_RDONLY);
613 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
614 font = (SWFFONT*)malloc(sizeof(SWFFONT));
615 memset(font, 0, sizeof(SWFFONT));
616 dictionary_put2(&fonts, name, font);
620 if (swf_ReadSWF(f,&swf)>=0) {
621 swf_FontExtract(&swf, 0x4e46, &font);
626 syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
631 /* fix the layout. Only needed for old fonts */
633 for(t=0;t<font->numchars;t++) {
634 font->glyph[t].advance = 0;
637 swf_FontCreateLayout(font);
641 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
642 swf_FontSetDefine2(tag, font);
645 if(dictionary_lookup(&fonts, name))
646 syntaxerror("font %s defined twice", name);
647 dictionary_put2(&fonts, name, font);
650 void s_shape(char*name, char*filename)
658 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
659 f = open(filename,O_RDONLY);
661 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
662 s_box(name, 0, 0, black, 20, black, 0);
665 if (swf_ReadSWF(f,&swf)<0) {
666 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
667 s_box(name, 0, 0, black, 20, black, 0);
672 /* FIXME: The following sets the bounding Box for the character.
673 It is wrong for two reasons:
674 a) It may be too small (in case objects in the movie clip at the borders)
675 b) it may be too big (because the poor movie never got autocropped)
679 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
683 swf_Relocate(&swf, idmap);
689 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
690 if(cutout[t] == ftag->id) {
694 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
696 if(ftag->id == ST_END)
700 /* We simply dump all tags right after the sprite
701 header, relying on the fact that swf_OptimizeTagOrder() will
702 sort things out for us later.
703 We also rely on the fact that the imported SWF is well-formed.
705 tag = swf_InsertTag(tag, ftag->id);
706 swf_SetBlock(tag, ftag->data, ftag->len);
710 syntaxerror("Included file %s contains errors", filename);
711 tag = swf_InsertTag(tag, ST_END);
715 s_addcharacter(name, id, tag, r);
718 SRECT s_getCharBBox(char*name)
720 character_t* c = dictionary_lookup(&characters, name);
721 if(!c) syntaxerror("character '%s' unknown(2)", name);
724 SRECT s_getInstanceBBox(char*name)
726 instance_t * i = dictionary_lookup(&instances, name);
728 if(!i) syntaxerror("instance '%s' unknown(4)", name);
730 if(!c) syntaxerror("internal error(5)");
733 parameters_t s_getParameters(char*name)
735 instance_t * i = dictionary_lookup(&instances, name);
736 if(!i) syntaxerror("instance '%s' unknown(10)", name);
737 return i->parameters;
739 void s_startclip(char*instance, char*character, parameters_t p)
741 character_t* c = dictionary_lookup(&characters, character);
745 syntaxerror("character %s not known", character);
747 i = s_addinstance(instance, c, currentdepth);
749 m = s_instancepos(i, &p);
751 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
752 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
753 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
755 i->lastFrame= currentframe;
757 stack[stackpos].tag = tag;
758 stack[stackpos].type = 2;
767 swf_SetTagPos(stack[stackpos].tag, 0);
768 swf_GetPlaceObject(stack[stackpos].tag, &p);
769 p.clipdepth = currentdepth;
770 swf_ClearTag(stack[stackpos].tag);
771 swf_SetPlaceObject(stack[stackpos].tag, &p);
775 void s_put(char*instance, char*character, parameters_t p)
777 character_t* c = dictionary_lookup(&characters, character);
781 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
784 i = s_addinstance(instance, c, currentdepth);
786 m = s_instancepos(i, &p);
788 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
789 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
791 i->lastFrame = currentframe;
795 void s_jump(char*instance, parameters_t p)
797 instance_t* i = dictionary_lookup(&instances, instance);
800 syntaxerror("instance %s not known", instance);
804 m = s_instancepos(i, &p);
806 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
807 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
809 i->lastFrame = currentframe;
812 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
818 ratio = (float)pos/(float)num;
820 p.x = (p2->x-p1->x)*ratio + p1->x;
821 p.y = (p2->y-p1->y)*ratio + p1->y;
822 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
823 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
824 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
825 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
827 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
828 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
829 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
830 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
832 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
833 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
834 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
835 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
837 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
838 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
839 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
840 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
844 void s_change(char*instance, parameters_t p2)
846 instance_t* i = dictionary_lookup(&instances, instance);
850 int frame, allframes;
852 syntaxerror("instance %s not known", instance);
856 allframes = currentframe - i->lastFrame - 1;
858 warning(".change ignored. can only .put/.change an object once per frame.");
862 m = s_instancepos(i, &p2);
863 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
864 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
867 /* o.k., we got the start and end point set. Now iterate though all the
868 tags in between, inserting object changes after each new frame */
871 if(!t) syntaxerror("internal error(6)");
873 while(frame < allframes) {
874 if(t->id == ST_SHOWFRAME) {
879 p = s_interpolate(&p1, &p2, frame, allframes);
880 m = s_instancepos(i, &p); //needed?
881 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
882 i->lastFrame = currentframe;
883 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
885 if(frame == allframes)
890 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
894 void s_delinstance(char*instance)
896 instance_t* i = dictionary_lookup(&instances, instance);
898 syntaxerror("instance %s not known", instance);
900 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
901 swf_SetU16(tag, i->depth);
902 dictionary_del(&instances, instance);
905 void s_qchange(char*instance, parameters_t p)
912 syntaxerror(".end unexpected");
913 if(stack[stackpos-1].type == 0)
915 else if(stack[stackpos-1].type == 1)
917 else if(stack[stackpos-1].type == 2)
919 else syntaxerror("internal error 1");
922 // ------------------------------------------------------------------------
924 typedef int command_func_t(map_t*args);
926 SRECT parseBox(char*str)
929 float xmin, xmax, ymin, ymax;
930 char*x = strchr(str, 'x');
932 if(!strcmp(str, "autocrop")) {
933 r.xmin = r.ymin = r.xmax = r.ymax = 0;
937 d1 = strchr(x+1, ':');
939 d2 = strchr(d1+1, ':');
941 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
946 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
952 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
957 r.xmin = (SCOORD)(xmin*20);
958 r.ymin = (SCOORD)(ymin*20);
959 r.xmax = (SCOORD)(xmax*20);
960 r.ymax = (SCOORD)(ymax*20);
963 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
966 float parseFloat(char*str)
970 int parseInt(char*str)
975 if(str[0]=='+' || str[0]=='-')
979 if(str[t]<'0' || str[t]>'9')
980 syntaxerror("Not an Integer: \"%s\"", str);
983 int parseTwip(char*str)
985 char*dot = strchr(str, '.');
989 return parseInt(str)*20;
993 for(s=str;s<dot-1;s++)
995 syntaxerror("Not a coordinate: \"%s\"", str);
998 syntaxerror("Not a coordinate: \"%s\"", str);
1000 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1001 warning("precision loss: %s converted to twip", str);
1006 return atoi(str)*20;
1008 return atoi(str)*20+atoi(dot)*2;
1010 return atoi(str)*20+atoi(dot)/5;
1015 int isPoint(char*str)
1017 if(strchr(str, '('))
1023 SPOINT parsePoint(char*str)
1027 int l = strlen(str);
1028 char*comma = strchr(str, ',');
1029 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1030 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1031 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1032 p.x = parseTwip(tmp);
1033 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1034 p.y = parseTwip(tmp);
1038 int parseColor2(char*str, RGBA*color)
1040 int l = strlen(str);
1043 char*names[8] = {"black", "blue", "green", "cyan",
1044 "red", "violet", "yellow", "white"};
1047 if(str[0]=='#' && (l==7 || l==9)) {
1048 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1050 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1052 color->r = r; color->g = g; color->b = b; color->a = a;
1056 if(!strcmp(str, names[t])) {
1063 color->r = r; color->g = g; color->b = b; color->a = a;
1069 RGBA parseColor(char*str)
1072 if(!parseColor2(str, &c))
1073 syntaxerror("Expression '%s' is not a color", str);
1077 typedef struct _muladd {
1082 MULADD parseMulAdd(char*str)
1085 char* str2 = (char*)malloc(strlen(str)+5);
1092 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1093 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1094 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1095 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1096 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1097 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1098 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1099 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1100 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1101 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1103 syntaxerror("'%s' is not a valid color transform expression", str);
1105 m.add = (int)(add*256);
1106 m.mul = (int)(mul*256);
1111 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1113 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1114 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1116 if(a<-32768) a=-32768;
1117 if(a>32767) a=32767;
1118 if(m<-32768) m=-32768;
1119 if(m>32767) m=32767;
1125 float parsePercent(char*str)
1127 int l = strlen(str);
1131 return atoi(str)/100.0;
1133 syntaxerror("Expression '%s' is not a percentage", str);
1136 int isPercent(char*str)
1138 return str[strlen(str)-1]=='%';
1140 int parseNewSize(char*str, int size)
1143 return parsePercent(str)*size;
1145 return (int)(atof(str)*20);
1148 int isColor(char*str)
1151 return parseColor2(str, &c);
1154 static char* lu(map_t* args, char*name)
1156 char* value = map_lookup(args, name);
1158 syntaxerror("internal error 2: value %s should be set", name);
1163 static int c_swf(map_t*args)
1165 char* name = lu(args, "name");
1166 char* compressstr = lu(args, "compress");
1167 SRECT bbox = parseBox(lu(args, "bbox"));
1168 int version = parseInt(lu(args, "version"));
1169 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1171 if(!strcmp(name, "!default!"))
1174 if(!strcmp(compressstr, "default"))
1175 compress = version==6;
1176 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1178 else if(!strcmp(compressstr, "no"))
1180 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1182 s_swf(name, bbox, version, fps, compress);
1185 int isRelative(char*str)
1187 return !strncmp(str, "<plus>", 6) ||
1188 !strncmp(str, "<minus>", 7);
1190 char* getOffset(char*str)
1192 if(!strncmp(str, "<plus>", 6))
1194 if(!strncmp(str, "<minus>", 7))
1196 syntaxerror("internal error (347)");
1199 int getSign(char*str)
1201 if(!strncmp(str, "<plus>", 6))
1203 if(!strncmp(str, "<minus>", 7))
1205 syntaxerror("internal error (348)");
1208 static dictionary_t points;
1209 static mem_t mpoints;
1210 int points_initialized = 0;
1212 SPOINT getPoint(SRECT r, char*name)
1215 if(!strcmp(name, "center")) {
1217 p.x = (r.xmin + r.xmax)/2;
1218 p.y = (r.ymin + r.ymax)/2;
1222 l = (int)dictionary_lookup(&points, name);
1224 syntaxerror("Couldn't find point \"%s\".", name);
1227 return *(SPOINT*)&mpoints.buffer[l];
1229 static int c_point(map_t*args)
1231 char*name = lu(args, "name");
1235 if(!points_initialized) {
1236 dictionary_init(&points);
1238 points_initialized = 1;
1240 p.x = parseTwip(lu(args, "x"));
1241 p.y = parseTwip(lu(args, "y"));
1242 pos = mem_put(&mpoints, &p, sizeof(p));
1243 string_set(&s1, name);
1245 dictionary_put(&points, s1, (void*)pos);
1248 static int c_placement(map_t*args, int type)
1250 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1253 char* luminancestr = lu(args, "luminance");
1254 char* scalestr = lu(args, "scale");
1255 char* scalexstr = lu(args, "scalex");
1256 char* scaleystr = lu(args, "scaley");
1257 char* rotatestr = lu(args, "rotate");
1258 char* shearstr = lu(args, "shear");
1259 char* xstr="", *pivotstr="";
1260 char* ystr="", *anglestr="";
1261 char*above = lu(args, "above"); /*FIXME*/
1262 char*below = lu(args, "below");
1263 char* rstr = lu(args, "red");
1264 char* gstr = lu(args, "green");
1265 char* bstr = lu(args, "blue");
1266 char* astr = lu(args, "alpha");
1267 char* pinstr = lu(args, "pin");
1276 pivotstr = lu(args, "pivot");
1277 anglestr = lu(args, "angle");
1279 xstr = lu(args, "x");
1280 ystr = lu(args, "y");
1283 luminance = parseMulAdd(luminancestr);
1286 luminance.mul = 256;
1290 if(scalexstr[0]||scaleystr[0])
1291 syntaxerror("scalex/scaley and scale cannot both be set");
1292 scalexstr = scaleystr = scalestr;
1295 if(type == 0 || type == 4) {
1297 character = lu(args, "character");
1298 parameters_clear(&p);
1300 p = s_getParameters(instance);
1305 if(isRelative(xstr)) {
1306 if(type == 0 || type == 4)
1307 syntaxerror("relative x values not allowed for initial put or startclip");
1308 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1310 p.x = parseTwip(xstr);
1314 if(isRelative(ystr)) {
1315 if(type == 0 || type == 4)
1316 syntaxerror("relative y values not allowed for initial put or startclip");
1317 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1319 p.y = parseTwip(ystr);
1323 /* scale, scalex, scaley */
1325 oldbbox = s_getCharBBox(character);
1327 oldbbox = s_getInstanceBBox(instance);
1329 oldwidth = oldbbox.xmax - oldbbox.xmin;
1330 oldheight = oldbbox.ymax - oldbbox.ymin;
1332 if(oldwidth==0) p.scalex = 1.0;
1335 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1339 if(oldheight==0) p.scaley = 1.0;
1342 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1348 if(isRelative(rotatestr)) {
1349 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1351 p.rotate = parseFloat(rotatestr);
1357 if(isRelative(shearstr)) {
1358 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1360 p.shear = parseFloat(shearstr);
1365 if(isPoint(pivotstr))
1366 p.pivot = parsePoint(pivotstr);
1368 p.pivot = getPoint(oldbbox, pivotstr);
1372 p.pin = parsePoint(pinstr);
1374 p.pin = getPoint(oldbbox, pinstr);
1377 /* color transform */
1379 if(rstr[0] || luminancestr[0]) {
1382 r = parseMulAdd(rstr);
1384 r.add = p.cxform.r0;
1385 r.mul = p.cxform.r1;
1387 r = mergeMulAdd(r, luminance);
1388 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1390 if(gstr[0] || luminancestr[0]) {
1393 g = parseMulAdd(gstr);
1395 g.add = p.cxform.g0;
1396 g.mul = p.cxform.g1;
1398 g = mergeMulAdd(g, luminance);
1399 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1401 if(bstr[0] || luminancestr[0]) {
1404 b = parseMulAdd(bstr);
1406 b.add = p.cxform.b0;
1407 b.mul = p.cxform.b1;
1409 b = mergeMulAdd(b, luminance);
1410 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1413 MULADD a = parseMulAdd(astr);
1414 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1418 s_put(instance, character, p);
1420 s_change(instance, p);
1422 s_qchange(instance, p);
1424 s_jump(instance, p);
1426 s_startclip(instance, character, p);
1429 static int c_put(map_t*args)
1431 c_placement(args, 0);
1434 static int c_change(map_t*args)
1436 c_placement(args, 1);
1439 static int c_qchange(map_t*args)
1441 c_placement(args, 2);
1444 static int c_arcchange(map_t*args)
1446 c_placement(args, 2);
1449 static int c_jump(map_t*args)
1451 c_placement(args, 3);
1454 static int c_startclip(map_t*args)
1456 c_placement(args, 4);
1459 static int c_del(map_t*args)
1461 char*instance = lu(args, "name");
1462 s_delinstance(instance);
1465 static int c_end(map_t*args)
1470 static int c_sprite(map_t*args)
1472 char* name = lu(args, "name");
1476 static int c_frame(map_t*args)
1478 char*framestr = lu(args, "n");
1480 if(framestr[0]=='+') {
1481 frame = s_getframe();
1482 frame += parseInt(framestr+1);
1485 frame = parseInt(framestr);
1486 if(s_getframe() >= frame
1487 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1488 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1493 static int c_primitive(map_t*args)
1495 char*name = lu(args, "name");
1496 char*command = lu(args, "commandname");
1497 int width=0, height=0, r=0;
1498 int linewidth = parseTwip(lu(args, "line"));
1499 char*colorstr = lu(args, "color");
1500 RGBA color = parseColor(colorstr);
1501 char*fillstr = lu(args, "fill");
1507 if(!strcmp(command, "circle"))
1509 else if(!strcmp(command, "textshape"))
1513 width = parseTwip(lu(args, "width"));
1514 height = parseTwip(lu(args, "height"));
1515 } else if (type==1) {
1516 r = parseTwip(lu(args, "r"));
1517 } else if (type==2) {
1518 text = lu(args, "text");
1519 font = lu(args, "font");
1522 if(!strcmp(fillstr, "fill"))
1524 if(!strcmp(fillstr, "none"))
1526 if(width<0 || height<0 || linewidth<0 || r<0)
1527 syntaxerror("values width, height, line, r must be positive");
1528 if(!dofill || isColor(fillstr)) {
1530 fill = parseColor(fillstr);
1532 /* FIXME - texture fill */
1533 fill.r = fill.g = 0;
1534 fill.b = fill.a = 255;
1535 warning("texture fill not supported yet. Filling with black.");
1537 if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1538 else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1539 else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1543 static int c_shape(map_t*args)
1545 char*name = lu(args, "name");
1546 char*filename = lu(args, "filename");
1547 s_shape(name, filename);
1551 static int c_font(map_t*args)
1553 char*name = lu(args, "name");
1554 char*filename = lu(args, "filename");
1555 s_font(name, filename);
1559 static int c_text(map_t*args)
1561 char*name = lu(args, "name");
1562 char*text = lu(args, "text");
1563 char*font = lu(args, "font");
1564 float size = parsePercent(lu(args, "size"));
1565 RGBA color = parseColor(lu(args, "color"));
1566 s_text(name, font, text, (int)(size*100), color);
1570 int fakechar(map_t*args)
1572 char*name = lu(args, "name");
1573 s_box(name, 0, 0, black, 20, black, 0);
1576 static int c_circle(map_t*args) {return fakechar(args);}
1578 static int c_egon(map_t*args) {return fakechar(args);}
1579 static int c_button(map_t*args) {return fakechar(args);}
1580 static int c_edittext(map_t*args) {return fakechar(args);}
1582 static int c_morphshape(map_t*args) {return fakechar(args);}
1583 static int c_image(map_t*args) {return fakechar(args);}
1584 static int c_movie(map_t*args) {return fakechar(args);}
1585 static int c_sound(map_t*args) {return fakechar(args);}
1587 static int c_play(map_t*args) {return 0;}
1588 static int c_stop(map_t*args) {return 0;}
1590 static int c_soundtrack(map_t*args) {return 0;}
1591 static int c_buttonsounds(map_t*args) {return 0;}
1592 static int c_buttonput(map_t*args) {return 0;}
1593 static int c_texture(map_t*args) {return 0;}
1594 static int c_action(map_t*args) {return 0;}
1598 command_func_t* func;
1601 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1602 {"frame", c_frame, "n=+1"},
1604 // "import" type stuff
1605 {"shape", c_shape, "name filename"},
1606 {"morphshape", c_morphshape, "name start end"},
1607 {"jpeg", c_image, "name filename quality=80%"},
1608 {"png", c_image, "name filename"},
1609 {"movie", c_movie, "name filename"},
1610 {"sound", c_sound, "name filename"},
1611 {"font", c_font, "name filename"},
1612 {"soundtrack", c_soundtrack, "filename"},
1614 // character generators
1615 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1616 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1617 {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1618 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1619 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1620 {"text", c_text, "name text font size=100% color=white"},
1621 {"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"},
1623 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1626 {"play", c_play, "sound loop=0 @nomultiple=0"},
1627 {"stop", c_stop, "sound"},
1629 // object placement tags
1630 {"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="},
1631 {"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="},
1632 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1633 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1634 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1635 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1636 {"del", c_del, "name"},
1637 // virtual object placement
1638 {"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="},
1639 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1640 {"point", c_point, "name x=0 y=0"},
1642 // commands which start a block
1643 //startclip (see above)
1644 {"sprite", c_sprite, "name"},
1645 {"action", c_action, ""},
1651 static map_t parseArguments(char*command, char*pattern)
1667 string_set(&t1, "commandname");
1668 string_set(&t2, command);
1669 map_put(&result, t1, t2);
1671 if(!pattern || !*pattern)
1678 if(!strncmp("<i> ", x, 3)) {
1680 if(type == COMMAND || type == LABEL) {
1682 syntaxerror("character name expected");
1684 name[pos].str = "instance";
1686 value[pos].str = text;
1687 value[pos].len = strlen(text);
1691 if(type == ASSIGNMENT)
1694 name[pos].str = "character";
1696 value[pos].str = text;
1697 value[pos].len = strlen(text);
1705 isboolean[pos] = (x[0] =='@');
1718 name[pos].len = d-x;
1723 name[pos].len = e-x;
1724 value[pos].str = e+1;
1725 value[pos].len = d-e-1;
1733 /* for(t=0;t<len;t++) {
1734 printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1735 isboolean[t]?"(boolean)":"");
1740 if(type == LABEL || type == COMMAND) {
1745 // first, search for boolean arguments
1746 for(pos=0;pos<len;pos++)
1748 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1750 if(type == ASSIGNMENT)
1752 value[pos].str = text;
1753 value[pos].len = strlen(text);
1754 /*printf("setting boolean parameter %s (to %s)\n",
1755 strndup(name[pos], namelen[pos]),
1756 strndup(value[pos], valuelen[pos]));*/
1761 // second, search for normal arguments
1763 for(pos=0;pos<len;pos++)
1765 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1766 (type != ASSIGNMENT && !set[pos])) {
1768 syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1770 if(type == ASSIGNMENT)
1773 value[pos].str = text;
1774 value[pos].len = strlen(text);
1776 printf("setting parameter %s (to %s)\n",
1777 strndup(name[pos].str, name[pos].len),
1778 strndup(value[pos].str, value[pos].len));
1784 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1788 for(t=0;t<len;t++) {
1789 printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1792 for(t=0;t<len;t++) {
1793 if(value[t].str && value[t].str[0] == '*') {
1794 //relative default- take value from some other parameter
1796 for(s=0;s<len;s++) {
1797 if(value[s].len == value[t].len-1 &&
1798 !strncmp(&value[t].str[1], value[s].str, value[s].len))
1799 value[t].str = value[s].str;
1802 if(value[t].str == 0) {
1804 syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1808 /* ok, now construct the dictionary from the parameters */
1812 map_put(&result, name[t], value[t]);
1816 static void parseArgumentsForCommand(char*command)
1821 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1822 if(!strcmp(arguments[t].command, command)) {
1823 args = parseArguments(command, arguments[t].arguments);
1828 syntaxerror("command %s not known", command);
1831 printf(".%s\n", command);fflush(stdout);
1832 map_dump(&args, stdout, "\t");fflush(stdout);
1835 (*arguments[nr].func)(&args);
1837 if(!strcmp(command, "button") ||
1838 !strcmp(command, "action")) {
1841 if(type == COMMAND) {
1842 if(!strcmp(text, "end"))
1856 int main (int argc,char ** argv)
1859 processargs(argc, argv);
1860 initLog(0,-1,0,0,-1,verbose);
1863 args_callback_usage(argv[0]);
1866 file = generateTokens(filename);
1868 printf("parser returned error.\n");
1875 while(!noMoreTokens()) {
1878 syntaxerror("command expected");
1879 parseArgumentsForCommand(text);