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 font = swf_LoadFont(filename);
819 /*f = open(filename,O_RDONLY|O_BINARY);
821 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
822 font = (SWFFONT*)malloc(sizeof(SWFFONT));
823 memset(font, 0, sizeof(SWFFONT));
824 dictionary_put2(&fonts, name, font);
828 if (swf_ReadSWF(f,&swf)>=0) {
829 swf_FontExtract(&swf, 0x4e46, &font);
834 syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
839 /* fix the layout. Only needed for old fonts */
841 for(t=0;t<font->numchars;t++) {
842 font->glyph[t].advance = 0;
845 swf_FontCreateLayout(font);
849 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
850 swf_FontSetDefine2(tag, font);
853 if(dictionary_lookup(&fonts, name))
854 syntaxerror("font %s defined twice", name);
855 dictionary_put2(&fonts, name, font);
860 typedef struct _sound_t
866 void s_sound(char*name, char*filename)
868 struct WAV wav, wav2;
873 if(!readWAV(filename, &wav)) {
874 warning("Couldn't read wav file \"%s\"", filename);
878 convertWAV2mono(&wav, &wav2, 44100);
879 samples = (U16*)wav2.data;
880 numsamples = wav2.size/2;
884 tag = swf_InsertTag(tag, ST_DEFINESOUND);
885 swf_SetU16(tag, id); //id
886 swf_SetSoundDefine(tag, samples, numsamples);
888 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
892 if(dictionary_lookup(&sounds, name))
893 syntaxerror("sound %s defined twice", name);
894 dictionary_put2(&sounds, name, sound);
902 static char* gradient_getToken(const char**p)
906 while(**p && strchr(" \t\n\r", **p)) {
910 while(**p && !strchr(" \t\n\r", **p)) {
913 result = malloc((*p)-start+1);
914 memcpy(result,start,(*p)-start+1);
915 result[(*p)-start] = 0;
919 float parsePercent(char*str);
920 RGBA parseColor(char*str);
922 GRADIENT parseGradient(const char*str)
926 memset(&gradient, 0, sizeof(GRADIENT));
928 char*posstr,*colorstr;
929 posstr = gradient_getToken(&p);
932 float pos = parsePercent(posstr);
933 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
934 colorstr = gradient_getToken(&p);
935 RGBA color = parseColor(colorstr);
936 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
937 warning("gradient record too big- max size is 8, rest ignored");
940 gradient.ratios[gradient.num] = (int)(pos*255.0);
941 gradient.rgba[gradient.num] = color;
949 void s_gradient(char*name, const char*text, int radial)
951 gradient_t* gradient;
952 gradient = malloc(sizeof(gradient_t));
953 memset(gradient, 0, sizeof(gradient_t));
954 gradient->gradient = parseGradient(text);
955 gradient->radial = radial;
957 if(dictionary_lookup(&gradients, name))
958 syntaxerror("gradient %s defined twice", name);
959 dictionary_put2(&gradients, name, gradient);
962 void s_outline(char*name, char*format, char*source)
971 swf_Shape11DrawerInit(&draw, 0);
972 draw_string(&draw, source);
974 shape = swf_ShapeDrawerToShape(&draw);
975 //shape2 = swf_ShapeToShape2(shape);
976 //bounds = swf_GetShapeBoundingBox(shape2);
977 //swf_Shape2Free(shape2);
978 bounds = swf_ShapeDrawerGetBBox(&draw);
981 outline = (outline_t*)malloc(sizeof(outline_t));
982 memset(outline, 0, sizeof(outline_t));
983 outline->shape = shape;
984 outline->bbox = bounds;
986 if(dictionary_lookup(&outlines, name))
987 syntaxerror("outline %s defined twice", name);
988 dictionary_put2(&outlines, name, outline);
991 void s_playsound(char*name, int loops, int nomultiple, int stop)
993 sound_t* sound = dictionary_lookup(&sounds, name);
996 syntaxerror("Don't know anything about sound \"%s\"", name);
998 tag = swf_InsertTag(tag, ST_STARTSOUND);
999 swf_SetU16(tag, sound->id); //id
1000 memset(&info, 0, sizeof(info));
1003 info.nomultiple = nomultiple;
1004 swf_SetSoundInfo(tag, &info);
1007 void s_includeswf(char*name, char*filename)
1015 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1016 f = open(filename,O_RDONLY);
1018 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1019 s_box(name, 0, 0, black, 20, 0);
1022 if (swf_ReadSWF(f,&swf)<0) {
1023 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1024 s_box(name, 0, 0, black, 20, 0);
1029 /* FIXME: The following sets the bounding Box for the character.
1030 It is wrong for two reasons:
1031 a) It may be too small (in case objects in the movie clip at the borders)
1032 b) it may be too big (because the poor movie never got autocropped)
1036 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1037 swf_SetU16(tag, id);
1040 swf_Relocate(&swf, idmap);
1042 ftag = swf.firstTag;
1046 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1047 if(cutout[t] == ftag->id) {
1051 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1053 if(ftag->id == ST_END)
1057 /* We simply dump all tags right after the sprite
1058 header, relying on the fact that swf_OptimizeTagOrder() will
1059 sort things out for us later.
1060 We also rely on the fact that the imported SWF is well-formed.
1062 tag = swf_InsertTag(tag, ftag->id);
1063 swf_SetBlock(tag, ftag->data, ftag->len);
1067 syntaxerror("Included file %s contains errors", filename);
1068 tag = swf_InsertTag(tag, ST_END);
1072 s_addcharacter(name, id, tag, r);
1075 SRECT s_getCharBBox(char*name)
1077 character_t* c = dictionary_lookup(&characters, name);
1078 if(!c) syntaxerror("character '%s' unknown(2)", name);
1081 SRECT s_getInstanceBBox(char*name)
1083 instance_t * i = dictionary_lookup(&instances, name);
1085 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1087 if(!c) syntaxerror("internal error(5)");
1090 parameters_t s_getParameters(char*name)
1092 instance_t * i = dictionary_lookup(&instances, name);
1093 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1094 return i->parameters;
1096 void s_startclip(char*instance, char*character, parameters_t p)
1098 character_t* c = dictionary_lookup(&characters, character);
1102 syntaxerror("character %s not known", character);
1104 i = s_addinstance(instance, c, currentdepth);
1106 m = s_instancepos(i, &p);
1108 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1109 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1110 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1112 i->lastFrame= currentframe;
1114 stack[stackpos].tag = tag;
1115 stack[stackpos].type = 2;
1124 swf_SetTagPos(stack[stackpos].tag, 0);
1125 swf_GetPlaceObject(stack[stackpos].tag, &p);
1126 p.clipdepth = currentdepth;
1127 swf_ClearTag(stack[stackpos].tag);
1128 swf_SetPlaceObject(stack[stackpos].tag, &p);
1132 void s_put(char*instance, char*character, parameters_t p)
1134 character_t* c = dictionary_lookup(&characters, character);
1138 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1141 i = s_addinstance(instance, c, currentdepth);
1143 m = s_instancepos(i, &p);
1145 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1146 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1148 i->lastFrame = currentframe;
1152 void s_jump(char*instance, parameters_t p)
1154 instance_t* i = dictionary_lookup(&instances, instance);
1157 syntaxerror("instance %s not known", instance);
1161 m = s_instancepos(i, &p);
1163 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1164 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1166 i->lastFrame = currentframe;
1169 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1173 if(num==0 || num==1)
1175 ratio = (float)pos/(float)num;
1177 p.x = (p2->x-p1->x)*ratio + p1->x;
1178 p.y = (p2->y-p1->y)*ratio + p1->y;
1179 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1180 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1181 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1182 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1184 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1185 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1186 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1187 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1189 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1190 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1191 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1192 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1194 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1195 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1196 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1197 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1201 void s_change(char*instance, parameters_t p2)
1203 instance_t* i = dictionary_lookup(&instances, instance);
1207 int frame, allframes;
1209 syntaxerror("instance %s not known", instance);
1213 allframes = currentframe - i->lastFrame - 1;
1215 warning(".change ignored. can only .put/.change an object once per frame.");
1219 m = s_instancepos(i, &p2);
1220 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1221 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1224 /* o.k., we got the start and end point set. Now iterate though all the
1225 tags in between, inserting object changes after each new frame */
1228 if(!t) syntaxerror("internal error(6)");
1230 while(frame < allframes) {
1231 if(t->id == ST_SHOWFRAME) {
1236 p = s_interpolate(&p1, &p2, frame, allframes);
1237 m = s_instancepos(i, &p); //needed?
1238 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1239 i->lastFrame = currentframe;
1240 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1242 if(frame == allframes)
1247 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1251 void s_delinstance(char*instance)
1253 instance_t* i = dictionary_lookup(&instances, instance);
1255 syntaxerror("instance %s not known", instance);
1257 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1258 swf_SetU16(tag, i->depth);
1259 dictionary_del(&instances, instance);
1262 void s_qchange(char*instance, parameters_t p)
1269 syntaxerror(".end unexpected");
1270 if(stack[stackpos-1].type == 0)
1272 else if(stack[stackpos-1].type == 1)
1274 else if(stack[stackpos-1].type == 2)
1276 else syntaxerror("internal error 1");
1279 // ------------------------------------------------------------------------
1281 typedef int command_func_t(map_t*args);
1283 SRECT parseBox(char*str)
1286 float xmin, xmax, ymin, ymax;
1287 char*x = strchr(str, 'x');
1289 if(!strcmp(str, "autocrop")) {
1290 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1294 d1 = strchr(x+1, ':');
1296 d2 = strchr(d1+1, ':');
1298 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1302 else if(d1 && !d2) {
1303 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1309 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1314 r.xmin = (SCOORD)(xmin*20);
1315 r.ymin = (SCOORD)(ymin*20);
1316 r.xmax = (SCOORD)(xmax*20);
1317 r.ymax = (SCOORD)(ymax*20);
1320 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1323 float parseFloat(char*str)
1327 int parseInt(char*str)
1332 if(str[0]=='+' || str[0]=='-')
1336 if(str[t]<'0' || str[t]>'9')
1337 syntaxerror("Not an Integer: \"%s\"", str);
1340 int parseTwip(char*str)
1344 if(str[0]=='+' || str[0]=='-') {
1349 dot = strchr(str, '.');
1353 return sign*parseInt(str)*20;
1355 int l=strlen(++dot);
1357 for(s=str;s<dot-1;s++)
1358 if(*s<'0' || *s>'9')
1359 syntaxerror("Not a coordinate: \"%s\"", str);
1361 if(*s<'0' || *s>'9')
1362 syntaxerror("Not a coordinate: \"%s\"", str);
1364 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1365 warning("precision loss: %s converted to twip", str);
1370 return sign*atoi(str)*20;
1372 return sign*atoi(str)*20+atoi(dot)*2;
1374 return sign*atoi(str)*20+atoi(dot)/5;
1379 int isPoint(char*str)
1381 if(strchr(str, '('))
1387 SPOINT parsePoint(char*str)
1391 int l = strlen(str);
1392 char*comma = strchr(str, ',');
1393 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1394 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1395 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1396 p.x = parseTwip(tmp);
1397 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1398 p.y = parseTwip(tmp);
1402 int parseColor2(char*str, RGBA*color)
1404 int l = strlen(str);
1408 struct {unsigned char r,g,b;char*name;} colors[] =
1409 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1410 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1411 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1412 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1413 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1414 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1415 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1416 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1417 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1418 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1419 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1420 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1424 if(str[0]=='#' && (l==7 || l==9)) {
1425 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1427 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1429 color->r = r; color->g = g; color->b = b; color->a = a;
1432 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1433 if(!strcmp(str, colors[t].name)) {
1438 color->r = r; color->g = g; color->b = b; color->a = a;
1444 RGBA parseColor(char*str)
1447 if(!parseColor2(str, &c))
1448 syntaxerror("Expression '%s' is not a color", str);
1452 typedef struct _muladd {
1457 MULADD parseMulAdd(char*str)
1460 char* str2 = (char*)malloc(strlen(str)+5);
1467 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1468 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1469 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1470 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1471 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1472 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1473 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1474 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1475 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1476 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1478 syntaxerror("'%s' is not a valid color transform expression", str);
1480 m.add = (int)(add*256);
1481 m.mul = (int)(mul*256);
1486 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1488 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1489 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1491 if(a<-32768) a=-32768;
1492 if(a>32767) a=32767;
1493 if(m<-32768) m=-32768;
1494 if(m>32767) m=32767;
1500 float parsePercent(char*str)
1502 int l = strlen(str);
1506 return atoi(str)/100.0;
1508 syntaxerror("Expression '%s' is not a percentage", str);
1511 int isPercent(char*str)
1513 return str[strlen(str)-1]=='%';
1515 int parseNewSize(char*str, int size)
1518 return parsePercent(str)*size;
1520 return (int)(atof(str)*20);
1523 int isColor(char*str)
1526 return parseColor2(str, &c);
1529 static char* lu(map_t* args, char*name)
1531 char* value = map_lookup(args, name);
1533 map_dump(args, stdout, "");
1534 syntaxerror("internal error 2: value %s should be set", name);
1539 static int c_flash(map_t*args)
1541 char* name = lu(args, "name");
1542 char* compressstr = lu(args, "compress");
1543 SRECT bbox = parseBox(lu(args, "bbox"));
1544 int version = parseInt(lu(args, "version"));
1545 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1547 RGBA color = parseColor(lu(args, "background"));
1548 if(!strcmp(name, "!default!") || override_outputname)
1551 if(!strcmp(compressstr, "default"))
1552 compress = version==6;
1553 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1555 else if(!strcmp(compressstr, "no"))
1557 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1559 s_swf(name, bbox, version, fps, compress, color);
1562 int isRelative(char*str)
1564 return !strncmp(str, "<plus>", 6) ||
1565 !strncmp(str, "<minus>", 7);
1567 char* getOffset(char*str)
1569 if(!strncmp(str, "<plus>", 6))
1571 if(!strncmp(str, "<minus>", 7))
1573 syntaxerror("internal error (347)");
1576 int getSign(char*str)
1578 if(!strncmp(str, "<plus>", 6))
1580 if(!strncmp(str, "<minus>", 7))
1582 syntaxerror("internal error (348)");
1585 static dictionary_t points;
1586 static mem_t mpoints;
1587 int points_initialized = 0;
1589 SPOINT getPoint(SRECT r, char*name)
1592 if(!strcmp(name, "center")) {
1594 p.x = (r.xmin + r.xmax)/2;
1595 p.y = (r.ymin + r.ymax)/2;
1599 if(points_initialized)
1600 l = (int)dictionary_lookup(&points, name);
1602 syntaxerror("Invalid point: \"%s\".", name);
1605 return *(SPOINT*)&mpoints.buffer[l];
1607 static int c_gradient(map_t*args)
1609 char*name = lu(args, "name");
1610 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1614 syntaxerror("colon (:) expected");
1616 s_gradient(name, text, radial);
1619 static int c_point(map_t*args)
1621 char*name = lu(args, "name");
1625 if(!points_initialized) {
1626 dictionary_init(&points);
1628 points_initialized = 1;
1630 p.x = parseTwip(lu(args, "x"));
1631 p.y = parseTwip(lu(args, "y"));
1632 pos = mem_put(&mpoints, &p, sizeof(p));
1633 string_set(&s1, name);
1635 dictionary_put(&points, s1, (void*)pos);
1638 static int c_play(map_t*args)
1640 char*name = lu(args, "sound");
1641 char*loop = lu(args, "loop");
1642 char*nomultiple = lu(args, "nomultiple");
1644 if(!strcmp(nomultiple, "nomultiple"))
1647 nm = parseInt(nomultiple);
1649 s_playsound(name, parseInt(loop), nm, 0);
1653 static int c_stop(map_t*args)
1655 char*name = lu(args, "sound");
1656 s_playsound(name, 0,0,1);
1660 static int c_placement(map_t*args, int type)
1662 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1665 char* luminancestr = lu(args, "luminance");
1666 char* scalestr = lu(args, "scale");
1667 char* scalexstr = lu(args, "scalex");
1668 char* scaleystr = lu(args, "scaley");
1669 char* rotatestr = lu(args, "rotate");
1670 char* shearstr = lu(args, "shear");
1671 char* xstr="", *pivotstr="";
1672 char* ystr="", *anglestr="";
1673 char*above = lu(args, "above"); /*FIXME*/
1674 char*below = lu(args, "below");
1675 char* rstr = lu(args, "red");
1676 char* gstr = lu(args, "green");
1677 char* bstr = lu(args, "blue");
1678 char* astr = lu(args, "alpha");
1679 char* pinstr = lu(args, "pin");
1688 pivotstr = lu(args, "pivot");
1689 anglestr = lu(args, "angle");
1691 xstr = lu(args, "x");
1692 ystr = lu(args, "y");
1695 luminance = parseMulAdd(luminancestr);
1698 luminance.mul = 256;
1702 if(scalexstr[0]||scaleystr[0])
1703 syntaxerror("scalex/scaley and scale cannot both be set");
1704 scalexstr = scaleystr = scalestr;
1707 if(type == 0 || type == 4) {
1709 character = lu(args, "character");
1710 parameters_clear(&p);
1712 p = s_getParameters(instance);
1717 if(isRelative(xstr)) {
1718 if(type == 0 || type == 4)
1719 syntaxerror("relative x values not allowed for initial put or startclip");
1720 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1722 p.x = parseTwip(xstr);
1726 if(isRelative(ystr)) {
1727 if(type == 0 || type == 4)
1728 syntaxerror("relative y values not allowed for initial put or startclip");
1729 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1731 p.y = parseTwip(ystr);
1735 /* scale, scalex, scaley */
1737 oldbbox = s_getCharBBox(character);
1739 oldbbox = s_getInstanceBBox(instance);
1741 oldwidth = oldbbox.xmax - oldbbox.xmin;
1742 oldheight = oldbbox.ymax - oldbbox.ymin;
1744 if(oldwidth==0) p.scalex = 1.0;
1747 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1751 if(oldheight==0) p.scaley = 1.0;
1754 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1760 if(isRelative(rotatestr)) {
1761 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1763 p.rotate = parseFloat(rotatestr);
1769 if(isRelative(shearstr)) {
1770 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1772 p.shear = parseFloat(shearstr);
1777 if(isPoint(pivotstr))
1778 p.pivot = parsePoint(pivotstr);
1780 p.pivot = getPoint(oldbbox, pivotstr);
1784 p.pin = parsePoint(pinstr);
1786 p.pin = getPoint(oldbbox, pinstr);
1789 /* color transform */
1791 if(rstr[0] || luminancestr[0]) {
1794 r = parseMulAdd(rstr);
1796 r.add = p.cxform.r0;
1797 r.mul = p.cxform.r1;
1799 r = mergeMulAdd(r, luminance);
1800 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1802 if(gstr[0] || luminancestr[0]) {
1805 g = parseMulAdd(gstr);
1807 g.add = p.cxform.g0;
1808 g.mul = p.cxform.g1;
1810 g = mergeMulAdd(g, luminance);
1811 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1813 if(bstr[0] || luminancestr[0]) {
1816 b = parseMulAdd(bstr);
1818 b.add = p.cxform.b0;
1819 b.mul = p.cxform.b1;
1821 b = mergeMulAdd(b, luminance);
1822 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1825 MULADD a = parseMulAdd(astr);
1826 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1830 s_put(instance, character, p);
1832 s_change(instance, p);
1834 s_qchange(instance, p);
1836 s_jump(instance, p);
1838 s_startclip(instance, character, p);
1841 static int c_put(map_t*args)
1843 c_placement(args, 0);
1846 static int c_change(map_t*args)
1848 c_placement(args, 1);
1851 static int c_qchange(map_t*args)
1853 c_placement(args, 2);
1856 static int c_arcchange(map_t*args)
1858 c_placement(args, 2);
1861 static int c_jump(map_t*args)
1863 c_placement(args, 3);
1866 static int c_startclip(map_t*args)
1868 c_placement(args, 4);
1871 static int c_del(map_t*args)
1873 char*instance = lu(args, "name");
1874 s_delinstance(instance);
1877 static int c_end(map_t*args)
1882 static int c_sprite(map_t*args)
1884 char* name = lu(args, "name");
1888 static int c_frame(map_t*args)
1890 char*framestr = lu(args, "n");
1891 char*cutstr = lu(args, "cut");
1894 if(strcmp(cutstr, "no"))
1896 if(isRelative(framestr)) {
1897 frame = s_getframe();
1898 if(getSign(framestr)<0)
1899 syntaxerror("relative frame expressions must be positive");
1900 frame += parseInt(getOffset(framestr));
1903 frame = parseInt(framestr);
1904 if(s_getframe() >= frame
1905 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1906 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1908 s_frame(frame, cut);
1911 static int c_primitive(map_t*args)
1913 char*name = lu(args, "name");
1914 char*command = lu(args, "commandname");
1915 int width=0, height=0, r=0;
1916 int linewidth = parseTwip(lu(args, "line"));
1917 char*colorstr = lu(args, "color");
1918 RGBA color = parseColor(colorstr);
1919 char*fillstr = lu(args, "fill");
1926 if(!strcmp(command, "circle"))
1928 else if(!strcmp(command, "filled"))
1932 width = parseTwip(lu(args, "width"));
1933 height = parseTwip(lu(args, "height"));
1934 } else if (type==1) {
1935 r = parseTwip(lu(args, "r"));
1936 } else if (type==2) {
1937 outline = lu(args, "outline");
1940 if(!strcmp(fillstr, "fill"))
1942 if(!strcmp(fillstr, "none"))
1944 if(width<0 || height<0 || linewidth<0 || r<0)
1945 syntaxerror("values width, height, line, r must be positive");
1947 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1948 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1949 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
1953 static int c_textshape(map_t*args)
1955 char*name = lu(args, "name");
1956 char*text = lu(args, "text");
1957 char*font = lu(args, "font");
1959 s_textshape(name, font, text);
1965 static int c_swf(map_t*args)
1967 char*name = lu(args, "name");
1968 char*filename = lu(args, "filename");
1969 char*command = lu(args, "commandname");
1970 if(!strcmp(command, "shape"))
1971 warning("Please use .swf instead of .shape");
1972 s_includeswf(name, filename);
1976 static int c_font(map_t*args)
1978 char*name = lu(args, "name");
1979 char*filename = lu(args, "filename");
1980 s_font(name, filename);
1984 static int c_sound(map_t*args)
1986 char*name = lu(args, "name");
1987 char*filename = lu(args, "filename");
1988 s_sound(name, filename);
1992 static int c_text(map_t*args)
1994 char*name = lu(args, "name");
1995 char*text = lu(args, "text");
1996 char*font = lu(args, "font");
1997 float size = parsePercent(lu(args, "size"));
1998 RGBA color = parseColor(lu(args, "color"));
1999 s_text(name, font, text, (int)(size*100), color);
2003 static int c_soundtrack(map_t*args)
2008 static int c_image(map_t*args)
2010 char*command = lu(args, "commandname");
2011 char*name = lu(args, "name");
2012 char*filename = lu(args, "filename");
2013 if(!strcmp(command,"jpeg")) {
2014 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2015 s_image(name, "jpeg", filename, quality);
2017 s_image(name, "png", filename, 0);
2022 static int c_outline(map_t*args)
2024 char*name = lu(args, "name");
2025 char*format = lu(args, "format");
2029 syntaxerror("colon (:) expected");
2031 s_outline(name, format, text);
2035 int fakechar(map_t*args)
2037 char*name = lu(args, "name");
2038 s_box(name, 0, 0, black, 20, 0);
2042 static int c_egon(map_t*args) {return fakechar(args);}
2043 static int c_button(map_t*args) {return fakechar(args);}
2044 static int c_edittext(map_t*args) {return fakechar(args);}
2046 static int c_morphshape(map_t*args) {return fakechar(args);}
2047 static int c_movie(map_t*args) {return fakechar(args);}
2049 static int c_buttonsounds(map_t*args) {return 0;}
2050 static int c_buttonput(map_t*args) {return 0;}
2051 static int c_texture(map_t*args) {return 0;}
2052 static int c_action(map_t*args) {return 0;}
2056 command_func_t* func;
2059 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2060 {"frame", c_frame, "n=<plus>1 @cut=no"},
2061 // "import" type stuff
2062 {"swf", c_swf, "name filename"},
2063 {"shape", c_swf, "name filename"},
2064 {"jpeg", c_image, "name filename quality=80%"},
2065 {"png", c_image, "name filename"},
2066 {"movie", c_movie, "name filename"},
2067 {"sound", c_sound, "name filename"},
2068 {"font", c_font, "name filename"},
2069 {"soundtrack", c_soundtrack, "filename"},
2071 // generators of primitives
2073 {"point", c_point, "name x=0 y=0"},
2074 {"gradient", c_gradient, "name @radial=0"},
2075 {"outline", c_outline, "name format=simple"},
2076 {"textshape", c_textshape, "name text font"},
2078 // character generators
2079 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2080 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2081 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2083 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2084 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
2085 {"text", c_text, "name text font size=100% color=white"},
2086 {"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"},
2087 {"morphshape", c_morphshape, "name start end"},
2089 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
2092 {"play", c_play, "sound loop=0 @nomultiple=0"},
2093 {"stop", c_stop, "sound"},
2095 // object placement tags
2096 {"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="},
2097 {"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="},
2098 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2099 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2100 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2101 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2102 {"del", c_del, "name"},
2103 // virtual object placement
2104 {"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="},
2105 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2107 // commands which start a block
2108 //startclip (see above)
2109 {"sprite", c_sprite, "name"},
2110 {"action", c_action, ""},
2116 static map_t parseArguments(char*command, char*pattern)
2132 string_set(&t1, "commandname");
2133 string_set(&t2, command);
2134 map_put(&result, t1, t2);
2136 if(!pattern || !*pattern)
2143 if(!strncmp("<i> ", x, 3)) {
2145 if(type == COMMAND || type == RAWDATA) {
2147 syntaxerror("character name expected");
2149 name[pos].str = "instance";
2151 value[pos].str = text;
2152 value[pos].len = strlen(text);
2156 if(type == ASSIGNMENT)
2159 name[pos].str = "character";
2161 value[pos].str = text;
2162 value[pos].len = strlen(text);
2170 isboolean[pos] = (x[0] =='@');
2183 name[pos].len = d-x;
2188 name[pos].len = e-x;
2189 value[pos].str = e+1;
2190 value[pos].len = d-e-1;
2198 /* for(t=0;t<len;t++) {
2199 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2200 isboolean[t]?"(boolean)":"");
2205 if(type == RAWDATA || type == COMMAND) {
2210 // first, search for boolean arguments
2211 for(pos=0;pos<len;pos++)
2213 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2215 if(type == ASSIGNMENT)
2217 value[pos].str = text;
2218 value[pos].len = strlen(text);
2219 /*printf("setting boolean parameter %s (to %s)\n",
2220 strdup_n(name[pos], namelen[pos]),
2221 strdup_n(value[pos], valuelen[pos]));*/
2226 // second, search for normal arguments
2228 for(pos=0;pos<len;pos++)
2230 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2231 (type != ASSIGNMENT && !set[pos])) {
2233 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2235 if(type == ASSIGNMENT)
2238 value[pos].str = text;
2239 value[pos].len = strlen(text);
2241 printf("setting parameter %s (to %s)\n",
2242 strdup_n(name[pos].str, name[pos].len),
2243 strdup_n(value[pos].str, value[pos].len));
2249 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2253 for(t=0;t<len;t++) {
2254 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2257 for(t=0;t<len;t++) {
2258 if(value[t].str && value[t].str[0] == '*') {
2259 //relative default- take value from some other parameter
2261 for(s=0;s<len;s++) {
2262 if(value[s].len == value[t].len-1 &&
2263 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2264 value[t].str = value[s].str;
2267 if(value[t].str == 0) {
2269 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2273 /* ok, now construct the dictionary from the parameters */
2277 map_put(&result, name[t], value[t]);
2281 static void parseArgumentsForCommand(char*command)
2286 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2287 if(!strcmp(arguments[t].command, command)) {
2289 /* ugly hack- will be removed soon (once documentation and .sc generating
2290 utilities have been changed) */
2291 if(!strcmp(command, "swf") && !stackpos) {
2292 warning("Please use .flash instead of .swf- this will be mandatory soon");
2297 args = parseArguments(command, arguments[t].arguments);
2303 syntaxerror("command %s not known", command);
2306 printf(".%s\n", command);fflush(stdout);
2307 map_dump(&args, stdout, "\t");fflush(stdout);
2310 (*arguments[nr].func)(&args);
2312 if(!strcmp(command, "button") ||
2313 !strcmp(command, "action")) {
2316 if(type == COMMAND) {
2317 if(!strcmp(text, "end"))
2331 int main (int argc,char ** argv)
2334 processargs(argc, argv);
2335 initLog(0,-1,0,0,-1,verbose);
2338 args_callback_usage(argv[0]);
2341 file = generateTokens(filename);
2343 printf("parser returned error.\n");
2350 while(!noMoreTokens()) {
2353 syntaxerror("command expected");
2354 parseArgumentsForCommand(text);