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/drawer.h"
32 #include "../lib/log.h"
33 #include "../lib/args.h"
40 static char * filename = 0;
41 static char * outputname = "output.swf";
42 static int verbose = 2;
43 static int override_outputname = 0;
45 static struct options_t options[] =
53 int args_callback_option(char*name,char*val)
55 if(!strcmp(name, "V")) {
56 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
59 else if(!strcmp(name, "o")) {
61 override_outputname = 1;
64 else if(!strcmp(name, "v")) {
69 printf("Unknown option: -%s\n", name);
74 int args_callback_longoption(char*name,char*val)
76 return args_long2shortoption(options, name, val);
78 void args_callback_usage(char*name)
80 printf("Usage: %s [-o filename] file.wav\n", name);
81 printf("\t-v , --verbose\t\t\t Be more verbose\n");
82 printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
83 printf("\t-V , --version\t\t\t Print program version and exit\n");
85 int args_callback_command(char*name,char*val)
88 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
95 static struct token_t* file;
104 static void syntaxerror(char*format, ...)
108 va_start(arglist, format);
109 vsprintf(buf, format, arglist);
111 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
115 static void warning(char*format, ...)
119 va_start(arglist, format);
120 vsprintf(buf, format, arglist);
122 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
125 static void readToken()
127 type = file[pos].type;
129 syntaxerror("unexpected end of file");
131 text = file[pos].text;
132 textlen = strlen(text);
133 line = file[pos].line;
134 column = file[pos].column;
136 //printf("---> %d(%s) %s\n", type, type_names[type], text);
139 static void pushBack()
142 if(!pos) syntaxerror("internal error 3");
147 textlen = strlen(text);
150 column = file[p].column;
153 static int noMoreTokens()
155 if(file[pos].type == END)
160 // ------------------------------ swf routines ----------------------------
164 int type; //0=swf, 1=sprite, 2=clip
170 /* for sprites (1): */
176 dictionary_t oldinstances;
181 static int stackpos = 0;
183 static dictionary_t characters;
184 static dictionary_t images;
185 static dictionary_t outlines;
186 static dictionary_t gradients;
187 static char idmap[65536];
188 static TAG*tag = 0; //current tag
190 static int id; //current character id
191 static int currentframe; //current frame in current level
192 static SRECT currentrect; //current bounding box in current level
193 static U16 currentdepth;
194 static dictionary_t instances;
195 static dictionary_t fonts;
196 static dictionary_t sounds;
198 typedef struct _parameters {
200 float scalex, scaley;
208 typedef struct _character {
214 typedef struct _instance {
215 character_t*character;
217 parameters_t parameters;
218 TAG* lastTag; //last tag which set the object
219 U16 lastFrame; //frame lastTag is in
222 typedef struct _outline {
227 typedef struct _gradient {
232 static void character_init(character_t*c)
234 memset(c, 0, sizeof(character_t));
236 static character_t* character_new()
239 c = (character_t*)malloc(sizeof(character_t));
243 static void instance_init(instance_t*i)
245 memset(i, 0, sizeof(instance_t));
247 static instance_t* instance_new()
250 c = (instance_t*)malloc(sizeof(instance_t));
255 static void incrementid()
259 syntaxerror("Out of character ids.");
264 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
266 character_t* c = character_new();
268 c->definingTag = ctag;
271 if(dictionary_lookup(&characters, name))
272 syntaxerror("character %s defined twice", name);
273 dictionary_put2(&characters, name, c);
275 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
277 swf_SetString(tag, name);
278 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
281 swf_SetString(tag, name);
283 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
285 character_t* c = character_new();
286 c->definingTag = ctag;
290 if(dictionary_lookup(&images, name))
291 syntaxerror("image %s defined twice", name);
292 dictionary_put2(&images, name, c);
294 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
296 instance_t* i = instance_new();
299 //swf_GetMatrix(0, &i->matrix);
300 if(dictionary_lookup(&instances, name))
301 syntaxerror("object %s defined twice", name);
302 dictionary_put2(&instances, name, i);
306 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)
309 p->scalex = scalex; p->scaley = scaley;
310 p->pin = pin; p->pivot = pivot;
311 p->rotate = rotate; p->cxform = cxform;
315 static void parameters_clear(parameters_t*p)
318 p->scalex = 1.0; p->scaley = 1.0;
319 p->pin.x = 1; p->pin.y = 0;
320 p->pivot.x = 0; p->pivot.y = 0;
323 swf_GetCXForm(0, &p->cxform, 1);
326 static void makeMatrix(MATRIX*m, parameters_t*p)
335 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
336 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
337 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
338 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
340 m->sx = (int)(sx*65536+0.5);
341 m->r1 = (int)(r1*65536+0.5);
342 m->r0 = (int)(r0*65536+0.5);
343 m->sy = (int)(sy*65536+0.5);
347 h = swf_TurnPoint(p->pin, m);
352 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
357 r = swf_TurnRect(i->character->size, &m);
358 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
359 currentrect.xmax == 0 && currentrect.ymax == 0)
362 swf_ExpandRect2(¤trect, &r);
366 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
368 SWF*swf = (SWF*)malloc(sizeof(SWF));
371 syntaxerror(".swf blocks can't be nested");
373 memset(swf, 0, sizeof(swf));
374 swf->fileVersion = version;
376 swf->frameRate = fps;
377 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
378 swf->compressed = compress;
379 swf_SetRGB(tag,&background);
381 if(stackpos==sizeof(stack)/sizeof(stack[0]))
382 syntaxerror("too many levels of recursion");
384 dictionary_init(&characters);
385 dictionary_init(&images);
386 dictionary_init(&outlines);
387 dictionary_init(&gradients);
388 dictionary_init(&instances);
389 dictionary_init(&fonts);
390 dictionary_init(&sounds);
392 memset(&stack[stackpos], 0, sizeof(stack[0]));
393 stack[stackpos].type = 0;
394 stack[stackpos].filename = strdup(name);
395 stack[stackpos].swf = swf;
396 stack[stackpos].oldframe = -1;
401 memset(¤trect, 0, sizeof(currentrect));
404 memset(idmap, 0, sizeof(idmap));
408 void s_sprite(char*name)
410 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
411 swf_SetU16(tag, id); //id
412 swf_SetU16(tag, 0); //frames
414 memset(&stack[stackpos], 0, sizeof(stack[0]));
415 stack[stackpos].type = 1;
416 stack[stackpos].oldframe = currentframe;
417 stack[stackpos].olddepth = currentdepth;
418 stack[stackpos].oldrect = currentrect;
419 stack[stackpos].oldinstances = instances;
420 stack[stackpos].tag = tag;
421 stack[stackpos].id = id;
422 stack[stackpos].name = strdup(name);
424 /* FIXME: those four fields should be bundled together */
425 dictionary_init(&instances);
428 memset(¤trect, 0, sizeof(currentrect));
434 TAG* removeFromTo(TAG*from, TAG*to)
436 TAG*save = from->prev;
438 TAG*next = from->next;
446 static void s_endSprite()
448 SRECT r = currentrect;
450 if(stack[stackpos].cut)
451 tag = removeFromTo(stack[stackpos].cut, tag);
455 /* TODO: before clearing, prepend "<spritename>." to names and
456 copy into old instances dict */
457 dictionary_clear(&instances);
459 currentframe = stack[stackpos].oldframe;
460 currentrect = stack[stackpos].oldrect;
461 currentdepth = stack[stackpos].olddepth;
462 instances = stack[stackpos].oldinstances;
464 tag = swf_InsertTag(tag, ST_END);
466 tag = stack[stackpos].tag;
469 syntaxerror("internal error(7)");
471 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
472 free(stack[stackpos].name);
475 static void s_endSWF()
481 if(stack[stackpos].cut)
482 tag = removeFromTo(stack[stackpos].cut, tag);
486 swf = stack[stackpos].swf;
487 filename = stack[stackpos].filename;
489 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
491 tag = swf_InsertTag(tag, ST_END);
493 swf_OptimizeTagOrder(swf);
495 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
496 swf->movieSize = currentrect; /* "autocrop" */
498 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
499 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
500 swf->movieSize.ymax += 20;
503 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
505 syntaxerror("couldn't create output file %s", filename);
508 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
510 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
514 dictionary_clear(&instances);
515 dictionary_clear(&characters);
516 dictionary_clear(&images);
517 dictionary_clear(&outlines);
518 dictionary_clear(&gradients);
519 dictionary_clear(&fonts);
520 dictionary_clear(&sounds);
530 if(stack[stackpos-1].type == 0)
531 syntaxerror("End of file encountered in .flash block");
532 if(stack[stackpos-1].type == 1)
533 syntaxerror("End of file encountered in .sprite block");
534 if(stack[stackpos-1].type == 2)
535 syntaxerror("End of file encountered in .clip block");
544 void s_frame(int nr, int cut)
549 for(t=currentframe;t<nr;t++) {
550 tag = swf_InsertTag(tag, ST_SHOWFRAME);
555 syntaxerror("Can't cut, frame empty");
557 stack[stackpos].cut = tag;
563 int parseColor2(char*str, RGBA*color);
565 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
570 if(texture[0] == '#') {
571 parseColor2(texture, &color);
572 return swf_ShapeAddSolidFillStyle(s, &color);
573 } else if((image = dictionary_lookup(&images, texture))) {
575 swf_GetMatrix(0, &m);
576 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
577 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
580 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
581 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
582 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
584 swf_GetMatrix(0, &m);
585 m.sx = (r->xmax - r->xmin)*2;
586 m.sy = (r->ymax - r->ymin)*2;
587 m.tx = r->xmin + (r->xmax - r->xmin)/2;
588 m.ty = r->ymin + (r->ymax - r->ymin)/2;
589 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
590 } else if (parseColor2(texture, &color)) {
591 return swf_ShapeAddSolidFillStyle(s, &color);
593 syntaxerror("not a color/fillstyle: %s", texture);
598 RGBA black={r:0,g:0,b:0,a:0};
599 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
608 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
610 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
613 fs1 = addFillStyle(s, &r2, texture);
616 r.xmin = r2.xmin-linewidth-linewidth/2;
617 r.ymin = r2.ymin-linewidth-linewidth/2;
618 r.xmax = r2.xmax+linewidth+linewidth/2;
619 r.ymax = r2.ymax+linewidth+linewidth/2;
621 swf_SetShapeHeader(tag,s);
622 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
623 swf_ShapeSetLine(tag,s,width,0);
624 swf_ShapeSetLine(tag,s,0,height);
625 swf_ShapeSetLine(tag,s,-width,0);
626 swf_ShapeSetLine(tag,s,0,-height);
627 swf_ShapeSetEnd(tag);
630 s_addcharacter(name, id, tag, r);
634 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
640 outline = dictionary_lookup(&outlines, outlinename);
642 syntaxerror("outline %s not defined", outlinename);
646 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
648 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
650 fs1 = addFillStyle(s, &r2, texture);
652 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
654 rect.xmin = r2.xmin-linewidth-linewidth/2;
655 rect.ymin = r2.ymin-linewidth-linewidth/2;
656 rect.xmax = r2.xmax+linewidth+linewidth/2;
657 rect.ymax = r2.ymax+linewidth+linewidth/2;
659 swf_SetRect(tag,&rect);
660 swf_SetShapeStyles(tag, s);
661 swf_SetShapeBits(tag, outline->shape); //does not count bits!
662 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
665 s_addcharacter(name, id, tag, rect);
669 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
674 r2.xmin = r2.ymin = 0;
678 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
680 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
682 fs1 = addFillStyle(s, &r2, texture);
684 rect.xmin = r2.xmin-linewidth-linewidth/2;
685 rect.ymin = r2.ymin-linewidth-linewidth/2;
686 rect.xmax = r2.xmax+linewidth+linewidth/2;
687 rect.ymax = r2.ymax+linewidth+linewidth/2;
689 swf_SetRect(tag,&rect);
690 swf_SetShapeHeader(tag,s);
691 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
692 swf_ShapeSetCircle(tag, s, r,r,r,r);
693 swf_ShapeSetEnd(tag);
696 s_addcharacter(name, id, tag, rect);
700 void s_textshape(char*name, char*fontname, char*_text)
703 U8*text = (U8*)_text;
707 font = dictionary_lookup(&fonts, fontname);
709 syntaxerror("font \"%s\" not known!", fontname);
711 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
712 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
713 s_box(name, 0, 0, black, 20, 0);
716 g = font->ascii2glyph[text[0]];
718 outline = malloc(sizeof(outline_t));
719 memset(outline, 0, sizeof(outline_t));
720 outline->shape = font->glyph[g].shape;
721 outline->bbox = font->layout->bounds[g];
725 swf_Shape11DrawerInit(&draw, 0);
726 swf_DrawText(&draw, font, _text);
728 outline->shape = swf_ShapeDrawerToShape(&draw);
729 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
733 if(dictionary_lookup(&outlines, name))
734 syntaxerror("outline %s defined twice", name);
735 dictionary_put2(&outlines, name, outline);
738 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
743 font = dictionary_lookup(&fonts, fontname);
745 syntaxerror("font \"%s\" not known!", fontname);
747 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
749 if(!font->numchars) {
750 s_box(name, 0, 0, black, 20, 0);
753 r = swf_SetDefineText(tag, font, &color, text, size);
755 s_addcharacter(name, id, tag, r);
759 /* type: either "jpeg" or "png"
761 void s_image(char*name, char*type, char*filename, int quality)
763 /* an image is actually two folded: 1st bitmap, 2nd character.
764 Both of them can be used separately */
766 /* step 1: the bitmap */
771 warning("image type \"png\" not supported yet!");
772 s_box(name, 0, 0, black, 20, 0);
775 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
776 swf_SetU16(tag, imageID);
778 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
779 syntaxerror("Image \"%s\" not found, or contains errors", filename);
782 swf_GetJPEGSize(filename, &width, &height);
789 s_addimage(name, id, tag, r);
792 /* step 2: the character */
793 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
795 swf_ShapeSetBitmapRect(tag, imageID, width, height);
797 s_addcharacter(name, id, tag, r);
801 void dumpSWF(SWF*swf)
803 TAG* tag = swf->firstTag;
804 printf("vvvvvvvvvvvvvvvvvvvvv\n");
806 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
809 printf("^^^^^^^^^^^^^^^^^^^^^\n");
812 void s_font(char*name, char*filename)
817 f = open(filename,O_RDONLY|O_BINARY);
819 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
820 font = (SWFFONT*)malloc(sizeof(SWFFONT));
821 memset(font, 0, sizeof(SWFFONT));
822 dictionary_put2(&fonts, name, font);
826 if (swf_ReadSWF(f,&swf)>=0) {
827 swf_FontExtract(&swf, 0x4e46, &font);
832 syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
837 /* fix the layout. Only needed for old fonts */
839 for(t=0;t<font->numchars;t++) {
840 font->glyph[t].advance = 0;
843 swf_FontCreateLayout(font);
847 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
848 swf_FontSetDefine2(tag, font);
851 if(dictionary_lookup(&fonts, name))
852 syntaxerror("font %s defined twice", name);
853 dictionary_put2(&fonts, name, font);
858 typedef struct _sound_t
864 void s_sound(char*name, char*filename)
866 struct WAV wav, wav2;
871 if(!readWAV(filename, &wav)) {
872 warning("Couldn't read wav file \"%s\"", filename);
876 convertWAV2mono(&wav, &wav2, 44100);
877 samples = (U16*)wav2.data;
878 numsamples = wav2.size/2;
882 tag = swf_InsertTag(tag, ST_DEFINESOUND);
883 swf_SetU16(tag, id); //id
884 swf_SetSoundDefine(tag, samples, numsamples);
886 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
890 if(dictionary_lookup(&sounds, name))
891 syntaxerror("sound %s defined twice", name);
892 dictionary_put2(&sounds, name, sound);
900 static char* gradient_getToken(const char**p)
904 while(**p && strchr(" \t\n\r", **p)) {
908 while(**p && !strchr(" \t\n\r", **p)) {
911 result = malloc((*p)-start+1);
912 memcpy(result,start,(*p)-start+1);
913 result[(*p)-start] = 0;
917 float parsePercent(char*str);
918 RGBA parseColor(char*str);
920 GRADIENT parseGradient(const char*str)
924 memset(&gradient, 0, sizeof(GRADIENT));
926 char*posstr,*colorstr;
927 posstr = gradient_getToken(&p);
930 float pos = parsePercent(posstr);
931 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
932 colorstr = gradient_getToken(&p);
933 RGBA color = parseColor(colorstr);
934 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
935 warning("gradient record too big- max size is 8, rest ignored");
938 gradient.ratios[gradient.num] = (int)(pos*255.0);
939 gradient.rgba[gradient.num] = color;
947 void s_gradient(char*name, const char*text, int radial)
949 gradient_t* gradient;
950 gradient = malloc(sizeof(gradient_t));
951 memset(gradient, 0, sizeof(gradient_t));
952 gradient->gradient = parseGradient(text);
953 gradient->radial = radial;
955 if(dictionary_lookup(&gradients, name))
956 syntaxerror("gradient %s defined twice", name);
957 dictionary_put2(&gradients, name, gradient);
960 void s_outline(char*name, char*format, char*source)
969 swf_Shape11DrawerInit(&draw, 0);
970 draw_string(&draw, source);
972 shape = swf_ShapeDrawerToShape(&draw);
973 //shape2 = swf_ShapeToShape2(shape);
974 //bounds = swf_GetShapeBoundingBox(shape2);
975 //swf_Shape2Free(shape2);
976 bounds = swf_ShapeDrawerGetBBox(&draw);
979 outline = (outline_t*)malloc(sizeof(outline_t));
980 memset(outline, 0, sizeof(outline_t));
981 outline->shape = shape;
982 outline->bbox = bounds;
984 if(dictionary_lookup(&outlines, name))
985 syntaxerror("outline %s defined twice", name);
986 dictionary_put2(&outlines, name, outline);
989 void s_playsound(char*name, int loops, int nomultiple, int stop)
991 sound_t* sound = dictionary_lookup(&sounds, name);
994 syntaxerror("Don't know anything about sound \"%s\"", name);
996 tag = swf_InsertTag(tag, ST_STARTSOUND);
997 swf_SetU16(tag, sound->id); //id
998 memset(&info, 0, sizeof(info));
1001 info.nomultiple = nomultiple;
1002 swf_SetSoundInfo(tag, &info);
1005 void s_includeswf(char*name, char*filename)
1013 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1014 f = open(filename,O_RDONLY);
1016 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1017 s_box(name, 0, 0, black, 20, 0);
1020 if (swf_ReadSWF(f,&swf)<0) {
1021 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1022 s_box(name, 0, 0, black, 20, 0);
1027 /* FIXME: The following sets the bounding Box for the character.
1028 It is wrong for two reasons:
1029 a) It may be too small (in case objects in the movie clip at the borders)
1030 b) it may be too big (because the poor movie never got autocropped)
1034 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1035 swf_SetU16(tag, id);
1038 swf_Relocate(&swf, idmap);
1040 ftag = swf.firstTag;
1044 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1045 if(cutout[t] == ftag->id) {
1049 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1051 if(ftag->id == ST_END)
1055 /* We simply dump all tags right after the sprite
1056 header, relying on the fact that swf_OptimizeTagOrder() will
1057 sort things out for us later.
1058 We also rely on the fact that the imported SWF is well-formed.
1060 tag = swf_InsertTag(tag, ftag->id);
1061 swf_SetBlock(tag, ftag->data, ftag->len);
1065 syntaxerror("Included file %s contains errors", filename);
1066 tag = swf_InsertTag(tag, ST_END);
1070 s_addcharacter(name, id, tag, r);
1073 SRECT s_getCharBBox(char*name)
1075 character_t* c = dictionary_lookup(&characters, name);
1076 if(!c) syntaxerror("character '%s' unknown(2)", name);
1079 SRECT s_getInstanceBBox(char*name)
1081 instance_t * i = dictionary_lookup(&instances, name);
1083 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1085 if(!c) syntaxerror("internal error(5)");
1088 parameters_t s_getParameters(char*name)
1090 instance_t * i = dictionary_lookup(&instances, name);
1091 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1092 return i->parameters;
1094 void s_startclip(char*instance, char*character, parameters_t p)
1096 character_t* c = dictionary_lookup(&characters, character);
1100 syntaxerror("character %s not known", character);
1102 i = s_addinstance(instance, c, currentdepth);
1104 m = s_instancepos(i, &p);
1106 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1107 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1108 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1110 i->lastFrame= currentframe;
1112 stack[stackpos].tag = tag;
1113 stack[stackpos].type = 2;
1122 swf_SetTagPos(stack[stackpos].tag, 0);
1123 swf_GetPlaceObject(stack[stackpos].tag, &p);
1124 p.clipdepth = currentdepth;
1125 swf_ClearTag(stack[stackpos].tag);
1126 swf_SetPlaceObject(stack[stackpos].tag, &p);
1130 void s_put(char*instance, char*character, parameters_t p)
1132 character_t* c = dictionary_lookup(&characters, character);
1136 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1139 i = s_addinstance(instance, c, currentdepth);
1141 m = s_instancepos(i, &p);
1143 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1144 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1146 i->lastFrame = currentframe;
1150 void s_jump(char*instance, parameters_t p)
1152 instance_t* i = dictionary_lookup(&instances, instance);
1155 syntaxerror("instance %s not known", instance);
1159 m = s_instancepos(i, &p);
1161 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1162 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1164 i->lastFrame = currentframe;
1167 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1171 if(num==0 || num==1)
1173 ratio = (float)pos/(float)num;
1175 p.x = (p2->x-p1->x)*ratio + p1->x;
1176 p.y = (p2->y-p1->y)*ratio + p1->y;
1177 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1178 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1179 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1180 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1182 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1183 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1184 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1185 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1187 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1188 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1189 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1190 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1192 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1193 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1194 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1195 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1199 void s_change(char*instance, parameters_t p2)
1201 instance_t* i = dictionary_lookup(&instances, instance);
1205 int frame, allframes;
1207 syntaxerror("instance %s not known", instance);
1211 allframes = currentframe - i->lastFrame - 1;
1213 warning(".change ignored. can only .put/.change an object once per frame.");
1217 m = s_instancepos(i, &p2);
1218 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1219 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1222 /* o.k., we got the start and end point set. Now iterate though all the
1223 tags in between, inserting object changes after each new frame */
1226 if(!t) syntaxerror("internal error(6)");
1228 while(frame < allframes) {
1229 if(t->id == ST_SHOWFRAME) {
1234 p = s_interpolate(&p1, &p2, frame, allframes);
1235 m = s_instancepos(i, &p); //needed?
1236 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1237 i->lastFrame = currentframe;
1238 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1240 if(frame == allframes)
1245 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1249 void s_delinstance(char*instance)
1251 instance_t* i = dictionary_lookup(&instances, instance);
1253 syntaxerror("instance %s not known", instance);
1255 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1256 swf_SetU16(tag, i->depth);
1257 dictionary_del(&instances, instance);
1260 void s_qchange(char*instance, parameters_t p)
1267 syntaxerror(".end unexpected");
1268 if(stack[stackpos-1].type == 0)
1270 else if(stack[stackpos-1].type == 1)
1272 else if(stack[stackpos-1].type == 2)
1274 else syntaxerror("internal error 1");
1277 // ------------------------------------------------------------------------
1279 typedef int command_func_t(map_t*args);
1281 SRECT parseBox(char*str)
1284 float xmin, xmax, ymin, ymax;
1285 char*x = strchr(str, 'x');
1287 if(!strcmp(str, "autocrop")) {
1288 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1292 d1 = strchr(x+1, ':');
1294 d2 = strchr(d1+1, ':');
1296 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1300 else if(d1 && !d2) {
1301 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1307 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1312 r.xmin = (SCOORD)(xmin*20);
1313 r.ymin = (SCOORD)(ymin*20);
1314 r.xmax = (SCOORD)(xmax*20);
1315 r.ymax = (SCOORD)(ymax*20);
1318 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1321 float parseFloat(char*str)
1325 int parseInt(char*str)
1330 if(str[0]=='+' || str[0]=='-')
1334 if(str[t]<'0' || str[t]>'9')
1335 syntaxerror("Not an Integer: \"%s\"", str);
1338 int parseTwip(char*str)
1342 if(str[0]=='+' || str[0]=='-') {
1347 dot = strchr(str, '.');
1351 return sign*parseInt(str)*20;
1353 int l=strlen(++dot);
1355 for(s=str;s<dot-1;s++)
1356 if(*s<'0' || *s>'9')
1357 syntaxerror("Not a coordinate: \"%s\"", str);
1359 if(*s<'0' || *s>'9')
1360 syntaxerror("Not a coordinate: \"%s\"", str);
1362 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1363 warning("precision loss: %s converted to twip", str);
1368 return sign*atoi(str)*20;
1370 return sign*atoi(str)*20+atoi(dot)*2;
1372 return sign*atoi(str)*20+atoi(dot)/5;
1377 int isPoint(char*str)
1379 if(strchr(str, '('))
1385 SPOINT parsePoint(char*str)
1389 int l = strlen(str);
1390 char*comma = strchr(str, ',');
1391 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1392 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1393 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1394 p.x = parseTwip(tmp);
1395 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1396 p.y = parseTwip(tmp);
1400 int parseColor2(char*str, RGBA*color)
1402 int l = strlen(str);
1406 struct {unsigned char r,g,b;char*name;} colors[] =
1407 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1408 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1409 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1410 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1411 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1412 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1413 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1414 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1415 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1416 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1417 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1418 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1422 if(str[0]=='#' && (l==7 || l==9)) {
1423 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1425 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1427 color->r = r; color->g = g; color->b = b; color->a = a;
1430 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1431 if(!strcmp(str, colors[t].name)) {
1436 color->r = r; color->g = g; color->b = b; color->a = a;
1442 RGBA parseColor(char*str)
1445 if(!parseColor2(str, &c))
1446 syntaxerror("Expression '%s' is not a color", str);
1450 typedef struct _muladd {
1455 MULADD parseMulAdd(char*str)
1458 char* str2 = (char*)malloc(strlen(str)+5);
1465 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1466 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1467 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1468 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1469 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1470 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1471 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1472 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1473 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1474 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1476 syntaxerror("'%s' is not a valid color transform expression", str);
1478 m.add = (int)(add*256);
1479 m.mul = (int)(mul*256);
1484 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1486 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1487 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1489 if(a<-32768) a=-32768;
1490 if(a>32767) a=32767;
1491 if(m<-32768) m=-32768;
1492 if(m>32767) m=32767;
1498 float parsePercent(char*str)
1500 int l = strlen(str);
1504 return atoi(str)/100.0;
1506 syntaxerror("Expression '%s' is not a percentage", str);
1509 int isPercent(char*str)
1511 return str[strlen(str)-1]=='%';
1513 int parseNewSize(char*str, int size)
1516 return parsePercent(str)*size;
1518 return (int)(atof(str)*20);
1521 int isColor(char*str)
1524 return parseColor2(str, &c);
1527 static char* lu(map_t* args, char*name)
1529 char* value = map_lookup(args, name);
1531 map_dump(args, stdout, "");
1532 syntaxerror("internal error 2: value %s should be set", name);
1537 static int c_flash(map_t*args)
1539 char* name = lu(args, "name");
1540 char* compressstr = lu(args, "compress");
1541 SRECT bbox = parseBox(lu(args, "bbox"));
1542 int version = parseInt(lu(args, "version"));
1543 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1545 RGBA color = parseColor(lu(args, "background"));
1546 if(!strcmp(name, "!default!") || override_outputname)
1549 if(!strcmp(compressstr, "default"))
1550 compress = version==6;
1551 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1553 else if(!strcmp(compressstr, "no"))
1555 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1557 s_swf(name, bbox, version, fps, compress, color);
1560 int isRelative(char*str)
1562 return !strncmp(str, "<plus>", 6) ||
1563 !strncmp(str, "<minus>", 7);
1565 char* getOffset(char*str)
1567 if(!strncmp(str, "<plus>", 6))
1569 if(!strncmp(str, "<minus>", 7))
1571 syntaxerror("internal error (347)");
1574 int getSign(char*str)
1576 if(!strncmp(str, "<plus>", 6))
1578 if(!strncmp(str, "<minus>", 7))
1580 syntaxerror("internal error (348)");
1583 static dictionary_t points;
1584 static mem_t mpoints;
1585 int points_initialized = 0;
1587 SPOINT getPoint(SRECT r, char*name)
1590 if(!strcmp(name, "center")) {
1592 p.x = (r.xmin + r.xmax)/2;
1593 p.y = (r.ymin + r.ymax)/2;
1597 if(points_initialized)
1598 l = (int)dictionary_lookup(&points, name);
1600 syntaxerror("Invalid point: \"%s\".", name);
1603 return *(SPOINT*)&mpoints.buffer[l];
1605 static int c_gradient(map_t*args)
1607 char*name = lu(args, "name");
1608 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1612 syntaxerror("colon (:) expected");
1614 s_gradient(name, text, radial);
1617 static int c_point(map_t*args)
1619 char*name = lu(args, "name");
1623 if(!points_initialized) {
1624 dictionary_init(&points);
1626 points_initialized = 1;
1628 p.x = parseTwip(lu(args, "x"));
1629 p.y = parseTwip(lu(args, "y"));
1630 pos = mem_put(&mpoints, &p, sizeof(p));
1631 string_set(&s1, name);
1633 dictionary_put(&points, s1, (void*)pos);
1636 static int c_play(map_t*args)
1638 char*name = lu(args, "sound");
1639 char*loop = lu(args, "loop");
1640 char*nomultiple = lu(args, "nomultiple");
1642 if(!strcmp(nomultiple, "nomultiple"))
1645 nm = parseInt(nomultiple);
1647 s_playsound(name, parseInt(loop), nm, 0);
1651 static int c_stop(map_t*args)
1653 char*name = lu(args, "sound");
1654 s_playsound(name, 0,0,1);
1658 static int c_placement(map_t*args, int type)
1660 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1663 char* luminancestr = lu(args, "luminance");
1664 char* scalestr = lu(args, "scale");
1665 char* scalexstr = lu(args, "scalex");
1666 char* scaleystr = lu(args, "scaley");
1667 char* rotatestr = lu(args, "rotate");
1668 char* shearstr = lu(args, "shear");
1669 char* xstr="", *pivotstr="";
1670 char* ystr="", *anglestr="";
1671 char*above = lu(args, "above"); /*FIXME*/
1672 char*below = lu(args, "below");
1673 char* rstr = lu(args, "red");
1674 char* gstr = lu(args, "green");
1675 char* bstr = lu(args, "blue");
1676 char* astr = lu(args, "alpha");
1677 char* pinstr = lu(args, "pin");
1686 pivotstr = lu(args, "pivot");
1687 anglestr = lu(args, "angle");
1689 xstr = lu(args, "x");
1690 ystr = lu(args, "y");
1693 luminance = parseMulAdd(luminancestr);
1696 luminance.mul = 256;
1700 if(scalexstr[0]||scaleystr[0])
1701 syntaxerror("scalex/scaley and scale cannot both be set");
1702 scalexstr = scaleystr = scalestr;
1705 if(type == 0 || type == 4) {
1707 character = lu(args, "character");
1708 parameters_clear(&p);
1710 p = s_getParameters(instance);
1715 if(isRelative(xstr)) {
1716 if(type == 0 || type == 4)
1717 syntaxerror("relative x values not allowed for initial put or startclip");
1718 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1720 p.x = parseTwip(xstr);
1724 if(isRelative(ystr)) {
1725 if(type == 0 || type == 4)
1726 syntaxerror("relative y values not allowed for initial put or startclip");
1727 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1729 p.y = parseTwip(ystr);
1733 /* scale, scalex, scaley */
1735 oldbbox = s_getCharBBox(character);
1737 oldbbox = s_getInstanceBBox(instance);
1739 oldwidth = oldbbox.xmax - oldbbox.xmin;
1740 oldheight = oldbbox.ymax - oldbbox.ymin;
1742 if(oldwidth==0) p.scalex = 1.0;
1745 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1749 if(oldheight==0) p.scaley = 1.0;
1752 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1758 if(isRelative(rotatestr)) {
1759 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1761 p.rotate = parseFloat(rotatestr);
1767 if(isRelative(shearstr)) {
1768 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1770 p.shear = parseFloat(shearstr);
1775 if(isPoint(pivotstr))
1776 p.pivot = parsePoint(pivotstr);
1778 p.pivot = getPoint(oldbbox, pivotstr);
1782 p.pin = parsePoint(pinstr);
1784 p.pin = getPoint(oldbbox, pinstr);
1787 /* color transform */
1789 if(rstr[0] || luminancestr[0]) {
1792 r = parseMulAdd(rstr);
1794 r.add = p.cxform.r0;
1795 r.mul = p.cxform.r1;
1797 r = mergeMulAdd(r, luminance);
1798 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1800 if(gstr[0] || luminancestr[0]) {
1803 g = parseMulAdd(gstr);
1805 g.add = p.cxform.g0;
1806 g.mul = p.cxform.g1;
1808 g = mergeMulAdd(g, luminance);
1809 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1811 if(bstr[0] || luminancestr[0]) {
1814 b = parseMulAdd(bstr);
1816 b.add = p.cxform.b0;
1817 b.mul = p.cxform.b1;
1819 b = mergeMulAdd(b, luminance);
1820 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1823 MULADD a = parseMulAdd(astr);
1824 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1828 s_put(instance, character, p);
1830 s_change(instance, p);
1832 s_qchange(instance, p);
1834 s_jump(instance, p);
1836 s_startclip(instance, character, p);
1839 static int c_put(map_t*args)
1841 c_placement(args, 0);
1844 static int c_change(map_t*args)
1846 c_placement(args, 1);
1849 static int c_qchange(map_t*args)
1851 c_placement(args, 2);
1854 static int c_arcchange(map_t*args)
1856 c_placement(args, 2);
1859 static int c_jump(map_t*args)
1861 c_placement(args, 3);
1864 static int c_startclip(map_t*args)
1866 c_placement(args, 4);
1869 static int c_del(map_t*args)
1871 char*instance = lu(args, "name");
1872 s_delinstance(instance);
1875 static int c_end(map_t*args)
1880 static int c_sprite(map_t*args)
1882 char* name = lu(args, "name");
1886 static int c_frame(map_t*args)
1888 char*framestr = lu(args, "n");
1889 char*cutstr = lu(args, "cut");
1892 if(strcmp(cutstr, "no"))
1894 if(isRelative(framestr)) {
1895 frame = s_getframe();
1896 if(getSign(framestr)<0)
1897 syntaxerror("relative frame expressions must be positive");
1898 frame += parseInt(getOffset(framestr));
1901 frame = parseInt(framestr);
1902 if(s_getframe() >= frame
1903 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1904 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1906 s_frame(frame, cut);
1909 static int c_primitive(map_t*args)
1911 char*name = lu(args, "name");
1912 char*command = lu(args, "commandname");
1913 int width=0, height=0, r=0;
1914 int linewidth = parseTwip(lu(args, "line"));
1915 char*colorstr = lu(args, "color");
1916 RGBA color = parseColor(colorstr);
1917 char*fillstr = lu(args, "fill");
1924 if(!strcmp(command, "circle"))
1926 else if(!strcmp(command, "filled"))
1930 width = parseTwip(lu(args, "width"));
1931 height = parseTwip(lu(args, "height"));
1932 } else if (type==1) {
1933 r = parseTwip(lu(args, "r"));
1934 } else if (type==2) {
1935 outline = lu(args, "outline");
1938 if(!strcmp(fillstr, "fill"))
1940 if(!strcmp(fillstr, "none"))
1942 if(width<0 || height<0 || linewidth<0 || r<0)
1943 syntaxerror("values width, height, line, r must be positive");
1945 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1946 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1947 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
1951 static int c_textshape(map_t*args)
1953 char*name = lu(args, "name");
1954 char*text = lu(args, "text");
1955 char*font = lu(args, "font");
1957 s_textshape(name, font, text);
1963 static int c_swf(map_t*args)
1965 char*name = lu(args, "name");
1966 char*filename = lu(args, "filename");
1967 char*command = lu(args, "commandname");
1968 if(!strcmp(command, "shape"))
1969 warning("Please use .swf instead of .shape");
1970 s_includeswf(name, filename);
1974 static int c_font(map_t*args)
1976 char*name = lu(args, "name");
1977 char*filename = lu(args, "filename");
1978 s_font(name, filename);
1982 static int c_sound(map_t*args)
1984 char*name = lu(args, "name");
1985 char*filename = lu(args, "filename");
1986 s_sound(name, filename);
1990 static int c_text(map_t*args)
1992 char*name = lu(args, "name");
1993 char*text = lu(args, "text");
1994 char*font = lu(args, "font");
1995 float size = parsePercent(lu(args, "size"));
1996 RGBA color = parseColor(lu(args, "color"));
1997 s_text(name, font, text, (int)(size*100), color);
2001 static int c_soundtrack(map_t*args)
2006 static int c_image(map_t*args)
2008 char*command = lu(args, "commandname");
2009 char*name = lu(args, "name");
2010 char*filename = lu(args, "filename");
2011 if(!strcmp(command,"jpeg")) {
2012 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2013 s_image(name, "jpeg", filename, quality);
2015 s_image(name, "png", filename, 0);
2020 static int c_outline(map_t*args)
2022 char*name = lu(args, "name");
2023 char*format = lu(args, "format");
2027 syntaxerror("colon (:) expected");
2029 s_outline(name, format, text);
2033 int fakechar(map_t*args)
2035 char*name = lu(args, "name");
2036 s_box(name, 0, 0, black, 20, 0);
2040 static int c_egon(map_t*args) {return fakechar(args);}
2041 static int c_button(map_t*args) {return fakechar(args);}
2042 static int c_edittext(map_t*args) {return fakechar(args);}
2044 static int c_morphshape(map_t*args) {return fakechar(args);}
2045 static int c_movie(map_t*args) {return fakechar(args);}
2047 static int c_buttonsounds(map_t*args) {return 0;}
2048 static int c_buttonput(map_t*args) {return 0;}
2049 static int c_texture(map_t*args) {return 0;}
2050 static int c_action(map_t*args) {return 0;}
2054 command_func_t* func;
2057 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2058 {"frame", c_frame, "n=<plus>1 @cut=no"},
2059 // "import" type stuff
2060 {"swf", c_swf, "name filename"},
2061 {"shape", c_swf, "name filename"},
2062 {"jpeg", c_image, "name filename quality=80%"},
2063 {"png", c_image, "name filename"},
2064 {"movie", c_movie, "name filename"},
2065 {"sound", c_sound, "name filename"},
2066 {"font", c_font, "name filename"},
2067 {"soundtrack", c_soundtrack, "filename"},
2069 // generators of primitives
2071 {"point", c_point, "name x=0 y=0"},
2072 {"gradient", c_gradient, "name @radial=0"},
2073 {"outline", c_outline, "name format=simple"},
2074 {"textshape", c_textshape, "name text font"},
2076 // character generators
2077 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2078 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2079 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2081 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2082 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
2083 {"text", c_text, "name text font size=100% color=white"},
2084 {"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"},
2085 {"morphshape", c_morphshape, "name start end"},
2087 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
2090 {"play", c_play, "sound loop=0 @nomultiple=0"},
2091 {"stop", c_stop, "sound"},
2093 // object placement tags
2094 {"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="},
2095 {"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="},
2096 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2097 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2098 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2099 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2100 {"del", c_del, "name"},
2101 // virtual object placement
2102 {"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="},
2103 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2105 // commands which start a block
2106 //startclip (see above)
2107 {"sprite", c_sprite, "name"},
2108 {"action", c_action, ""},
2114 static map_t parseArguments(char*command, char*pattern)
2130 string_set(&t1, "commandname");
2131 string_set(&t2, command);
2132 map_put(&result, t1, t2);
2134 if(!pattern || !*pattern)
2141 if(!strncmp("<i> ", x, 3)) {
2143 if(type == COMMAND || type == RAWDATA) {
2145 syntaxerror("character name expected");
2147 name[pos].str = "instance";
2149 value[pos].str = text;
2150 value[pos].len = strlen(text);
2154 if(type == ASSIGNMENT)
2157 name[pos].str = "character";
2159 value[pos].str = text;
2160 value[pos].len = strlen(text);
2168 isboolean[pos] = (x[0] =='@');
2181 name[pos].len = d-x;
2186 name[pos].len = e-x;
2187 value[pos].str = e+1;
2188 value[pos].len = d-e-1;
2196 /* for(t=0;t<len;t++) {
2197 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2198 isboolean[t]?"(boolean)":"");
2203 if(type == RAWDATA || type == COMMAND) {
2208 // first, search for boolean arguments
2209 for(pos=0;pos<len;pos++)
2211 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2213 if(type == ASSIGNMENT)
2215 value[pos].str = text;
2216 value[pos].len = strlen(text);
2217 /*printf("setting boolean parameter %s (to %s)\n",
2218 strdup_n(name[pos], namelen[pos]),
2219 strdup_n(value[pos], valuelen[pos]));*/
2224 // second, search for normal arguments
2226 for(pos=0;pos<len;pos++)
2228 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2229 (type != ASSIGNMENT && !set[pos])) {
2231 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2233 if(type == ASSIGNMENT)
2236 value[pos].str = text;
2237 value[pos].len = strlen(text);
2239 printf("setting parameter %s (to %s)\n",
2240 strdup_n(name[pos].str, name[pos].len),
2241 strdup_n(value[pos].str, value[pos].len));
2247 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2251 for(t=0;t<len;t++) {
2252 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2255 for(t=0;t<len;t++) {
2256 if(value[t].str && value[t].str[0] == '*') {
2257 //relative default- take value from some other parameter
2259 for(s=0;s<len;s++) {
2260 if(value[s].len == value[t].len-1 &&
2261 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2262 value[t].str = value[s].str;
2265 if(value[t].str == 0) {
2267 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2271 /* ok, now construct the dictionary from the parameters */
2275 map_put(&result, name[t], value[t]);
2279 static void parseArgumentsForCommand(char*command)
2284 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2285 if(!strcmp(arguments[t].command, command)) {
2287 /* ugly hack- will be removed soon (once documentation and .sc generating
2288 utilities have been changed) */
2289 if(!strcmp(command, "swf") && !stackpos) {
2290 warning("Please use .flash instead of .swf- this will be mandatory soon");
2295 args = parseArguments(command, arguments[t].arguments);
2301 syntaxerror("command %s not known", command);
2304 printf(".%s\n", command);fflush(stdout);
2305 map_dump(&args, stdout, "\t");fflush(stdout);
2308 (*arguments[nr].func)(&args);
2310 if(!strcmp(command, "button") ||
2311 !strcmp(command, "action")) {
2314 if(type == COMMAND) {
2315 if(!strcmp(text, "end"))
2329 int main (int argc,char ** argv)
2332 processargs(argc, argv);
2333 initLog(0,-1,0,0,-1,verbose);
2336 args_callback_usage(argv[0]);
2339 file = generateTokens(filename);
2341 printf("parser returned error.\n");
2348 while(!noMoreTokens()) {
2351 syntaxerror("command expected");
2352 parseArgumentsForCommand(text);