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)
81 printf("Usage: %s [-o file.swf] file.sc\n", name);
83 printf("-h , --help Print short help message and exit\n");
84 printf("-V , --version Print version info and exit\n");
85 printf("-v , --verbose Increase verbosity. \n");
86 printf("-o , --output <filename> Set output file to <filename>.\n");
89 int args_callback_command(char*name,char*val)
92 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
99 static struct token_t* file;
108 static void syntaxerror(char*format, ...)
112 va_start(arglist, format);
113 vsprintf(buf, format, arglist);
115 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
119 static void warning(char*format, ...)
123 va_start(arglist, format);
124 vsprintf(buf, format, arglist);
126 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
129 static void readToken()
131 type = file[pos].type;
133 syntaxerror("unexpected end of file");
135 text = file[pos].text;
136 textlen = strlen(text);
137 line = file[pos].line;
138 column = file[pos].column;
140 //printf("---> %d(%s) %s\n", type, type_names[type], text);
143 static void pushBack()
146 if(!pos) syntaxerror("internal error 3");
151 textlen = strlen(text);
154 column = file[p].column;
157 static int noMoreTokens()
159 if(file[pos].type == END)
164 // ------------------------------ swf routines ----------------------------
168 int type; //0=swf, 1=sprite, 2=clip
174 /* for sprites (1): */
180 dictionary_t oldinstances;
185 static int stackpos = 0;
187 static dictionary_t characters;
188 static dictionary_t images;
189 static dictionary_t outlines;
190 static dictionary_t gradients;
191 static char idmap[65536];
192 static TAG*tag = 0; //current tag
194 static int id; //current character id
195 static int currentframe; //current frame in current level
196 static SRECT currentrect; //current bounding box in current level
197 static U16 currentdepth;
198 static dictionary_t instances;
199 static dictionary_t fonts;
200 static dictionary_t sounds;
202 typedef struct _parameters {
204 float scalex, scaley;
212 typedef struct _character {
218 typedef struct _instance {
219 character_t*character;
221 parameters_t parameters;
222 TAG* lastTag; //last tag which set the object
223 U16 lastFrame; //frame lastTag is in
226 typedef struct _outline {
231 typedef struct _gradient {
236 static void character_init(character_t*c)
238 memset(c, 0, sizeof(character_t));
240 static character_t* character_new()
243 c = (character_t*)malloc(sizeof(character_t));
247 static void instance_init(instance_t*i)
249 memset(i, 0, sizeof(instance_t));
251 static instance_t* instance_new()
254 c = (instance_t*)malloc(sizeof(instance_t));
259 static void incrementid()
263 syntaxerror("Out of character ids.");
268 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
270 character_t* c = character_new();
272 c->definingTag = ctag;
275 if(dictionary_lookup(&characters, name))
276 syntaxerror("character %s defined twice", name);
277 dictionary_put2(&characters, name, c);
279 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
281 swf_SetString(tag, name);
282 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
285 swf_SetString(tag, name);
287 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
289 character_t* c = character_new();
290 c->definingTag = ctag;
294 if(dictionary_lookup(&images, name))
295 syntaxerror("image %s defined twice", name);
296 dictionary_put2(&images, name, c);
298 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
300 instance_t* i = instance_new();
303 //swf_GetMatrix(0, &i->matrix);
304 if(dictionary_lookup(&instances, name))
305 syntaxerror("object %s defined twice", name);
306 dictionary_put2(&instances, name, i);
310 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)
313 p->scalex = scalex; p->scaley = scaley;
314 p->pin = pin; p->pivot = pivot;
315 p->rotate = rotate; p->cxform = cxform;
319 static void parameters_clear(parameters_t*p)
322 p->scalex = 1.0; p->scaley = 1.0;
323 p->pin.x = 1; p->pin.y = 0;
324 p->pivot.x = 0; p->pivot.y = 0;
327 swf_GetCXForm(0, &p->cxform, 1);
330 static void makeMatrix(MATRIX*m, parameters_t*p)
339 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
340 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
341 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
342 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
344 m->sx = (int)(sx*65536+0.5);
345 m->r1 = (int)(r1*65536+0.5);
346 m->r0 = (int)(r0*65536+0.5);
347 m->sy = (int)(sy*65536+0.5);
351 h = swf_TurnPoint(p->pin, m);
356 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
361 r = swf_TurnRect(i->character->size, &m);
362 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
363 currentrect.xmax == 0 && currentrect.ymax == 0)
366 swf_ExpandRect2(¤trect, &r);
370 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
372 SWF*swf = (SWF*)malloc(sizeof(SWF));
375 syntaxerror(".swf blocks can't be nested");
377 memset(swf, 0, sizeof(swf));
378 swf->fileVersion = version;
380 swf->frameRate = fps;
381 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
382 swf->compressed = compress;
383 swf_SetRGB(tag,&background);
385 if(stackpos==sizeof(stack)/sizeof(stack[0]))
386 syntaxerror("too many levels of recursion");
388 dictionary_init(&characters);
389 dictionary_init(&images);
390 dictionary_init(&outlines);
391 dictionary_init(&gradients);
392 dictionary_init(&instances);
393 dictionary_init(&fonts);
394 dictionary_init(&sounds);
396 memset(&stack[stackpos], 0, sizeof(stack[0]));
397 stack[stackpos].type = 0;
398 stack[stackpos].filename = strdup(name);
399 stack[stackpos].swf = swf;
400 stack[stackpos].oldframe = -1;
405 memset(¤trect, 0, sizeof(currentrect));
408 memset(idmap, 0, sizeof(idmap));
412 void s_sprite(char*name)
414 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
415 swf_SetU16(tag, id); //id
416 swf_SetU16(tag, 0); //frames
418 memset(&stack[stackpos], 0, sizeof(stack[0]));
419 stack[stackpos].type = 1;
420 stack[stackpos].oldframe = currentframe;
421 stack[stackpos].olddepth = currentdepth;
422 stack[stackpos].oldrect = currentrect;
423 stack[stackpos].oldinstances = instances;
424 stack[stackpos].tag = tag;
425 stack[stackpos].id = id;
426 stack[stackpos].name = strdup(name);
428 /* FIXME: those four fields should be bundled together */
429 dictionary_init(&instances);
432 memset(¤trect, 0, sizeof(currentrect));
438 TAG* removeFromTo(TAG*from, TAG*to)
440 TAG*save = from->prev;
442 TAG*next = from->next;
450 static void s_endSprite()
452 SRECT r = currentrect;
454 if(stack[stackpos].cut)
455 tag = removeFromTo(stack[stackpos].cut, tag);
459 /* TODO: before clearing, prepend "<spritename>." to names and
460 copy into old instances dict */
461 dictionary_clear(&instances);
463 currentframe = stack[stackpos].oldframe;
464 currentrect = stack[stackpos].oldrect;
465 currentdepth = stack[stackpos].olddepth;
466 instances = stack[stackpos].oldinstances;
468 tag = swf_InsertTag(tag, ST_END);
470 tag = stack[stackpos].tag;
473 syntaxerror("internal error(7)");
475 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
476 free(stack[stackpos].name);
479 static void s_endSWF()
485 if(stack[stackpos].cut)
486 tag = removeFromTo(stack[stackpos].cut, tag);
490 swf = stack[stackpos].swf;
491 filename = stack[stackpos].filename;
493 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
495 tag = swf_InsertTag(tag, ST_END);
497 swf_OptimizeTagOrder(swf);
499 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
500 swf->movieSize = currentrect; /* "autocrop" */
502 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
503 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
504 swf->movieSize.ymax += 20;
507 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
509 syntaxerror("couldn't create output file %s", filename);
512 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
514 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
518 dictionary_clear(&instances);
519 dictionary_clear(&characters);
520 dictionary_clear(&images);
521 dictionary_clear(&outlines);
522 dictionary_clear(&gradients);
523 dictionary_clear(&fonts);
524 dictionary_clear(&sounds);
534 if(stack[stackpos-1].type == 0)
535 syntaxerror("End of file encountered in .flash block");
536 if(stack[stackpos-1].type == 1)
537 syntaxerror("End of file encountered in .sprite block");
538 if(stack[stackpos-1].type == 2)
539 syntaxerror("End of file encountered in .clip block");
548 void s_frame(int nr, int cut)
553 for(t=currentframe;t<nr;t++) {
554 tag = swf_InsertTag(tag, ST_SHOWFRAME);
559 syntaxerror("Can't cut, frame empty");
561 stack[stackpos].cut = tag;
567 int parseColor2(char*str, RGBA*color);
569 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
574 if(texture[0] == '#') {
575 parseColor2(texture, &color);
576 return swf_ShapeAddSolidFillStyle(s, &color);
577 } else if((image = dictionary_lookup(&images, texture))) {
579 swf_GetMatrix(0, &m);
580 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
581 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
584 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
585 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
586 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
588 swf_GetMatrix(0, &m);
589 m.sx = (r->xmax - r->xmin)*2;
590 m.sy = (r->ymax - r->ymin)*2;
591 m.tx = r->xmin + (r->xmax - r->xmin)/2;
592 m.ty = r->ymin + (r->ymax - r->ymin)/2;
593 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
594 } else if (parseColor2(texture, &color)) {
595 return swf_ShapeAddSolidFillStyle(s, &color);
597 syntaxerror("not a color/fillstyle: %s", texture);
602 RGBA black={r:0,g:0,b:0,a:0};
603 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
612 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
614 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
617 fs1 = addFillStyle(s, &r2, texture);
620 r.xmin = r2.xmin-linewidth-linewidth/2;
621 r.ymin = r2.ymin-linewidth-linewidth/2;
622 r.xmax = r2.xmax+linewidth+linewidth/2;
623 r.ymax = r2.ymax+linewidth+linewidth/2;
625 swf_SetShapeHeader(tag,s);
626 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
627 swf_ShapeSetLine(tag,s,width,0);
628 swf_ShapeSetLine(tag,s,0,height);
629 swf_ShapeSetLine(tag,s,-width,0);
630 swf_ShapeSetLine(tag,s,0,-height);
631 swf_ShapeSetEnd(tag);
634 s_addcharacter(name, id, tag, r);
638 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
644 outline = dictionary_lookup(&outlines, outlinename);
646 syntaxerror("outline %s not defined", outlinename);
650 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
652 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
654 fs1 = addFillStyle(s, &r2, texture);
656 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
658 rect.xmin = r2.xmin-linewidth-linewidth/2;
659 rect.ymin = r2.ymin-linewidth-linewidth/2;
660 rect.xmax = r2.xmax+linewidth+linewidth/2;
661 rect.ymax = r2.ymax+linewidth+linewidth/2;
663 swf_SetRect(tag,&rect);
664 swf_SetShapeStyles(tag, s);
665 swf_SetShapeBits(tag, outline->shape); //does not count bits!
666 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
669 s_addcharacter(name, id, tag, rect);
673 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
678 r2.xmin = r2.ymin = 0;
682 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
684 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
686 fs1 = addFillStyle(s, &r2, texture);
688 rect.xmin = r2.xmin-linewidth-linewidth/2;
689 rect.ymin = r2.ymin-linewidth-linewidth/2;
690 rect.xmax = r2.xmax+linewidth+linewidth/2;
691 rect.ymax = r2.ymax+linewidth+linewidth/2;
693 swf_SetRect(tag,&rect);
694 swf_SetShapeHeader(tag,s);
695 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
696 swf_ShapeSetCircle(tag, s, r,r,r,r);
697 swf_ShapeSetEnd(tag);
700 s_addcharacter(name, id, tag, rect);
704 void s_textshape(char*name, char*fontname, char*_text)
707 U8*text = (U8*)_text;
711 font = dictionary_lookup(&fonts, fontname);
713 syntaxerror("font \"%s\" not known!", fontname);
715 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
716 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
717 s_box(name, 0, 0, black, 20, 0);
720 g = font->ascii2glyph[text[0]];
722 outline = malloc(sizeof(outline_t));
723 memset(outline, 0, sizeof(outline_t));
724 outline->shape = font->glyph[g].shape;
725 outline->bbox = font->layout->bounds[g];
729 swf_Shape11DrawerInit(&draw, 0);
730 swf_DrawText(&draw, font, _text);
732 outline->shape = swf_ShapeDrawerToShape(&draw);
733 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
737 if(dictionary_lookup(&outlines, name))
738 syntaxerror("outline %s defined twice", name);
739 dictionary_put2(&outlines, name, outline);
742 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
747 font = dictionary_lookup(&fonts, fontname);
749 syntaxerror("font \"%s\" not known!", fontname);
751 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
753 if(!font->numchars) {
754 s_box(name, 0, 0, black, 20, 0);
757 r = swf_SetDefineText(tag, font, &color, text, size);
759 s_addcharacter(name, id, tag, r);
763 /* type: either "jpeg" or "png"
765 void s_image(char*name, char*type, char*filename, int quality)
767 /* an image is actually two folded: 1st bitmap, 2nd character.
768 Both of them can be used separately */
770 /* step 1: the bitmap */
775 warning("image type \"png\" not supported yet!");
776 s_box(name, 0, 0, black, 20, 0);
779 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
780 swf_SetU16(tag, imageID);
782 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
783 syntaxerror("Image \"%s\" not found, or contains errors", filename);
786 swf_GetJPEGSize(filename, &width, &height);
793 s_addimage(name, id, tag, r);
796 /* step 2: the character */
797 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
799 swf_ShapeSetBitmapRect(tag, imageID, width, height);
801 s_addcharacter(name, id, tag, r);
805 void dumpSWF(SWF*swf)
807 TAG* tag = swf->firstTag;
808 printf("vvvvvvvvvvvvvvvvvvvvv\n");
810 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
813 printf("^^^^^^^^^^^^^^^^^^^^^\n");
816 void s_font(char*name, char*filename)
819 font = swf_LoadFont(filename);
822 warning("Couldn't open font file \"%s\"", filename);
823 font = (SWFFONT*)malloc(sizeof(SWFFONT));
824 memset(font, 0, sizeof(SWFFONT));
825 dictionary_put2(&fonts, name, font);
831 /* fix the layout. Only needed for old fonts */
833 for(t=0;t<font->numchars;t++) {
834 font->glyph[t].advance = 0;
837 swf_FontCreateLayout(font);
841 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
842 swf_FontSetDefine2(tag, font);
845 if(dictionary_lookup(&fonts, name))
846 syntaxerror("font %s defined twice", name);
847 dictionary_put2(&fonts, name, font);
852 typedef struct _sound_t
858 void s_sound(char*name, char*filename)
860 struct WAV wav, wav2;
865 if(!readWAV(filename, &wav)) {
866 warning("Couldn't read wav file \"%s\"", filename);
870 convertWAV2mono(&wav, &wav2, 44100);
871 samples = (U16*)wav2.data;
872 numsamples = wav2.size/2;
876 tag = swf_InsertTag(tag, ST_DEFINESOUND);
877 swf_SetU16(tag, id); //id
878 swf_SetSoundDefine(tag, samples, numsamples);
880 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
884 if(dictionary_lookup(&sounds, name))
885 syntaxerror("sound %s defined twice", name);
886 dictionary_put2(&sounds, name, sound);
894 static char* gradient_getToken(const char**p)
898 while(**p && strchr(" \t\n\r", **p)) {
902 while(**p && !strchr(" \t\n\r", **p)) {
905 result = malloc((*p)-start+1);
906 memcpy(result,start,(*p)-start+1);
907 result[(*p)-start] = 0;
911 float parsePercent(char*str);
912 RGBA parseColor(char*str);
914 GRADIENT parseGradient(const char*str)
918 memset(&gradient, 0, sizeof(GRADIENT));
920 char*posstr,*colorstr;
923 posstr = gradient_getToken(&p);
926 pos = parsePercent(posstr);
927 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
928 colorstr = gradient_getToken(&p);
929 color = parseColor(colorstr);
930 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
931 warning("gradient record too big- max size is 8, rest ignored");
934 gradient.ratios[gradient.num] = (int)(pos*255.0);
935 gradient.rgba[gradient.num] = color;
943 void s_gradient(char*name, const char*text, int radial)
945 gradient_t* gradient;
946 gradient = malloc(sizeof(gradient_t));
947 memset(gradient, 0, sizeof(gradient_t));
948 gradient->gradient = parseGradient(text);
949 gradient->radial = radial;
951 if(dictionary_lookup(&gradients, name))
952 syntaxerror("gradient %s defined twice", name);
953 dictionary_put2(&gradients, name, gradient);
956 void s_action(const char*text)
959 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
961 tag = swf_InsertTag(tag, ST_DOACTION);
963 swf_ActionSet(tag, a);
968 void s_outline(char*name, char*format, char*source)
977 swf_Shape11DrawerInit(&draw, 0);
978 draw_string(&draw, source);
980 shape = swf_ShapeDrawerToShape(&draw);
981 //shape2 = swf_ShapeToShape2(shape);
982 //bounds = swf_GetShapeBoundingBox(shape2);
983 //swf_Shape2Free(shape2);
984 bounds = swf_ShapeDrawerGetBBox(&draw);
987 outline = (outline_t*)malloc(sizeof(outline_t));
988 memset(outline, 0, sizeof(outline_t));
989 outline->shape = shape;
990 outline->bbox = bounds;
992 if(dictionary_lookup(&outlines, name))
993 syntaxerror("outline %s defined twice", name);
994 dictionary_put2(&outlines, name, outline);
997 void s_playsound(char*name, int loops, int nomultiple, int stop)
999 sound_t* sound = dictionary_lookup(&sounds, name);
1002 syntaxerror("Don't know anything about sound \"%s\"", name);
1004 tag = swf_InsertTag(tag, ST_STARTSOUND);
1005 swf_SetU16(tag, sound->id); //id
1006 memset(&info, 0, sizeof(info));
1009 info.nomultiple = nomultiple;
1010 swf_SetSoundInfo(tag, &info);
1013 void s_includeswf(char*name, char*filename)
1021 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1022 f = open(filename,O_RDONLY);
1024 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1025 s_box(name, 0, 0, black, 20, 0);
1028 if (swf_ReadSWF(f,&swf)<0) {
1029 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1030 s_box(name, 0, 0, black, 20, 0);
1035 /* FIXME: The following sets the bounding Box for the character.
1036 It is wrong for two reasons:
1037 a) It may be too small (in case objects in the movie clip at the borders)
1038 b) it may be too big (because the poor movie never got autocropped)
1042 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1043 swf_SetU16(tag, id);
1046 swf_Relocate(&swf, idmap);
1048 ftag = swf.firstTag;
1052 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1053 if(cutout[t] == ftag->id) {
1057 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1059 if(ftag->id == ST_END)
1063 /* We simply dump all tags right after the sprite
1064 header, relying on the fact that swf_OptimizeTagOrder() will
1065 sort things out for us later.
1066 We also rely on the fact that the imported SWF is well-formed.
1068 tag = swf_InsertTag(tag, ftag->id);
1069 swf_SetBlock(tag, ftag->data, ftag->len);
1073 syntaxerror("Included file %s contains errors", filename);
1074 tag = swf_InsertTag(tag, ST_END);
1078 s_addcharacter(name, id, tag, r);
1081 SRECT s_getCharBBox(char*name)
1083 character_t* c = dictionary_lookup(&characters, name);
1084 if(!c) syntaxerror("character '%s' unknown(2)", name);
1087 SRECT s_getInstanceBBox(char*name)
1089 instance_t * i = dictionary_lookup(&instances, name);
1091 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1093 if(!c) syntaxerror("internal error(5)");
1096 parameters_t s_getParameters(char*name)
1098 instance_t * i = dictionary_lookup(&instances, name);
1099 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1100 return i->parameters;
1102 void s_startclip(char*instance, char*character, parameters_t p)
1104 character_t* c = dictionary_lookup(&characters, character);
1108 syntaxerror("character %s not known", character);
1110 i = s_addinstance(instance, c, currentdepth);
1112 m = s_instancepos(i, &p);
1114 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1115 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1116 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1118 i->lastFrame= currentframe;
1120 stack[stackpos].tag = tag;
1121 stack[stackpos].type = 2;
1130 swf_SetTagPos(stack[stackpos].tag, 0);
1131 swf_GetPlaceObject(stack[stackpos].tag, &p);
1132 p.clipdepth = currentdepth;
1133 swf_ClearTag(stack[stackpos].tag);
1134 swf_SetPlaceObject(stack[stackpos].tag, &p);
1138 void s_put(char*instance, char*character, parameters_t p)
1140 character_t* c = dictionary_lookup(&characters, character);
1144 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1147 i = s_addinstance(instance, c, currentdepth);
1149 m = s_instancepos(i, &p);
1151 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1152 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1154 i->lastFrame = currentframe;
1158 void s_jump(char*instance, parameters_t p)
1160 instance_t* i = dictionary_lookup(&instances, instance);
1163 syntaxerror("instance %s not known", instance);
1167 m = s_instancepos(i, &p);
1169 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1170 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1172 i->lastFrame = currentframe;
1175 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1179 if(num==0 || num==1)
1181 ratio = (float)pos/(float)num;
1183 p.x = (p2->x-p1->x)*ratio + p1->x;
1184 p.y = (p2->y-p1->y)*ratio + p1->y;
1185 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1186 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1187 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1188 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1190 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1191 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1192 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1193 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1195 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1196 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1197 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1198 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1200 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1201 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1202 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1203 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1207 void s_change(char*instance, parameters_t p2)
1209 instance_t* i = dictionary_lookup(&instances, instance);
1213 int frame, allframes;
1215 syntaxerror("instance %s not known", instance);
1219 allframes = currentframe - i->lastFrame - 1;
1221 warning(".change ignored. can only .put/.change an object once per frame.");
1225 m = s_instancepos(i, &p2);
1226 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1227 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1230 /* o.k., we got the start and end point set. Now iterate though all the
1231 tags in between, inserting object changes after each new frame */
1234 if(!t) syntaxerror("internal error(6)");
1236 while(frame < allframes) {
1237 if(t->id == ST_SHOWFRAME) {
1242 p = s_interpolate(&p1, &p2, frame, allframes);
1243 m = s_instancepos(i, &p); //needed?
1244 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1245 i->lastFrame = currentframe;
1246 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1248 if(frame == allframes)
1253 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1257 void s_delinstance(char*instance)
1259 instance_t* i = dictionary_lookup(&instances, instance);
1261 syntaxerror("instance %s not known", instance);
1263 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1264 swf_SetU16(tag, i->depth);
1265 dictionary_del(&instances, instance);
1268 void s_qchange(char*instance, parameters_t p)
1275 syntaxerror(".end unexpected");
1276 if(stack[stackpos-1].type == 0)
1278 else if(stack[stackpos-1].type == 1)
1280 else if(stack[stackpos-1].type == 2)
1282 else syntaxerror("internal error 1");
1285 // ------------------------------------------------------------------------
1287 typedef int command_func_t(map_t*args);
1289 SRECT parseBox(char*str)
1292 float xmin, xmax, ymin, ymax;
1293 char*x = strchr(str, 'x');
1295 if(!strcmp(str, "autocrop")) {
1296 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1300 d1 = strchr(x+1, ':');
1302 d2 = strchr(d1+1, ':');
1304 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1308 else if(d1 && !d2) {
1309 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1315 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1320 r.xmin = (SCOORD)(xmin*20);
1321 r.ymin = (SCOORD)(ymin*20);
1322 r.xmax = (SCOORD)(xmax*20);
1323 r.ymax = (SCOORD)(ymax*20);
1326 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1329 float parseFloat(char*str)
1333 int parseInt(char*str)
1338 if(str[0]=='+' || str[0]=='-')
1342 if(str[t]<'0' || str[t]>'9')
1343 syntaxerror("Not an Integer: \"%s\"", str);
1346 int parseTwip(char*str)
1350 if(str[0]=='+' || str[0]=='-') {
1355 dot = strchr(str, '.');
1359 return sign*parseInt(str)*20;
1361 int l=strlen(++dot);
1363 for(s=str;s<dot-1;s++)
1364 if(*s<'0' || *s>'9')
1365 syntaxerror("Not a coordinate: \"%s\"", str);
1367 if(*s<'0' || *s>'9')
1368 syntaxerror("Not a coordinate: \"%s\"", str);
1370 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1371 warning("precision loss: %s converted to twip", str);
1376 return sign*atoi(str)*20;
1378 return sign*atoi(str)*20+atoi(dot)*2;
1380 return sign*atoi(str)*20+atoi(dot)/5;
1385 int isPoint(char*str)
1387 if(strchr(str, '('))
1393 SPOINT parsePoint(char*str)
1397 int l = strlen(str);
1398 char*comma = strchr(str, ',');
1399 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1400 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1401 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1402 p.x = parseTwip(tmp);
1403 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1404 p.y = parseTwip(tmp);
1408 int parseColor2(char*str, RGBA*color)
1410 int l = strlen(str);
1414 struct {unsigned char r,g,b;char*name;} colors[] =
1415 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1416 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1417 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1418 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1419 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1420 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1421 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1422 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1423 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1424 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1425 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1426 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1430 if(str[0]=='#' && (l==7 || l==9)) {
1431 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1433 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1435 color->r = r; color->g = g; color->b = b; color->a = a;
1438 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1439 if(!strcmp(str, colors[t].name)) {
1444 color->r = r; color->g = g; color->b = b; color->a = a;
1450 RGBA parseColor(char*str)
1453 if(!parseColor2(str, &c))
1454 syntaxerror("Expression '%s' is not a color", str);
1458 typedef struct _muladd {
1463 MULADD parseMulAdd(char*str)
1466 char* str2 = (char*)malloc(strlen(str)+5);
1473 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1474 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1475 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1476 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1477 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1478 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1479 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1480 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1481 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1482 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1484 syntaxerror("'%s' is not a valid color transform expression", str);
1486 m.add = (int)(add*256);
1487 m.mul = (int)(mul*256);
1492 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1494 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1495 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1497 if(a<-32768) a=-32768;
1498 if(a>32767) a=32767;
1499 if(m<-32768) m=-32768;
1500 if(m>32767) m=32767;
1506 float parsePercent(char*str)
1508 int l = strlen(str);
1512 return atoi(str)/100.0;
1514 syntaxerror("Expression '%s' is not a percentage", str);
1517 int isPercent(char*str)
1519 return str[strlen(str)-1]=='%';
1521 int parseNewSize(char*str, int size)
1524 return parsePercent(str)*size;
1526 return (int)(atof(str)*20);
1529 int isColor(char*str)
1532 return parseColor2(str, &c);
1535 static char* lu(map_t* args, char*name)
1537 char* value = map_lookup(args, name);
1539 map_dump(args, stdout, "");
1540 syntaxerror("internal error 2: value %s should be set", name);
1545 static int c_flash(map_t*args)
1547 char* name = lu(args, "name");
1548 char* compressstr = lu(args, "compress");
1549 SRECT bbox = parseBox(lu(args, "bbox"));
1550 int version = parseInt(lu(args, "version"));
1551 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1553 RGBA color = parseColor(lu(args, "background"));
1554 if(!strcmp(name, "!default!") || override_outputname)
1557 if(!strcmp(compressstr, "default"))
1558 compress = version==6;
1559 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1561 else if(!strcmp(compressstr, "no"))
1563 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1565 s_swf(name, bbox, version, fps, compress, color);
1568 int isRelative(char*str)
1570 return !strncmp(str, "<plus>", 6) ||
1571 !strncmp(str, "<minus>", 7);
1573 char* getOffset(char*str)
1575 if(!strncmp(str, "<plus>", 6))
1577 if(!strncmp(str, "<minus>", 7))
1579 syntaxerror("internal error (347)");
1582 int getSign(char*str)
1584 if(!strncmp(str, "<plus>", 6))
1586 if(!strncmp(str, "<minus>", 7))
1588 syntaxerror("internal error (348)");
1591 static dictionary_t points;
1592 static mem_t mpoints;
1593 int points_initialized = 0;
1595 SPOINT getPoint(SRECT r, char*name)
1598 if(!strcmp(name, "center")) {
1600 p.x = (r.xmin + r.xmax)/2;
1601 p.y = (r.ymin + r.ymax)/2;
1605 if(points_initialized)
1606 l = (int)dictionary_lookup(&points, name);
1608 syntaxerror("Invalid point: \"%s\".", name);
1611 return *(SPOINT*)&mpoints.buffer[l];
1613 static int c_gradient(map_t*args)
1615 char*name = lu(args, "name");
1616 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1620 syntaxerror("colon (:) expected");
1622 s_gradient(name, text, radial);
1625 static int c_point(map_t*args)
1627 char*name = lu(args, "name");
1631 if(!points_initialized) {
1632 dictionary_init(&points);
1634 points_initialized = 1;
1636 p.x = parseTwip(lu(args, "x"));
1637 p.y = parseTwip(lu(args, "y"));
1638 pos = mem_put(&mpoints, &p, sizeof(p));
1639 string_set(&s1, name);
1641 dictionary_put(&points, s1, (void*)pos);
1644 static int c_play(map_t*args)
1646 char*name = lu(args, "sound");
1647 char*loop = lu(args, "loop");
1648 char*nomultiple = lu(args, "nomultiple");
1650 if(!strcmp(nomultiple, "nomultiple"))
1653 nm = parseInt(nomultiple);
1655 s_playsound(name, parseInt(loop), nm, 0);
1659 static int c_stop(map_t*args)
1661 char*name = lu(args, "sound");
1662 s_playsound(name, 0,0,1);
1666 static int c_placement(map_t*args, int type)
1668 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1671 char* luminancestr = lu(args, "luminance");
1672 char* scalestr = lu(args, "scale");
1673 char* scalexstr = lu(args, "scalex");
1674 char* scaleystr = lu(args, "scaley");
1675 char* rotatestr = lu(args, "rotate");
1676 char* shearstr = lu(args, "shear");
1677 char* xstr="", *pivotstr="";
1678 char* ystr="", *anglestr="";
1679 char*above = lu(args, "above"); /*FIXME*/
1680 char*below = lu(args, "below");
1681 char* rstr = lu(args, "red");
1682 char* gstr = lu(args, "green");
1683 char* bstr = lu(args, "blue");
1684 char* astr = lu(args, "alpha");
1685 char* pinstr = lu(args, "pin");
1694 pivotstr = lu(args, "pivot");
1695 anglestr = lu(args, "angle");
1697 xstr = lu(args, "x");
1698 ystr = lu(args, "y");
1701 luminance = parseMulAdd(luminancestr);
1704 luminance.mul = 256;
1708 if(scalexstr[0]||scaleystr[0])
1709 syntaxerror("scalex/scaley and scale cannot both be set");
1710 scalexstr = scaleystr = scalestr;
1713 if(type == 0 || type == 4) {
1715 character = lu(args, "character");
1716 parameters_clear(&p);
1718 p = s_getParameters(instance);
1723 if(isRelative(xstr)) {
1724 if(type == 0 || type == 4)
1725 syntaxerror("relative x values not allowed for initial put or startclip");
1726 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1728 p.x = parseTwip(xstr);
1732 if(isRelative(ystr)) {
1733 if(type == 0 || type == 4)
1734 syntaxerror("relative y values not allowed for initial put or startclip");
1735 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1737 p.y = parseTwip(ystr);
1741 /* scale, scalex, scaley */
1743 oldbbox = s_getCharBBox(character);
1745 oldbbox = s_getInstanceBBox(instance);
1747 oldwidth = oldbbox.xmax - oldbbox.xmin;
1748 oldheight = oldbbox.ymax - oldbbox.ymin;
1750 if(oldwidth==0) p.scalex = 1.0;
1753 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1757 if(oldheight==0) p.scaley = 1.0;
1760 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1766 if(isRelative(rotatestr)) {
1767 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1769 p.rotate = parseFloat(rotatestr);
1775 if(isRelative(shearstr)) {
1776 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1778 p.shear = parseFloat(shearstr);
1783 if(isPoint(pivotstr))
1784 p.pivot = parsePoint(pivotstr);
1786 p.pivot = getPoint(oldbbox, pivotstr);
1790 p.pin = parsePoint(pinstr);
1792 p.pin = getPoint(oldbbox, pinstr);
1795 /* color transform */
1797 if(rstr[0] || luminancestr[0]) {
1800 r = parseMulAdd(rstr);
1802 r.add = p.cxform.r0;
1803 r.mul = p.cxform.r1;
1805 r = mergeMulAdd(r, luminance);
1806 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1808 if(gstr[0] || luminancestr[0]) {
1811 g = parseMulAdd(gstr);
1813 g.add = p.cxform.g0;
1814 g.mul = p.cxform.g1;
1816 g = mergeMulAdd(g, luminance);
1817 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1819 if(bstr[0] || luminancestr[0]) {
1822 b = parseMulAdd(bstr);
1824 b.add = p.cxform.b0;
1825 b.mul = p.cxform.b1;
1827 b = mergeMulAdd(b, luminance);
1828 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1831 MULADD a = parseMulAdd(astr);
1832 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1836 s_put(instance, character, p);
1838 s_change(instance, p);
1840 s_qchange(instance, p);
1842 s_jump(instance, p);
1844 s_startclip(instance, character, p);
1847 static int c_put(map_t*args)
1849 c_placement(args, 0);
1852 static int c_change(map_t*args)
1854 c_placement(args, 1);
1857 static int c_qchange(map_t*args)
1859 c_placement(args, 2);
1862 static int c_arcchange(map_t*args)
1864 c_placement(args, 2);
1867 static int c_jump(map_t*args)
1869 c_placement(args, 3);
1872 static int c_startclip(map_t*args)
1874 c_placement(args, 4);
1877 static int c_del(map_t*args)
1879 char*instance = lu(args, "name");
1880 s_delinstance(instance);
1883 static int c_end(map_t*args)
1888 static int c_sprite(map_t*args)
1890 char* name = lu(args, "name");
1894 static int c_frame(map_t*args)
1896 char*framestr = lu(args, "n");
1897 char*cutstr = lu(args, "cut");
1900 if(strcmp(cutstr, "no"))
1902 if(isRelative(framestr)) {
1903 frame = s_getframe();
1904 if(getSign(framestr)<0)
1905 syntaxerror("relative frame expressions must be positive");
1906 frame += parseInt(getOffset(framestr));
1909 frame = parseInt(framestr);
1910 if(s_getframe() >= frame
1911 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1912 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1914 s_frame(frame, cut);
1917 static int c_primitive(map_t*args)
1919 char*name = lu(args, "name");
1920 char*command = lu(args, "commandname");
1921 int width=0, height=0, r=0;
1922 int linewidth = parseTwip(lu(args, "line"));
1923 char*colorstr = lu(args, "color");
1924 RGBA color = parseColor(colorstr);
1925 char*fillstr = lu(args, "fill");
1932 if(!strcmp(command, "circle"))
1934 else if(!strcmp(command, "filled"))
1938 width = parseTwip(lu(args, "width"));
1939 height = parseTwip(lu(args, "height"));
1940 } else if (type==1) {
1941 r = parseTwip(lu(args, "r"));
1942 } else if (type==2) {
1943 outline = lu(args, "outline");
1946 if(!strcmp(fillstr, "fill"))
1948 if(!strcmp(fillstr, "none"))
1950 if(width<0 || height<0 || linewidth<0 || r<0)
1951 syntaxerror("values width, height, line, r must be positive");
1953 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1954 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1955 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
1959 static int c_textshape(map_t*args)
1961 char*name = lu(args, "name");
1962 char*text = lu(args, "text");
1963 char*font = lu(args, "font");
1965 s_textshape(name, font, text);
1971 static int c_swf(map_t*args)
1973 char*name = lu(args, "name");
1974 char*filename = lu(args, "filename");
1975 char*command = lu(args, "commandname");
1976 if(!strcmp(command, "shape"))
1977 warning("Please use .swf instead of .shape");
1978 s_includeswf(name, filename);
1982 static int c_font(map_t*args)
1984 char*name = lu(args, "name");
1985 char*filename = lu(args, "filename");
1986 s_font(name, filename);
1990 static int c_sound(map_t*args)
1992 char*name = lu(args, "name");
1993 char*filename = lu(args, "filename");
1994 s_sound(name, filename);
1998 static int c_text(map_t*args)
2000 char*name = lu(args, "name");
2001 char*text = lu(args, "text");
2002 char*font = lu(args, "font");
2003 float size = parsePercent(lu(args, "size"));
2004 RGBA color = parseColor(lu(args, "color"));
2005 s_text(name, font, text, (int)(size*100), color);
2009 static int c_soundtrack(map_t*args)
2014 static int c_image(map_t*args)
2016 char*command = lu(args, "commandname");
2017 char*name = lu(args, "name");
2018 char*filename = lu(args, "filename");
2019 if(!strcmp(command,"jpeg")) {
2020 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2021 s_image(name, "jpeg", filename, quality);
2023 s_image(name, "png", filename, 0);
2028 static int c_outline(map_t*args)
2030 char*name = lu(args, "name");
2031 char*format = lu(args, "format");
2035 syntaxerror("colon (:) expected");
2037 s_outline(name, format, text);
2041 int fakechar(map_t*args)
2043 char*name = lu(args, "name");
2044 s_box(name, 0, 0, black, 20, 0);
2048 static int c_egon(map_t*args) {return fakechar(args);}
2049 static int c_button(map_t*args) {
2057 return fakechar(args);
2059 static int c_edittext(map_t*args) {return fakechar(args);}
2061 static int c_morphshape(map_t*args) {return fakechar(args);}
2062 static int c_movie(map_t*args) {return fakechar(args);}
2064 static int c_buttonsounds(map_t*args) {return 0;}
2065 static int c_buttonput(map_t*args) {return 0;}
2066 static int c_texture(map_t*args) {return 0;}
2068 static int c_action(map_t*args)
2072 syntaxerror("colon (:) expected");
2081 command_func_t* func;
2084 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2085 {"frame", c_frame, "n=<plus>1 @cut=no"},
2086 // "import" type stuff
2087 {"swf", c_swf, "name filename"},
2088 {"shape", c_swf, "name filename"},
2089 {"jpeg", c_image, "name filename quality=80%"},
2090 {"png", c_image, "name filename"},
2091 {"movie", c_movie, "name filename"},
2092 {"sound", c_sound, "name filename"},
2093 {"font", c_font, "name filename"},
2094 {"soundtrack", c_soundtrack, "filename"},
2096 // generators of primitives
2098 {"point", c_point, "name x=0 y=0"},
2099 {"gradient", c_gradient, "name @radial=0"},
2100 {"outline", c_outline, "name format=simple"},
2101 {"textshape", c_textshape, "name text font"},
2103 // character generators
2104 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2105 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2106 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2108 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2109 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
2110 {"text", c_text, "name text font size=100% color=white"},
2111 {"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"},
2112 {"morphshape", c_morphshape, "name start end"},
2114 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
2117 {"play", c_play, "sound loop=0 @nomultiple=0"},
2118 {"stop", c_stop, "sound"},
2120 // object placement tags
2121 {"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="},
2122 {"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="},
2123 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2124 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2125 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2126 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2127 {"del", c_del, "name"},
2128 // virtual object placement
2129 {"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="},
2130 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2132 // commands which start a block
2133 //startclip (see above)
2134 {"sprite", c_sprite, "name"},
2135 {"action", c_action, ""},
2141 static map_t parseArguments(char*command, char*pattern)
2157 string_set(&t1, "commandname");
2158 string_set(&t2, command);
2159 map_put(&result, t1, t2);
2161 if(!pattern || !*pattern)
2168 if(!strncmp("<i> ", x, 3)) {
2170 if(type == COMMAND || type == RAWDATA) {
2172 syntaxerror("character name expected");
2174 name[pos].str = "instance";
2176 value[pos].str = text;
2177 value[pos].len = strlen(text);
2181 if(type == ASSIGNMENT)
2184 name[pos].str = "character";
2186 value[pos].str = text;
2187 value[pos].len = strlen(text);
2195 isboolean[pos] = (x[0] =='@');
2208 name[pos].len = d-x;
2213 name[pos].len = e-x;
2214 value[pos].str = e+1;
2215 value[pos].len = d-e-1;
2223 /* for(t=0;t<len;t++) {
2224 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2225 isboolean[t]?"(boolean)":"");
2230 if(type == RAWDATA || type == COMMAND) {
2235 // first, search for boolean arguments
2236 for(pos=0;pos<len;pos++)
2238 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2240 if(type == ASSIGNMENT)
2242 value[pos].str = text;
2243 value[pos].len = strlen(text);
2244 /*printf("setting boolean parameter %s (to %s)\n",
2245 strdup_n(name[pos], namelen[pos]),
2246 strdup_n(value[pos], valuelen[pos]));*/
2251 // second, search for normal arguments
2253 for(pos=0;pos<len;pos++)
2255 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2256 (type != ASSIGNMENT && !set[pos])) {
2258 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2260 if(type == ASSIGNMENT)
2263 value[pos].str = text;
2264 value[pos].len = strlen(text);
2266 printf("setting parameter %s (to %s)\n",
2267 strdup_n(name[pos].str, name[pos].len),
2268 strdup_n(value[pos].str, value[pos].len));
2274 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2278 for(t=0;t<len;t++) {
2279 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2282 for(t=0;t<len;t++) {
2283 if(value[t].str && value[t].str[0] == '*') {
2284 //relative default- take value from some other parameter
2286 for(s=0;s<len;s++) {
2287 if(value[s].len == value[t].len-1 &&
2288 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2289 value[t].str = value[s].str;
2292 if(value[t].str == 0) {
2294 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2298 /* ok, now construct the dictionary from the parameters */
2302 map_put(&result, name[t], value[t]);
2306 static void parseArgumentsForCommand(char*command)
2311 msg("<verbose> parse Command: %s (line %d)", command, line);
2312 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2313 if(!strcmp(arguments[t].command, command)) {
2315 /* ugly hack- will be removed soon (once documentation and .sc generating
2316 utilities have been changed) */
2317 if(!strcmp(command, "swf") && !stackpos) {
2318 warning("Please use .flash instead of .swf- this will be mandatory soon");
2323 args = parseArguments(command, arguments[t].arguments);
2329 syntaxerror("command %s not known", command);
2332 printf(".%s\n", command);fflush(stdout);
2333 map_dump(&args, stdout, "\t");fflush(stdout);
2336 (*arguments[nr].func)(&args);
2338 /*if(!strcmp(command, "button") ||
2339 !strcmp(command, "action")) {
2342 if(type == COMMAND) {
2343 if(!strcmp(text, "end"))
2357 int main (int argc,char ** argv)
2360 processargs(argc, argv);
2361 initLog(0,-1,0,0,-1,verbose);
2364 args_callback_usage(argv[0]);
2368 file = generateTokens(filename);
2370 printf("parser returned error.\n");
2377 while(!noMoreTokens()) {
2380 syntaxerror("command expected");
2381 parseArgumentsForCommand(text);