2 Compiles swf code (.sc) files into .swf files.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/log.h"
32 #include "../lib/args.h"
39 static char * filename = 0;
40 static char * outputname = "output.swf";
41 static int verbose = 2;
42 static int override_outputname = 0;
44 static struct options_t options[] =
52 int args_callback_option(char*name,char*val)
54 if(!strcmp(name, "V")) {
55 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
58 else if(!strcmp(name, "o")) {
60 override_outputname = 1;
63 else if(!strcmp(name, "v")) {
68 printf("Unknown option: -%s\n", name);
73 int args_callback_longoption(char*name,char*val)
75 return args_long2shortoption(options, name, val);
77 void args_callback_usage(char*name)
79 printf("Usage: %s [-o filename] file.wav\n", name);
80 printf("\t-v , --verbose\t\t\t Be more verbose\n");
81 printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
82 printf("\t-V , --version\t\t\t Print program version and exit\n");
84 int args_callback_command(char*name,char*val)
87 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
94 static struct token_t* file;
103 static void syntaxerror(char*format, ...)
107 va_start(arglist, format);
108 vsprintf(buf, format, arglist);
110 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
114 static void warning(char*format, ...)
118 va_start(arglist, format);
119 vsprintf(buf, format, arglist);
121 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
124 static void readToken()
126 type = file[pos].type;
128 syntaxerror("unexpected end of file");
130 text = file[pos].text;
131 textlen = strlen(text);
132 line = file[pos].line;
133 column = file[pos].column;
135 //printf("---> %d(%s) %s\n", type, type_names[type], text);
138 static void pushBack()
141 if(!pos) syntaxerror("internal error 3");
146 textlen = strlen(text);
149 column = file[p].column;
152 static int noMoreTokens()
154 if(file[pos].type == END)
159 // ------------------------------ swf routines ----------------------------
163 int type; //0=swf, 1=sprite, 2=clip
169 /* for sprites (1): */
175 dictionary_t oldinstances;
180 static int stackpos = 0;
182 static dictionary_t characters;
183 static dictionary_t images;
184 static char idmap[65536];
185 static TAG*tag = 0; //current tag
187 static int id; //current character id
188 static int currentframe; //current frame in current level
189 static SRECT currentrect; //current bounding box in current level
190 static U16 currentdepth;
191 static dictionary_t instances;
192 static dictionary_t fonts;
193 static dictionary_t sounds;
195 typedef struct _parameters {
197 float scalex, scaley;
205 typedef struct _character {
211 typedef struct _instance {
212 character_t*character;
214 parameters_t parameters;
215 TAG* lastTag; //last tag which set the object
216 U16 lastFrame; //frame lastTag is in
219 static void character_init(character_t*c)
221 memset(c, 0, sizeof(character_t));
223 static character_t* character_new()
226 c = (character_t*)malloc(sizeof(character_t));
230 static void instance_init(instance_t*i)
232 memset(i, 0, sizeof(instance_t));
234 static instance_t* instance_new()
237 c = (instance_t*)malloc(sizeof(instance_t));
242 static void incrementid()
246 syntaxerror("Out of character ids.");
251 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
253 character_t* c = character_new();
255 c->definingTag = ctag;
258 if(dictionary_lookup(&characters, name))
259 syntaxerror("character %s defined twice", name);
260 dictionary_put2(&characters, name, c);
262 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
264 swf_SetString(tag, name);
265 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
268 swf_SetString(tag, name);
270 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
272 character_t* c = character_new();
273 c->definingTag = ctag;
277 if(dictionary_lookup(&images, name))
278 syntaxerror("image %s defined twice", name);
279 dictionary_put2(&images, name, c);
281 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
283 instance_t* i = instance_new();
286 //swf_GetMatrix(0, &i->matrix);
287 if(dictionary_lookup(&instances, name))
288 syntaxerror("object %s defined twice", name);
289 dictionary_put2(&instances, name, i);
293 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)
296 p->scalex = scalex; p->scaley = scaley;
297 p->pin = pin; p->pivot = pivot;
298 p->rotate = rotate; p->cxform = cxform;
302 static void parameters_clear(parameters_t*p)
305 p->scalex = 1.0; p->scaley = 1.0;
306 p->pin.x = 1; p->pin.y = 0;
307 p->pivot.x = 0; p->pivot.y = 0;
310 swf_GetCXForm(0, &p->cxform, 1);
313 static void makeMatrix(MATRIX*m, parameters_t*p)
322 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
323 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
324 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
325 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
327 m->sx = (int)(sx*65536+0.5);
328 m->r1 = (int)(r1*65536+0.5);
329 m->r0 = (int)(r0*65536+0.5);
330 m->sy = (int)(sy*65536+0.5);
334 h = swf_TurnPoint(p->pin, m);
339 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
344 r = swf_TurnRect(i->character->size, &m);
345 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
346 currentrect.xmax == 0 && currentrect.ymax == 0)
349 swf_ExpandRect2(¤trect, &r);
353 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
355 SWF*swf = (SWF*)malloc(sizeof(SWF));
358 syntaxerror(".swf blocks can't be nested");
360 memset(swf, 0, sizeof(swf));
361 swf->fileVersion = version;
363 swf->frameRate = fps;
364 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
365 swf->compressed = compress;
366 swf_SetRGB(tag,&background);
368 if(stackpos==sizeof(stack)/sizeof(stack[0]))
369 syntaxerror("too many levels of recursion");
371 dictionary_init(&characters);
372 dictionary_init(&images);
373 dictionary_init(&instances);
374 dictionary_init(&fonts);
375 dictionary_init(&sounds);
377 memset(&stack[stackpos], 0, sizeof(stack[0]));
378 stack[stackpos].type = 0;
379 stack[stackpos].filename = strdup(name);
380 stack[stackpos].swf = swf;
381 stack[stackpos].oldframe = -1;
386 memset(¤trect, 0, sizeof(currentrect));
389 memset(idmap, 0, sizeof(idmap));
393 void s_sprite(char*name)
395 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
396 swf_SetU16(tag, id); //id
397 swf_SetU16(tag, 0); //frames
399 memset(&stack[stackpos], 0, sizeof(stack[0]));
400 stack[stackpos].type = 1;
401 stack[stackpos].oldframe = currentframe;
402 stack[stackpos].olddepth = currentdepth;
403 stack[stackpos].oldrect = currentrect;
404 stack[stackpos].oldinstances = instances;
405 stack[stackpos].tag = tag;
406 stack[stackpos].id = id;
407 stack[stackpos].name = strdup(name);
409 /* FIXME: those four fields should be bundled together */
410 dictionary_init(&instances);
413 memset(¤trect, 0, sizeof(currentrect));
419 TAG* removeFromTo(TAG*from, TAG*to)
421 TAG*save = from->prev;
423 TAG*next = from->next;
431 static void s_endSprite()
433 SRECT r = currentrect;
435 if(stack[stackpos].cut)
436 tag = removeFromTo(stack[stackpos].cut, tag);
440 /* TODO: before clearing, prepend "<spritename>." to names and
441 copy into old instances dict */
442 dictionary_clear(&instances);
444 currentframe = stack[stackpos].oldframe;
445 currentrect = stack[stackpos].oldrect;
446 currentdepth = stack[stackpos].olddepth;
447 instances = stack[stackpos].oldinstances;
449 tag = swf_InsertTag(tag, ST_END);
451 tag = stack[stackpos].tag;
454 syntaxerror("internal error(7)");
456 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
457 free(stack[stackpos].name);
460 static void s_endSWF()
466 if(stack[stackpos].cut)
467 tag = removeFromTo(stack[stackpos].cut, tag);
471 swf = stack[stackpos].swf;
472 filename = stack[stackpos].filename;
474 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
476 tag = swf_InsertTag(tag, ST_END);
478 swf_OptimizeTagOrder(swf);
480 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
481 swf->movieSize = currentrect; /* "autocrop" */
483 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
484 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
485 swf->movieSize.ymax += 20;
488 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
490 syntaxerror("couldn't create output file %s", filename);
493 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
495 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
499 dictionary_clear(&instances);
500 dictionary_clear(&characters);
501 dictionary_clear(&images);
502 dictionary_clear(&fonts);
503 dictionary_clear(&sounds);
513 if(stack[stackpos-1].type == 0)
514 syntaxerror("End of file encountered in .swf block");
515 if(stack[stackpos-1].type == 1)
516 syntaxerror("End of file encountered in .sprite block");
517 if(stack[stackpos-1].type == 2)
518 syntaxerror("End of file encountered in .clip block");
527 void s_frame(int nr, int cut)
532 for(t=currentframe;t<nr;t++) {
533 tag = swf_InsertTag(tag, ST_SHOWFRAME);
538 syntaxerror("Can't cut, frame empty");
540 stack[stackpos].cut = tag;
546 int parseColor2(char*str, RGBA*color);
548 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
552 if(texture[0] == '#') {
553 parseColor2(texture, &color);
554 return swf_ShapeAddSolidFillStyle(s, &color);
555 } else if((image = dictionary_lookup(&images, texture))) {
557 swf_GetMatrix(0, &m);
558 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
559 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
562 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
563 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
564 } else if ((gradient = dictionary_lookup(&gradients, texture))) {
565 } */ else if (parseColor2(texture, &color)) {
566 return swf_ShapeAddSolidFillStyle(s, &color);
568 syntaxerror("not a color/fillstyle: %s", texture);
573 RGBA black={r:0,g:0,b:0,a:0};
574 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
583 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
585 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
588 fs1 = addFillStyle(s, &r2, texture);
591 r.xmin = r2.xmin-linewidth-linewidth/2;
592 r.ymin = r2.ymin-linewidth-linewidth/2;
593 r.xmax = r2.xmax+linewidth+linewidth/2;
594 r.ymax = r2.ymax+linewidth+linewidth/2;
596 swf_SetShapeHeader(tag,s);
597 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
598 swf_ShapeSetLine(tag,s,width,0);
599 swf_ShapeSetLine(tag,s,0,height);
600 swf_ShapeSetLine(tag,s,-width,0);
601 swf_ShapeSetLine(tag,s,0,-height);
602 swf_ShapeSetEnd(tag);
605 s_addcharacter(name, id, tag, r);
609 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
614 r2.xmin = r2.ymin = 0;
618 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
620 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
622 fs1 = addFillStyle(s, &r2, texture);
624 rect.xmin = r2.xmin-linewidth-linewidth/2;
625 rect.ymin = r2.ymin-linewidth-linewidth/2;
626 rect.xmax = r2.xmax+linewidth+linewidth/2;
627 rect.ymax = r2.ymax+linewidth+linewidth/2;
629 swf_SetRect(tag,&rect);
630 swf_SetShapeHeader(tag,s);
631 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
632 swf_ShapeSetCircle(tag, s, r,r,r,r);
633 swf_ShapeSetEnd(tag);
636 s_addcharacter(name, id, tag, rect);
640 void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, char*texture)
646 U8*text = (U8*)_text;
649 font = dictionary_lookup(&fonts, fontname);
651 syntaxerror("font \"%s\" not known!", fontname);
654 /* the shape information we have implies exactly one fill style
655 This means to support outlines we'd have to rework the whole shape.
657 syntaxerror("textshapes must be filled", fontname);
659 /* TODO: supporting more than once character would be nice... */
661 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
662 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
663 s_box(name, 0, 0, black, 20, 0);
666 g = font->ascii2glyph[text[0]];
667 rect = font->layout->bounds[g];
670 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
672 fs1 = addFillStyle(s, &rect, texture);
674 rect.xmin -= linewidth + linewidth/2;
675 rect.ymin -= linewidth + linewidth/2;
676 rect.xmin += linewidth + linewidth/2;
677 rect.ymin += linewidth + linewidth/2;
679 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
681 swf_SetRect(tag, &rect);
682 swf_SetShapeStyles(tag, s);
683 swf_SetSimpleShape(tag, font->glyph[g].shape);
686 s_addcharacter(name, id, tag, rect);
690 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
695 font = dictionary_lookup(&fonts, fontname);
697 syntaxerror("font \"%s\" not known!", fontname);
699 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
701 if(!font->numchars) {
702 s_box(name, 0, 0, black, 20, 0);
705 r = swf_SetDefineText(tag, font, &color, text, size);
707 s_addcharacter(name, id, tag, r);
711 /* type: either "jpeg" or "png"
713 void s_image(char*name, char*type, char*filename, int quality)
715 /* an image is actually two folded: 1st bitmap, 2nd character.
716 Both of them can be used separately */
718 /* step 1: the bitmap */
723 warning("image type \"png\" not supported yet!");
724 s_box(name, 0, 0, black, 20, 0);
727 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
728 swf_SetU16(tag, imageID);
730 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
731 syntaxerror("Image \"%s\" not found, or contains errors", filename);
734 swf_GetJPEGSize(filename, &width, &height);
741 s_addimage(name, id, tag, r);
744 /* step 2: the character */
745 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
747 swf_ShapeSetBitmapRect(tag, imageID, width, height);
749 s_addcharacter(name, id, tag, r);
753 void dumpSWF(SWF*swf)
755 TAG* tag = swf->firstTag;
756 printf("vvvvvvvvvvvvvvvvvvvvv\n");
758 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
761 printf("^^^^^^^^^^^^^^^^^^^^^\n");
764 void s_font(char*name, char*filename)
769 f = open(filename,O_RDONLY|O_BINARY);
771 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
772 font = (SWFFONT*)malloc(sizeof(SWFFONT));
773 memset(font, 0, sizeof(SWFFONT));
774 dictionary_put2(&fonts, name, font);
778 if (swf_ReadSWF(f,&swf)>=0) {
779 swf_FontExtract(&swf, 0x4e46, &font);
784 syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
789 /* fix the layout. Only needed for old fonts */
791 for(t=0;t<font->numchars;t++) {
792 font->glyph[t].advance = 0;
795 swf_FontCreateLayout(font);
799 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
800 swf_FontSetDefine2(tag, font);
803 if(dictionary_lookup(&fonts, name))
804 syntaxerror("font %s defined twice", name);
805 dictionary_put2(&fonts, name, font);
808 typedef struct _sound_t
814 void s_sound(char*name, char*filename)
816 struct WAV wav, wav2;
821 if(!readWAV(filename, &wav)) {
822 warning("Couldn't read wav file \"%s\"", filename);
826 convertWAV2mono(&wav, &wav2, 44100);
827 samples = (U16*)wav2.data;
828 numsamples = wav2.size/2;
832 tag = swf_InsertTag(tag, ST_DEFINESOUND);
833 swf_SetU16(tag, id); //id
834 swf_SetSoundDefine(tag, samples, numsamples);
836 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
840 if(dictionary_lookup(&sounds, name))
841 syntaxerror("sound %s defined twice", name);
842 dictionary_put2(&sounds, name, sound);
850 void s_playsound(char*name, int loops, int nomultiple, int stop)
852 sound_t* sound = dictionary_lookup(&sounds, name);
855 syntaxerror("Don't know anything about sound \"%s\"", name);
857 tag = swf_InsertTag(tag, ST_STARTSOUND);
858 swf_SetU16(tag, sound->id); //id
859 memset(&info, 0, sizeof(info));
862 info.nomultiple = nomultiple;
863 swf_SetSoundInfo(tag, &info);
866 void s_includeswf(char*name, char*filename)
874 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
875 f = open(filename,O_RDONLY);
877 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
878 s_box(name, 0, 0, black, 20, 0);
881 if (swf_ReadSWF(f,&swf)<0) {
882 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
883 s_box(name, 0, 0, black, 20, 0);
888 /* FIXME: The following sets the bounding Box for the character.
889 It is wrong for two reasons:
890 a) It may be too small (in case objects in the movie clip at the borders)
891 b) it may be too big (because the poor movie never got autocropped)
895 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
899 swf_Relocate(&swf, idmap);
905 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
906 if(cutout[t] == ftag->id) {
910 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
912 if(ftag->id == ST_END)
916 /* We simply dump all tags right after the sprite
917 header, relying on the fact that swf_OptimizeTagOrder() will
918 sort things out for us later.
919 We also rely on the fact that the imported SWF is well-formed.
921 tag = swf_InsertTag(tag, ftag->id);
922 swf_SetBlock(tag, ftag->data, ftag->len);
926 syntaxerror("Included file %s contains errors", filename);
927 tag = swf_InsertTag(tag, ST_END);
931 s_addcharacter(name, id, tag, r);
934 SRECT s_getCharBBox(char*name)
936 character_t* c = dictionary_lookup(&characters, name);
937 if(!c) syntaxerror("character '%s' unknown(2)", name);
940 SRECT s_getInstanceBBox(char*name)
942 instance_t * i = dictionary_lookup(&instances, name);
944 if(!i) syntaxerror("instance '%s' unknown(4)", name);
946 if(!c) syntaxerror("internal error(5)");
949 parameters_t s_getParameters(char*name)
951 instance_t * i = dictionary_lookup(&instances, name);
952 if(!i) syntaxerror("instance '%s' unknown(10)", name);
953 return i->parameters;
955 void s_startclip(char*instance, char*character, parameters_t p)
957 character_t* c = dictionary_lookup(&characters, character);
961 syntaxerror("character %s not known", character);
963 i = s_addinstance(instance, c, currentdepth);
965 m = s_instancepos(i, &p);
967 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
968 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
969 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
971 i->lastFrame= currentframe;
973 stack[stackpos].tag = tag;
974 stack[stackpos].type = 2;
983 swf_SetTagPos(stack[stackpos].tag, 0);
984 swf_GetPlaceObject(stack[stackpos].tag, &p);
985 p.clipdepth = currentdepth;
986 swf_ClearTag(stack[stackpos].tag);
987 swf_SetPlaceObject(stack[stackpos].tag, &p);
991 void s_put(char*instance, char*character, parameters_t p)
993 character_t* c = dictionary_lookup(&characters, character);
997 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1000 i = s_addinstance(instance, c, currentdepth);
1002 m = s_instancepos(i, &p);
1004 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1005 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1007 i->lastFrame = currentframe;
1011 void s_jump(char*instance, parameters_t p)
1013 instance_t* i = dictionary_lookup(&instances, instance);
1016 syntaxerror("instance %s not known", instance);
1020 m = s_instancepos(i, &p);
1022 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1023 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1025 i->lastFrame = currentframe;
1028 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1032 if(num==0 || num==1)
1034 ratio = (float)pos/(float)num;
1036 p.x = (p2->x-p1->x)*ratio + p1->x;
1037 p.y = (p2->y-p1->y)*ratio + p1->y;
1038 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1039 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1040 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1041 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1043 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1044 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1045 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1046 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1048 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1049 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1050 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1051 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1053 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1054 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1055 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1056 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1060 void s_change(char*instance, parameters_t p2)
1062 instance_t* i = dictionary_lookup(&instances, instance);
1066 int frame, allframes;
1068 syntaxerror("instance %s not known", instance);
1072 allframes = currentframe - i->lastFrame - 1;
1074 warning(".change ignored. can only .put/.change an object once per frame.");
1078 m = s_instancepos(i, &p2);
1079 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1080 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1083 /* o.k., we got the start and end point set. Now iterate though all the
1084 tags in between, inserting object changes after each new frame */
1087 if(!t) syntaxerror("internal error(6)");
1089 while(frame < allframes) {
1090 if(t->id == ST_SHOWFRAME) {
1095 p = s_interpolate(&p1, &p2, frame, allframes);
1096 m = s_instancepos(i, &p); //needed?
1097 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1098 i->lastFrame = currentframe;
1099 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1101 if(frame == allframes)
1106 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1110 void s_delinstance(char*instance)
1112 instance_t* i = dictionary_lookup(&instances, instance);
1114 syntaxerror("instance %s not known", instance);
1116 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1117 swf_SetU16(tag, i->depth);
1118 dictionary_del(&instances, instance);
1121 void s_qchange(char*instance, parameters_t p)
1128 syntaxerror(".end unexpected");
1129 if(stack[stackpos-1].type == 0)
1131 else if(stack[stackpos-1].type == 1)
1133 else if(stack[stackpos-1].type == 2)
1135 else syntaxerror("internal error 1");
1138 // ------------------------------------------------------------------------
1140 typedef int command_func_t(map_t*args);
1142 SRECT parseBox(char*str)
1145 float xmin, xmax, ymin, ymax;
1146 char*x = strchr(str, 'x');
1148 if(!strcmp(str, "autocrop")) {
1149 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1153 d1 = strchr(x+1, ':');
1155 d2 = strchr(d1+1, ':');
1157 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1161 else if(d1 && !d2) {
1162 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1168 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1173 r.xmin = (SCOORD)(xmin*20);
1174 r.ymin = (SCOORD)(ymin*20);
1175 r.xmax = (SCOORD)(xmax*20);
1176 r.ymax = (SCOORD)(ymax*20);
1179 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1182 float parseFloat(char*str)
1186 int parseInt(char*str)
1191 if(str[0]=='+' || str[0]=='-')
1195 if(str[t]<'0' || str[t]>'9')
1196 syntaxerror("Not an Integer: \"%s\"", str);
1199 int parseTwip(char*str)
1203 if(str[0]=='+' || str[0]=='-') {
1208 dot = strchr(str, '.');
1212 return sign*parseInt(str)*20;
1214 int l=strlen(++dot);
1216 for(s=str;s<dot-1;s++)
1217 if(*s<'0' || *s>'9')
1218 syntaxerror("Not a coordinate: \"%s\"", str);
1220 if(*s<'0' || *s>'9')
1221 syntaxerror("Not a coordinate: \"%s\"", str);
1223 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1224 warning("precision loss: %s converted to twip", str);
1229 return sign*atoi(str)*20;
1231 return sign*atoi(str)*20+atoi(dot)*2;
1233 return sign*atoi(str)*20+atoi(dot)/5;
1238 int isPoint(char*str)
1240 if(strchr(str, '('))
1246 SPOINT parsePoint(char*str)
1250 int l = strlen(str);
1251 char*comma = strchr(str, ',');
1252 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1253 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1254 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1255 p.x = parseTwip(tmp);
1256 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1257 p.y = parseTwip(tmp);
1261 int parseColor2(char*str, RGBA*color)
1263 int l = strlen(str);
1267 struct {unsigned char r,g,b;char*name;} colors[] =
1268 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1269 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1270 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1271 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1272 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1273 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1274 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1275 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1276 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1277 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1278 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1279 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1283 if(str[0]=='#' && (l==7 || l==9)) {
1284 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1286 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1288 color->r = r; color->g = g; color->b = b; color->a = a;
1291 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1292 if(!strcmp(str, colors[t].name)) {
1297 color->r = r; color->g = g; color->b = b; color->a = a;
1303 RGBA parseColor(char*str)
1306 if(!parseColor2(str, &c))
1307 syntaxerror("Expression '%s' is not a color", str);
1311 typedef struct _muladd {
1316 MULADD parseMulAdd(char*str)
1319 char* str2 = (char*)malloc(strlen(str)+5);
1326 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1327 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1328 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1329 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1330 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1331 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1332 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1333 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1334 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1335 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1337 syntaxerror("'%s' is not a valid color transform expression", str);
1339 m.add = (int)(add*256);
1340 m.mul = (int)(mul*256);
1345 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1347 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1348 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1350 if(a<-32768) a=-32768;
1351 if(a>32767) a=32767;
1352 if(m<-32768) m=-32768;
1353 if(m>32767) m=32767;
1359 float parsePercent(char*str)
1361 int l = strlen(str);
1365 return atoi(str)/100.0;
1367 syntaxerror("Expression '%s' is not a percentage", str);
1370 int isPercent(char*str)
1372 return str[strlen(str)-1]=='%';
1374 int parseNewSize(char*str, int size)
1377 return parsePercent(str)*size;
1379 return (int)(atof(str)*20);
1382 int isColor(char*str)
1385 return parseColor2(str, &c);
1388 static char* lu(map_t* args, char*name)
1390 char* value = map_lookup(args, name);
1392 map_dump(args, stdout, "");
1393 syntaxerror("internal error 2: value %s should be set", name);
1398 static int c_flash(map_t*args)
1400 char* name = lu(args, "name");
1401 char* compressstr = lu(args, "compress");
1402 SRECT bbox = parseBox(lu(args, "bbox"));
1403 int version = parseInt(lu(args, "version"));
1404 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1406 RGBA color = parseColor(lu(args, "background"));
1407 if(!strcmp(name, "!default!") || override_outputname)
1410 if(!strcmp(compressstr, "default"))
1411 compress = version==6;
1412 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1414 else if(!strcmp(compressstr, "no"))
1416 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1418 s_swf(name, bbox, version, fps, compress, color);
1421 int isRelative(char*str)
1423 return !strncmp(str, "<plus>", 6) ||
1424 !strncmp(str, "<minus>", 7);
1426 char* getOffset(char*str)
1428 if(!strncmp(str, "<plus>", 6))
1430 if(!strncmp(str, "<minus>", 7))
1432 syntaxerror("internal error (347)");
1435 int getSign(char*str)
1437 if(!strncmp(str, "<plus>", 6))
1439 if(!strncmp(str, "<minus>", 7))
1441 syntaxerror("internal error (348)");
1444 static dictionary_t points;
1445 static mem_t mpoints;
1446 int points_initialized = 0;
1448 SPOINT getPoint(SRECT r, char*name)
1451 if(!strcmp(name, "center")) {
1453 p.x = (r.xmin + r.xmax)/2;
1454 p.y = (r.ymin + r.ymax)/2;
1458 if(points_initialized)
1459 l = (int)dictionary_lookup(&points, name);
1461 syntaxerror("Invalid point: \"%s\".", name);
1464 return *(SPOINT*)&mpoints.buffer[l];
1466 static int c_gradient(map_t*args)
1470 static int c_point(map_t*args)
1472 char*name = lu(args, "name");
1476 if(!points_initialized) {
1477 dictionary_init(&points);
1479 points_initialized = 1;
1481 p.x = parseTwip(lu(args, "x"));
1482 p.y = parseTwip(lu(args, "y"));
1483 pos = mem_put(&mpoints, &p, sizeof(p));
1484 string_set(&s1, name);
1486 dictionary_put(&points, s1, (void*)pos);
1489 static int c_play(map_t*args)
1491 char*name = lu(args, "sound");
1492 char*loop = lu(args, "loop");
1493 char*nomultiple = lu(args, "nomultiple");
1495 if(!strcmp(nomultiple, "nomultiple"))
1498 nm = parseInt(nomultiple);
1500 s_playsound(name, parseInt(loop), nm, 0);
1504 static int c_stop(map_t*args)
1506 char*name = lu(args, "sound");
1507 s_playsound(name, 0,0,1);
1511 static int c_placement(map_t*args, int type)
1513 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1516 char* luminancestr = lu(args, "luminance");
1517 char* scalestr = lu(args, "scale");
1518 char* scalexstr = lu(args, "scalex");
1519 char* scaleystr = lu(args, "scaley");
1520 char* rotatestr = lu(args, "rotate");
1521 char* shearstr = lu(args, "shear");
1522 char* xstr="", *pivotstr="";
1523 char* ystr="", *anglestr="";
1524 char*above = lu(args, "above"); /*FIXME*/
1525 char*below = lu(args, "below");
1526 char* rstr = lu(args, "red");
1527 char* gstr = lu(args, "green");
1528 char* bstr = lu(args, "blue");
1529 char* astr = lu(args, "alpha");
1530 char* pinstr = lu(args, "pin");
1539 pivotstr = lu(args, "pivot");
1540 anglestr = lu(args, "angle");
1542 xstr = lu(args, "x");
1543 ystr = lu(args, "y");
1546 luminance = parseMulAdd(luminancestr);
1549 luminance.mul = 256;
1553 if(scalexstr[0]||scaleystr[0])
1554 syntaxerror("scalex/scaley and scale cannot both be set");
1555 scalexstr = scaleystr = scalestr;
1558 if(type == 0 || type == 4) {
1560 character = lu(args, "character");
1561 parameters_clear(&p);
1563 p = s_getParameters(instance);
1568 if(isRelative(xstr)) {
1569 if(type == 0 || type == 4)
1570 syntaxerror("relative x values not allowed for initial put or startclip");
1571 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1573 p.x = parseTwip(xstr);
1577 if(isRelative(ystr)) {
1578 if(type == 0 || type == 4)
1579 syntaxerror("relative y values not allowed for initial put or startclip");
1580 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1582 p.y = parseTwip(ystr);
1586 /* scale, scalex, scaley */
1588 oldbbox = s_getCharBBox(character);
1590 oldbbox = s_getInstanceBBox(instance);
1592 oldwidth = oldbbox.xmax - oldbbox.xmin;
1593 oldheight = oldbbox.ymax - oldbbox.ymin;
1595 if(oldwidth==0) p.scalex = 1.0;
1598 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1602 if(oldheight==0) p.scaley = 1.0;
1605 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1611 if(isRelative(rotatestr)) {
1612 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1614 p.rotate = parseFloat(rotatestr);
1620 if(isRelative(shearstr)) {
1621 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1623 p.shear = parseFloat(shearstr);
1628 if(isPoint(pivotstr))
1629 p.pivot = parsePoint(pivotstr);
1631 p.pivot = getPoint(oldbbox, pivotstr);
1635 p.pin = parsePoint(pinstr);
1637 p.pin = getPoint(oldbbox, pinstr);
1640 /* color transform */
1642 if(rstr[0] || luminancestr[0]) {
1645 r = parseMulAdd(rstr);
1647 r.add = p.cxform.r0;
1648 r.mul = p.cxform.r1;
1650 r = mergeMulAdd(r, luminance);
1651 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1653 if(gstr[0] || luminancestr[0]) {
1656 g = parseMulAdd(gstr);
1658 g.add = p.cxform.g0;
1659 g.mul = p.cxform.g1;
1661 g = mergeMulAdd(g, luminance);
1662 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1664 if(bstr[0] || luminancestr[0]) {
1667 b = parseMulAdd(bstr);
1669 b.add = p.cxform.b0;
1670 b.mul = p.cxform.b1;
1672 b = mergeMulAdd(b, luminance);
1673 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1676 MULADD a = parseMulAdd(astr);
1677 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1681 s_put(instance, character, p);
1683 s_change(instance, p);
1685 s_qchange(instance, p);
1687 s_jump(instance, p);
1689 s_startclip(instance, character, p);
1692 static int c_put(map_t*args)
1694 c_placement(args, 0);
1697 static int c_change(map_t*args)
1699 c_placement(args, 1);
1702 static int c_qchange(map_t*args)
1704 c_placement(args, 2);
1707 static int c_arcchange(map_t*args)
1709 c_placement(args, 2);
1712 static int c_jump(map_t*args)
1714 c_placement(args, 3);
1717 static int c_startclip(map_t*args)
1719 c_placement(args, 4);
1722 static int c_del(map_t*args)
1724 char*instance = lu(args, "name");
1725 s_delinstance(instance);
1728 static int c_end(map_t*args)
1733 static int c_sprite(map_t*args)
1735 char* name = lu(args, "name");
1739 static int c_frame(map_t*args)
1741 char*framestr = lu(args, "n");
1742 char*cutstr = lu(args, "cut");
1745 if(strcmp(cutstr, "no"))
1747 if(isRelative(framestr)) {
1748 frame = s_getframe();
1749 if(getSign(framestr)<0)
1750 syntaxerror("relative frame expressions must be positive");
1751 frame += parseInt(getOffset(framestr));
1754 frame = parseInt(framestr);
1755 if(s_getframe() >= frame
1756 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1757 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1759 s_frame(frame, cut);
1762 static int c_primitive(map_t*args)
1764 char*name = lu(args, "name");
1765 char*command = lu(args, "commandname");
1766 int width=0, height=0, r=0;
1767 int linewidth = parseTwip(lu(args, "line"));
1768 char*colorstr = lu(args, "color");
1769 RGBA color = parseColor(colorstr);
1770 char*fillstr = lu(args, "fill");
1776 if(!strcmp(command, "circle"))
1778 else if(!strcmp(command, "textshape"))
1782 width = parseTwip(lu(args, "width"));
1783 height = parseTwip(lu(args, "height"));
1784 } else if (type==1) {
1785 r = parseTwip(lu(args, "r"));
1786 } else if (type==2) {
1787 text = lu(args, "text");
1788 font = lu(args, "font");
1791 if(!strcmp(fillstr, "fill"))
1793 if(!strcmp(fillstr, "none"))
1795 if(width<0 || height<0 || linewidth<0 || r<0)
1796 syntaxerror("values width, height, line, r must be positive");
1798 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1799 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1800 else if(type==2) s_textshape(name, font, text, color, linewidth, fillstr);
1804 static int c_swf(map_t*args)
1806 char*name = lu(args, "name");
1807 char*filename = lu(args, "filename");
1808 char*command = lu(args, "commandname");
1809 if(!strcmp(command, "shape"))
1810 warning("Please use .swf instead of .shape");
1811 s_includeswf(name, filename);
1815 static int c_font(map_t*args)
1817 char*name = lu(args, "name");
1818 char*filename = lu(args, "filename");
1819 s_font(name, filename);
1823 static int c_sound(map_t*args)
1825 char*name = lu(args, "name");
1826 char*filename = lu(args, "filename");
1827 s_sound(name, filename);
1831 static int c_text(map_t*args)
1833 char*name = lu(args, "name");
1834 char*text = lu(args, "text");
1835 char*font = lu(args, "font");
1836 float size = parsePercent(lu(args, "size"));
1837 RGBA color = parseColor(lu(args, "color"));
1838 s_text(name, font, text, (int)(size*100), color);
1842 static int c_soundtrack(map_t*args)
1847 static int c_image(map_t*args)
1849 char*command = lu(args, "commandname");
1850 char*name = lu(args, "name");
1851 char*filename = lu(args, "filename");
1852 if(!strcmp(command,"jpeg")) {
1853 int quality = (int)(parsePercent(lu(args, "quality"))*100);
1854 s_image(name, "jpeg", filename, quality);
1856 s_image(name, "png", filename, 0);
1861 int fakechar(map_t*args)
1863 char*name = lu(args, "name");
1864 s_box(name, 0, 0, black, 20, 0);
1868 static int c_egon(map_t*args) {return fakechar(args);}
1869 static int c_button(map_t*args) {return fakechar(args);}
1870 static int c_edittext(map_t*args) {return fakechar(args);}
1872 static int c_morphshape(map_t*args) {return fakechar(args);}
1873 static int c_movie(map_t*args) {return fakechar(args);}
1875 static int c_buttonsounds(map_t*args) {return 0;}
1876 static int c_buttonput(map_t*args) {return 0;}
1877 static int c_texture(map_t*args) {return 0;}
1878 static int c_action(map_t*args) {return 0;}
1882 command_func_t* func;
1885 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
1886 {"frame", c_frame, "n=<plus>1 @cut=no"},
1887 // "import" type stuff
1888 {"swf", c_swf, "name filename"},
1889 {"shape", c_swf, "name filename"},
1890 {"morphshape", c_morphshape, "name start end"},
1891 {"jpeg", c_image, "name filename quality=80%"},
1892 {"png", c_image, "name filename"},
1893 {"movie", c_movie, "name filename"},
1894 {"sound", c_sound, "name filename"},
1895 {"font", c_font, "name filename"},
1896 {"soundtrack", c_soundtrack, "filename"},
1898 // generators of primitives
1900 {"point", c_point, "name x=0 y=0"},
1901 {"gradient", c_gradient, "name"},
1903 // character generators
1904 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1905 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1906 {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1908 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1909 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1910 {"text", c_text, "name text font size=100% color=white"},
1911 {"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"},
1913 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1916 {"play", c_play, "sound loop=0 @nomultiple=0"},
1917 {"stop", c_stop, "sound"},
1919 // object placement tags
1920 {"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="},
1921 {"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="},
1922 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1923 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1924 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1925 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1926 {"del", c_del, "name"},
1927 // virtual object placement
1928 {"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="},
1929 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1931 // commands which start a block
1932 //startclip (see above)
1933 {"sprite", c_sprite, "name"},
1934 {"action", c_action, ""},
1940 static map_t parseArguments(char*command, char*pattern)
1956 string_set(&t1, "commandname");
1957 string_set(&t2, command);
1958 map_put(&result, t1, t2);
1960 if(!pattern || !*pattern)
1967 if(!strncmp("<i> ", x, 3)) {
1969 if(type == COMMAND || type == LABEL) {
1971 syntaxerror("character name expected");
1973 name[pos].str = "instance";
1975 value[pos].str = text;
1976 value[pos].len = strlen(text);
1980 if(type == ASSIGNMENT)
1983 name[pos].str = "character";
1985 value[pos].str = text;
1986 value[pos].len = strlen(text);
1994 isboolean[pos] = (x[0] =='@');
2007 name[pos].len = d-x;
2012 name[pos].len = e-x;
2013 value[pos].str = e+1;
2014 value[pos].len = d-e-1;
2022 /* for(t=0;t<len;t++) {
2023 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2024 isboolean[t]?"(boolean)":"");
2029 if(type == LABEL || type == COMMAND) {
2034 // first, search for boolean arguments
2035 for(pos=0;pos<len;pos++)
2037 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2039 if(type == ASSIGNMENT)
2041 value[pos].str = text;
2042 value[pos].len = strlen(text);
2043 /*printf("setting boolean parameter %s (to %s)\n",
2044 strdup_n(name[pos], namelen[pos]),
2045 strdup_n(value[pos], valuelen[pos]));*/
2050 // second, search for normal arguments
2052 for(pos=0;pos<len;pos++)
2054 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2055 (type != ASSIGNMENT && !set[pos])) {
2057 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2059 if(type == ASSIGNMENT)
2062 value[pos].str = text;
2063 value[pos].len = strlen(text);
2065 printf("setting parameter %s (to %s)\n",
2066 strdup_n(name[pos].str, name[pos].len),
2067 strdup_n(value[pos].str, value[pos].len));
2073 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2077 for(t=0;t<len;t++) {
2078 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2081 for(t=0;t<len;t++) {
2082 if(value[t].str && value[t].str[0] == '*') {
2083 //relative default- take value from some other parameter
2085 for(s=0;s<len;s++) {
2086 if(value[s].len == value[t].len-1 &&
2087 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2088 value[t].str = value[s].str;
2091 if(value[t].str == 0) {
2093 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2097 /* ok, now construct the dictionary from the parameters */
2101 map_put(&result, name[t], value[t]);
2105 static void parseArgumentsForCommand(char*command)
2110 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2111 if(!strcmp(arguments[t].command, command)) {
2113 /* ugly hack- will be removed soon (once documentation and .sc generating
2114 utilities have been changed) */
2115 if(!strcmp(command, "swf") && !stackpos) {
2116 warning("Please use .flash instead of .swf- this will be mandatory soon");
2121 args = parseArguments(command, arguments[t].arguments);
2127 syntaxerror("command %s not known", command);
2130 printf(".%s\n", command);fflush(stdout);
2131 map_dump(&args, stdout, "\t");fflush(stdout);
2134 (*arguments[nr].func)(&args);
2136 if(!strcmp(command, "button") ||
2137 !strcmp(command, "action")) {
2140 if(type == COMMAND) {
2141 if(!strcmp(text, "end"))
2155 int main (int argc,char ** argv)
2158 processargs(argc, argv);
2159 initLog(0,-1,0,0,-1,verbose);
2162 args_callback_usage(argv[0]);
2165 file = generateTokens(filename);
2167 printf("parser returned error.\n");
2174 while(!noMoreTokens()) {
2177 syntaxerror("command expected");
2178 parseArgumentsForCommand(text);