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 syntaxerror("Couldn't compile ActionScript");
964 tag = swf_InsertTag(tag, ST_DOACTION);
966 swf_ActionSet(tag, a);
971 void s_outline(char*name, char*format, char*source)
980 swf_Shape11DrawerInit(&draw, 0);
981 draw_string(&draw, source);
983 shape = swf_ShapeDrawerToShape(&draw);
984 //shape2 = swf_ShapeToShape2(shape);
985 //bounds = swf_GetShapeBoundingBox(shape2);
986 //swf_Shape2Free(shape2);
987 bounds = swf_ShapeDrawerGetBBox(&draw);
990 outline = (outline_t*)malloc(sizeof(outline_t));
991 memset(outline, 0, sizeof(outline_t));
992 outline->shape = shape;
993 outline->bbox = bounds;
995 if(dictionary_lookup(&outlines, name))
996 syntaxerror("outline %s defined twice", name);
997 dictionary_put2(&outlines, name, outline);
1000 void s_playsound(char*name, int loops, int nomultiple, int stop)
1002 sound_t* sound = dictionary_lookup(&sounds, name);
1005 syntaxerror("Don't know anything about sound \"%s\"", name);
1007 tag = swf_InsertTag(tag, ST_STARTSOUND);
1008 swf_SetU16(tag, sound->id); //id
1009 memset(&info, 0, sizeof(info));
1012 info.nomultiple = nomultiple;
1013 swf_SetSoundInfo(tag, &info);
1016 void s_includeswf(char*name, char*filename)
1024 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1025 f = open(filename,O_RDONLY);
1027 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1028 s_box(name, 0, 0, black, 20, 0);
1031 if (swf_ReadSWF(f,&swf)<0) {
1032 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1033 s_box(name, 0, 0, black, 20, 0);
1038 /* FIXME: The following sets the bounding Box for the character.
1039 It is wrong for two reasons:
1040 a) It may be too small (in case objects in the movie clip at the borders)
1041 b) it may be too big (because the poor movie never got autocropped)
1045 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1046 swf_SetU16(tag, id);
1049 swf_Relocate(&swf, idmap);
1051 ftag = swf.firstTag;
1055 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1056 if(cutout[t] == ftag->id) {
1060 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1062 if(ftag->id == ST_END)
1066 /* We simply dump all tags right after the sprite
1067 header, relying on the fact that swf_OptimizeTagOrder() will
1068 sort things out for us later.
1069 We also rely on the fact that the imported SWF is well-formed.
1071 tag = swf_InsertTag(tag, ftag->id);
1072 swf_SetBlock(tag, ftag->data, ftag->len);
1076 syntaxerror("Included file %s contains errors", filename);
1077 tag = swf_InsertTag(tag, ST_END);
1081 s_addcharacter(name, id, tag, r);
1084 SRECT s_getCharBBox(char*name)
1086 character_t* c = dictionary_lookup(&characters, name);
1087 if(!c) syntaxerror("character '%s' unknown(2)", name);
1090 SRECT s_getInstanceBBox(char*name)
1092 instance_t * i = dictionary_lookup(&instances, name);
1094 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1096 if(!c) syntaxerror("internal error(5)");
1099 parameters_t s_getParameters(char*name)
1101 instance_t * i = dictionary_lookup(&instances, name);
1102 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1103 return i->parameters;
1105 void s_startclip(char*instance, char*character, parameters_t p)
1107 character_t* c = dictionary_lookup(&characters, character);
1111 syntaxerror("character %s not known", character);
1113 i = s_addinstance(instance, c, currentdepth);
1115 m = s_instancepos(i, &p);
1117 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1118 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1119 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1121 i->lastFrame= currentframe;
1123 stack[stackpos].tag = tag;
1124 stack[stackpos].type = 2;
1133 swf_SetTagPos(stack[stackpos].tag, 0);
1134 swf_GetPlaceObject(stack[stackpos].tag, &p);
1135 p.clipdepth = currentdepth;
1136 swf_ClearTag(stack[stackpos].tag);
1137 swf_SetPlaceObject(stack[stackpos].tag, &p);
1141 void s_put(char*instance, char*character, parameters_t p)
1143 character_t* c = dictionary_lookup(&characters, character);
1147 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1150 i = s_addinstance(instance, c, currentdepth);
1152 m = s_instancepos(i, &p);
1154 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1155 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1157 i->lastFrame = currentframe;
1161 void s_jump(char*instance, parameters_t p)
1163 instance_t* i = dictionary_lookup(&instances, instance);
1166 syntaxerror("instance %s not known", instance);
1170 m = s_instancepos(i, &p);
1172 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1173 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1175 i->lastFrame = currentframe;
1178 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1182 if(num==0 || num==1)
1184 ratio = (float)pos/(float)num;
1186 p.x = (p2->x-p1->x)*ratio + p1->x;
1187 p.y = (p2->y-p1->y)*ratio + p1->y;
1188 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1189 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1190 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1191 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1193 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1194 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1195 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1196 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1198 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1199 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1200 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1201 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1203 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1204 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1205 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1206 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1210 void s_change(char*instance, parameters_t p2)
1212 instance_t* i = dictionary_lookup(&instances, instance);
1216 int frame, allframes;
1218 syntaxerror("instance %s not known", instance);
1222 allframes = currentframe - i->lastFrame - 1;
1224 warning(".change ignored. can only .put/.change an object once per frame.");
1228 m = s_instancepos(i, &p2);
1229 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1230 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1233 /* o.k., we got the start and end point set. Now iterate though all the
1234 tags in between, inserting object changes after each new frame */
1237 if(!t) syntaxerror("internal error(6)");
1239 while(frame < allframes) {
1240 if(t->id == ST_SHOWFRAME) {
1245 p = s_interpolate(&p1, &p2, frame, allframes);
1246 m = s_instancepos(i, &p); //needed?
1247 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1248 i->lastFrame = currentframe;
1249 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1251 if(frame == allframes)
1256 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1260 void s_delinstance(char*instance)
1262 instance_t* i = dictionary_lookup(&instances, instance);
1264 syntaxerror("instance %s not known", instance);
1266 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1267 swf_SetU16(tag, i->depth);
1268 dictionary_del(&instances, instance);
1271 void s_qchange(char*instance, parameters_t p)
1278 syntaxerror(".end unexpected");
1279 if(stack[stackpos-1].type == 0)
1281 else if(stack[stackpos-1].type == 1)
1283 else if(stack[stackpos-1].type == 2)
1285 else syntaxerror("internal error 1");
1288 // ------------------------------------------------------------------------
1290 typedef int command_func_t(map_t*args);
1292 SRECT parseBox(char*str)
1295 float xmin, xmax, ymin, ymax;
1296 char*x = strchr(str, 'x');
1298 if(!strcmp(str, "autocrop")) {
1299 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1303 d1 = strchr(x+1, ':');
1305 d2 = strchr(d1+1, ':');
1307 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1311 else if(d1 && !d2) {
1312 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1318 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1323 r.xmin = (SCOORD)(xmin*20);
1324 r.ymin = (SCOORD)(ymin*20);
1325 r.xmax = (SCOORD)(xmax*20);
1326 r.ymax = (SCOORD)(ymax*20);
1329 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1332 float parseFloat(char*str)
1336 int parseInt(char*str)
1341 if(str[0]=='+' || str[0]=='-')
1345 if(str[t]<'0' || str[t]>'9')
1346 syntaxerror("Not an Integer: \"%s\"", str);
1349 int parseTwip(char*str)
1353 if(str[0]=='+' || str[0]=='-') {
1358 dot = strchr(str, '.');
1362 return sign*parseInt(str)*20;
1364 int l=strlen(++dot);
1366 for(s=str;s<dot-1;s++)
1367 if(*s<'0' || *s>'9')
1368 syntaxerror("Not a coordinate: \"%s\"", str);
1370 if(*s<'0' || *s>'9')
1371 syntaxerror("Not a coordinate: \"%s\"", str);
1373 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1374 warning("precision loss: %s converted to twip", str);
1379 return sign*atoi(str)*20;
1381 return sign*atoi(str)*20+atoi(dot)*2;
1383 return sign*atoi(str)*20+atoi(dot)/5;
1388 int isPoint(char*str)
1390 if(strchr(str, '('))
1396 SPOINT parsePoint(char*str)
1400 int l = strlen(str);
1401 char*comma = strchr(str, ',');
1402 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1403 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1404 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1405 p.x = parseTwip(tmp);
1406 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1407 p.y = parseTwip(tmp);
1411 int parseColor2(char*str, RGBA*color)
1413 int l = strlen(str);
1417 struct {unsigned char r,g,b;char*name;} colors[] =
1418 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1419 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1420 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1421 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1422 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1423 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1424 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1425 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1426 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1427 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1428 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1429 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1433 if(str[0]=='#' && (l==7 || l==9)) {
1434 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1436 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1438 color->r = r; color->g = g; color->b = b; color->a = a;
1441 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1442 if(!strcmp(str, colors[t].name)) {
1447 color->r = r; color->g = g; color->b = b; color->a = a;
1453 RGBA parseColor(char*str)
1456 if(!parseColor2(str, &c))
1457 syntaxerror("Expression '%s' is not a color", str);
1461 typedef struct _muladd {
1466 MULADD parseMulAdd(char*str)
1469 char* str2 = (char*)malloc(strlen(str)+5);
1476 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1477 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1478 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1479 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1480 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1481 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1482 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1483 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1484 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1485 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1487 syntaxerror("'%s' is not a valid color transform expression", str);
1489 m.add = (int)(add*256);
1490 m.mul = (int)(mul*256);
1495 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1497 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1498 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1500 if(a<-32768) a=-32768;
1501 if(a>32767) a=32767;
1502 if(m<-32768) m=-32768;
1503 if(m>32767) m=32767;
1509 float parsePercent(char*str)
1511 int l = strlen(str);
1515 return atoi(str)/100.0;
1517 syntaxerror("Expression '%s' is not a percentage", str);
1520 int isPercent(char*str)
1522 return str[strlen(str)-1]=='%';
1524 int parseNewSize(char*str, int size)
1527 return parsePercent(str)*size;
1529 return (int)(atof(str)*20);
1532 int isColor(char*str)
1535 return parseColor2(str, &c);
1538 static char* lu(map_t* args, char*name)
1540 char* value = map_lookup(args, name);
1542 map_dump(args, stdout, "");
1543 syntaxerror("internal error 2: value %s should be set", name);
1548 static int c_flash(map_t*args)
1550 char* name = lu(args, "name");
1551 char* compressstr = lu(args, "compress");
1552 SRECT bbox = parseBox(lu(args, "bbox"));
1553 int version = parseInt(lu(args, "version"));
1554 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1556 RGBA color = parseColor(lu(args, "background"));
1557 if(!strcmp(name, "!default!") || override_outputname)
1560 if(!strcmp(compressstr, "default"))
1561 compress = version==6;
1562 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1564 else if(!strcmp(compressstr, "no"))
1566 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1568 s_swf(name, bbox, version, fps, compress, color);
1571 int isRelative(char*str)
1573 return !strncmp(str, "<plus>", 6) ||
1574 !strncmp(str, "<minus>", 7);
1576 char* getOffset(char*str)
1578 if(!strncmp(str, "<plus>", 6))
1580 if(!strncmp(str, "<minus>", 7))
1582 syntaxerror("internal error (347)");
1585 int getSign(char*str)
1587 if(!strncmp(str, "<plus>", 6))
1589 if(!strncmp(str, "<minus>", 7))
1591 syntaxerror("internal error (348)");
1594 static dictionary_t points;
1595 static mem_t mpoints;
1596 int points_initialized = 0;
1598 SPOINT getPoint(SRECT r, char*name)
1601 if(!strcmp(name, "center")) {
1603 p.x = (r.xmin + r.xmax)/2;
1604 p.y = (r.ymin + r.ymax)/2;
1608 if(points_initialized)
1609 l = (int)dictionary_lookup(&points, name);
1611 syntaxerror("Invalid point: \"%s\".", name);
1614 return *(SPOINT*)&mpoints.buffer[l];
1616 static int c_gradient(map_t*args)
1618 char*name = lu(args, "name");
1619 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1623 syntaxerror("colon (:) expected");
1625 s_gradient(name, text, radial);
1628 static int c_point(map_t*args)
1630 char*name = lu(args, "name");
1634 if(!points_initialized) {
1635 dictionary_init(&points);
1637 points_initialized = 1;
1639 p.x = parseTwip(lu(args, "x"));
1640 p.y = parseTwip(lu(args, "y"));
1641 pos = mem_put(&mpoints, &p, sizeof(p));
1642 string_set(&s1, name);
1644 dictionary_put(&points, s1, (void*)pos);
1647 static int c_play(map_t*args)
1649 char*name = lu(args, "sound");
1650 char*loop = lu(args, "loop");
1651 char*nomultiple = lu(args, "nomultiple");
1653 if(!strcmp(nomultiple, "nomultiple"))
1656 nm = parseInt(nomultiple);
1658 s_playsound(name, parseInt(loop), nm, 0);
1662 static int c_stop(map_t*args)
1664 char*name = lu(args, "sound");
1665 s_playsound(name, 0,0,1);
1669 static int c_placement(map_t*args, int type)
1671 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1674 char* luminancestr = lu(args, "luminance");
1675 char* scalestr = lu(args, "scale");
1676 char* scalexstr = lu(args, "scalex");
1677 char* scaleystr = lu(args, "scaley");
1678 char* rotatestr = lu(args, "rotate");
1679 char* shearstr = lu(args, "shear");
1680 char* xstr="", *pivotstr="";
1681 char* ystr="", *anglestr="";
1682 char*above = lu(args, "above"); /*FIXME*/
1683 char*below = lu(args, "below");
1684 char* rstr = lu(args, "red");
1685 char* gstr = lu(args, "green");
1686 char* bstr = lu(args, "blue");
1687 char* astr = lu(args, "alpha");
1688 char* pinstr = lu(args, "pin");
1697 pivotstr = lu(args, "pivot");
1698 anglestr = lu(args, "angle");
1700 xstr = lu(args, "x");
1701 ystr = lu(args, "y");
1704 luminance = parseMulAdd(luminancestr);
1707 luminance.mul = 256;
1711 if(scalexstr[0]||scaleystr[0])
1712 syntaxerror("scalex/scaley and scale cannot both be set");
1713 scalexstr = scaleystr = scalestr;
1716 if(type == 0 || type == 4) {
1718 character = lu(args, "character");
1719 parameters_clear(&p);
1721 p = s_getParameters(instance);
1726 if(isRelative(xstr)) {
1727 if(type == 0 || type == 4)
1728 syntaxerror("relative x values not allowed for initial put or startclip");
1729 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1731 p.x = parseTwip(xstr);
1735 if(isRelative(ystr)) {
1736 if(type == 0 || type == 4)
1737 syntaxerror("relative y values not allowed for initial put or startclip");
1738 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1740 p.y = parseTwip(ystr);
1744 /* scale, scalex, scaley */
1746 oldbbox = s_getCharBBox(character);
1748 oldbbox = s_getInstanceBBox(instance);
1750 oldwidth = oldbbox.xmax - oldbbox.xmin;
1751 oldheight = oldbbox.ymax - oldbbox.ymin;
1753 if(oldwidth==0) p.scalex = 1.0;
1756 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1760 if(oldheight==0) p.scaley = 1.0;
1763 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1769 if(isRelative(rotatestr)) {
1770 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1772 p.rotate = parseFloat(rotatestr);
1778 if(isRelative(shearstr)) {
1779 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1781 p.shear = parseFloat(shearstr);
1786 if(isPoint(pivotstr))
1787 p.pivot = parsePoint(pivotstr);
1789 p.pivot = getPoint(oldbbox, pivotstr);
1793 p.pin = parsePoint(pinstr);
1795 p.pin = getPoint(oldbbox, pinstr);
1798 /* color transform */
1800 if(rstr[0] || luminancestr[0]) {
1803 r = parseMulAdd(rstr);
1805 r.add = p.cxform.r0;
1806 r.mul = p.cxform.r1;
1808 r = mergeMulAdd(r, luminance);
1809 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1811 if(gstr[0] || luminancestr[0]) {
1814 g = parseMulAdd(gstr);
1816 g.add = p.cxform.g0;
1817 g.mul = p.cxform.g1;
1819 g = mergeMulAdd(g, luminance);
1820 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1822 if(bstr[0] || luminancestr[0]) {
1825 b = parseMulAdd(bstr);
1827 b.add = p.cxform.b0;
1828 b.mul = p.cxform.b1;
1830 b = mergeMulAdd(b, luminance);
1831 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1834 MULADD a = parseMulAdd(astr);
1835 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1839 s_put(instance, character, p);
1841 s_change(instance, p);
1843 s_qchange(instance, p);
1845 s_jump(instance, p);
1847 s_startclip(instance, character, p);
1850 static int c_put(map_t*args)
1852 c_placement(args, 0);
1855 static int c_change(map_t*args)
1857 c_placement(args, 1);
1860 static int c_qchange(map_t*args)
1862 c_placement(args, 2);
1865 static int c_arcchange(map_t*args)
1867 c_placement(args, 2);
1870 static int c_jump(map_t*args)
1872 c_placement(args, 3);
1875 static int c_startclip(map_t*args)
1877 c_placement(args, 4);
1880 static int c_del(map_t*args)
1882 char*instance = lu(args, "name");
1883 s_delinstance(instance);
1886 static int c_end(map_t*args)
1891 static int c_sprite(map_t*args)
1893 char* name = lu(args, "name");
1897 static int c_frame(map_t*args)
1899 char*framestr = lu(args, "n");
1900 char*cutstr = lu(args, "cut");
1903 if(strcmp(cutstr, "no"))
1905 if(isRelative(framestr)) {
1906 frame = s_getframe();
1907 if(getSign(framestr)<0)
1908 syntaxerror("relative frame expressions must be positive");
1909 frame += parseInt(getOffset(framestr));
1912 frame = parseInt(framestr);
1913 if(s_getframe() >= frame
1914 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1915 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1917 s_frame(frame, cut);
1920 static int c_primitive(map_t*args)
1922 char*name = lu(args, "name");
1923 char*command = lu(args, "commandname");
1924 int width=0, height=0, r=0;
1925 int linewidth = parseTwip(lu(args, "line"));
1926 char*colorstr = lu(args, "color");
1927 RGBA color = parseColor(colorstr);
1928 char*fillstr = lu(args, "fill");
1935 if(!strcmp(command, "circle"))
1937 else if(!strcmp(command, "filled"))
1941 width = parseTwip(lu(args, "width"));
1942 height = parseTwip(lu(args, "height"));
1943 } else if (type==1) {
1944 r = parseTwip(lu(args, "r"));
1945 } else if (type==2) {
1946 outline = lu(args, "outline");
1949 if(!strcmp(fillstr, "fill"))
1951 if(!strcmp(fillstr, "none"))
1953 if(width<0 || height<0 || linewidth<0 || r<0)
1954 syntaxerror("values width, height, line, r must be positive");
1956 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1957 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1958 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
1962 static int c_textshape(map_t*args)
1964 char*name = lu(args, "name");
1965 char*text = lu(args, "text");
1966 char*font = lu(args, "font");
1968 s_textshape(name, font, text);
1974 static int c_swf(map_t*args)
1976 char*name = lu(args, "name");
1977 char*filename = lu(args, "filename");
1978 char*command = lu(args, "commandname");
1979 if(!strcmp(command, "shape"))
1980 warning("Please use .swf instead of .shape");
1981 s_includeswf(name, filename);
1985 static int c_font(map_t*args)
1987 char*name = lu(args, "name");
1988 char*filename = lu(args, "filename");
1989 s_font(name, filename);
1993 static int c_sound(map_t*args)
1995 char*name = lu(args, "name");
1996 char*filename = lu(args, "filename");
1997 s_sound(name, filename);
2001 static int c_text(map_t*args)
2003 char*name = lu(args, "name");
2004 char*text = lu(args, "text");
2005 char*font = lu(args, "font");
2006 float size = parsePercent(lu(args, "size"));
2007 RGBA color = parseColor(lu(args, "color"));
2008 s_text(name, font, text, (int)(size*100), color);
2012 static int c_soundtrack(map_t*args)
2017 static int c_image(map_t*args)
2019 char*command = lu(args, "commandname");
2020 char*name = lu(args, "name");
2021 char*filename = lu(args, "filename");
2022 if(!strcmp(command,"jpeg")) {
2023 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2024 s_image(name, "jpeg", filename, quality);
2026 s_image(name, "png", filename, 0);
2031 static int c_outline(map_t*args)
2033 char*name = lu(args, "name");
2034 char*format = lu(args, "format");
2038 syntaxerror("colon (:) expected");
2040 s_outline(name, format, text);
2044 int fakechar(map_t*args)
2046 char*name = lu(args, "name");
2047 s_box(name, 0, 0, black, 20, 0);
2051 static int c_egon(map_t*args) {return fakechar(args);}
2052 static int c_button(map_t*args) {
2060 return fakechar(args);
2062 static int c_edittext(map_t*args) {return fakechar(args);}
2064 static int c_morphshape(map_t*args) {return fakechar(args);}
2065 static int c_movie(map_t*args) {return fakechar(args);}
2067 static int c_buttonsounds(map_t*args) {return 0;}
2068 static int c_buttonput(map_t*args) {return 0;}
2069 static int c_texture(map_t*args) {return 0;}
2071 static int c_action(map_t*args)
2074 if(type != RAWDATA) {
2075 syntaxerror("colon (:) expected");
2085 command_func_t* func;
2088 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2089 {"frame", c_frame, "n=<plus>1 @cut=no"},
2090 // "import" type stuff
2091 {"swf", c_swf, "name filename"},
2092 {"shape", c_swf, "name filename"},
2093 {"jpeg", c_image, "name filename quality=80%"},
2094 {"png", c_image, "name filename"},
2095 {"movie", c_movie, "name filename"},
2096 {"sound", c_sound, "name filename"},
2097 {"font", c_font, "name filename"},
2098 {"soundtrack", c_soundtrack, "filename"},
2100 // generators of primitives
2102 {"point", c_point, "name x=0 y=0"},
2103 {"gradient", c_gradient, "name @radial=0"},
2104 {"outline", c_outline, "name format=simple"},
2105 {"textshape", c_textshape, "name text font"},
2107 // character generators
2108 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2109 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2110 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2112 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2113 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
2114 {"text", c_text, "name text font size=100% color=white"},
2115 {"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"},
2116 {"morphshape", c_morphshape, "name start end"},
2118 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
2121 {"play", c_play, "sound loop=0 @nomultiple=0"},
2122 {"stop", c_stop, "sound"},
2124 // object placement tags
2125 {"put", c_put, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2126 {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2127 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2128 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2129 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2130 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2131 {"del", c_del, "name"},
2132 // virtual object placement
2133 {"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="}, //TODO: ratio???
2134 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2136 // commands which start a block
2137 //startclip (see above)
2138 {"sprite", c_sprite, "name"},
2139 {"action", c_action, ""},
2145 static map_t parseArguments(char*command, char*pattern)
2161 string_set(&t1, "commandname");
2162 string_set(&t2, command);
2163 map_put(&result, t1, t2);
2165 if(!pattern || !*pattern)
2172 if(!strncmp("<i> ", x, 3)) {
2174 if(type == COMMAND || type == RAWDATA) {
2176 syntaxerror("character name expected");
2178 name[pos].str = "instance";
2180 value[pos].str = text;
2181 value[pos].len = strlen(text);
2185 if(type == ASSIGNMENT)
2188 name[pos].str = "character";
2190 value[pos].str = text;
2191 value[pos].len = strlen(text);
2199 isboolean[pos] = (x[0] =='@');
2212 name[pos].len = d-x;
2217 name[pos].len = e-x;
2218 value[pos].str = e+1;
2219 value[pos].len = d-e-1;
2227 /* for(t=0;t<len;t++) {
2228 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2229 isboolean[t]?"(boolean)":"");
2234 if(type == RAWDATA || type == COMMAND) {
2239 // first, search for boolean arguments
2240 for(pos=0;pos<len;pos++)
2242 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2244 if(type == ASSIGNMENT)
2246 value[pos].str = text;
2247 value[pos].len = strlen(text);
2248 /*printf("setting boolean parameter %s (to %s)\n",
2249 strdup_n(name[pos], namelen[pos]),
2250 strdup_n(value[pos], valuelen[pos]));*/
2255 // second, search for normal arguments
2257 for(pos=0;pos<len;pos++)
2259 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2260 (type != ASSIGNMENT && !set[pos])) {
2262 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2264 if(type == ASSIGNMENT)
2267 value[pos].str = text;
2268 value[pos].len = strlen(text);
2270 printf("setting parameter %s (to %s)\n",
2271 strdup_n(name[pos].str, name[pos].len),
2272 strdup_n(value[pos].str, value[pos].len));
2278 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2282 for(t=0;t<len;t++) {
2283 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2286 for(t=0;t<len;t++) {
2287 if(value[t].str && value[t].str[0] == '*') {
2288 //relative default- take value from some other parameter
2290 for(s=0;s<len;s++) {
2291 if(value[s].len == value[t].len-1 &&
2292 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2293 value[t].str = value[s].str;
2296 if(value[t].str == 0) {
2298 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2302 /* ok, now construct the dictionary from the parameters */
2306 map_put(&result, name[t], value[t]);
2310 static void parseArgumentsForCommand(char*command)
2315 msg("<verbose> parse Command: %s (line %d)", command, line);
2317 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2318 if(!strcmp(arguments[t].command, command)) {
2320 /* ugly hack- will be removed soon (once documentation and .sc generating
2321 utilities have been changed) */
2322 if(!strcmp(command, "swf") && !stackpos) {
2323 warning("Please use .flash instead of .swf- this will be mandatory soon");
2328 args = parseArguments(command, arguments[t].arguments);
2334 syntaxerror("command %s not known", command);
2336 // catch missing .flash directives at the beginning of a file
2337 if(strcmp(command, "flash") && !stackpos)
2339 syntaxerror("No movie defined- use .flash first");
2343 printf(".%s\n", command);fflush(stdout);
2344 map_dump(&args, stdout, "\t");fflush(stdout);
2347 (*arguments[nr].func)(&args);
2349 /*if(!strcmp(command, "button") ||
2350 !strcmp(command, "action")) {
2353 if(type == COMMAND) {
2354 if(!strcmp(text, "end"))
2368 int main (int argc,char ** argv)
2371 processargs(argc, argv);
2372 initLog(0,-1,0,0,-1,verbose);
2375 args_callback_usage(argv[0]);
2379 file = generateTokens(filename);
2381 printf("parser returned error.\n");
2387 while(!noMoreTokens()) {
2390 syntaxerror("command expected");
2391 parseArgumentsForCommand(text);