2 Compiles swf code (.sc) files into .swf files.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/drawer.h"
32 #include "../lib/log.h"
33 #include "../lib/args.h"
40 static char * filename = 0;
41 static char * outputname = "output.swf";
42 static int verbose = 2;
43 static int override_outputname = 0;
45 static struct options_t options[] =
53 int args_callback_option(char*name,char*val)
55 if(!strcmp(name, "V")) {
56 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
59 else if(!strcmp(name, "o")) {
61 override_outputname = 1;
64 else if(!strcmp(name, "v")) {
69 printf("Unknown option: -%s\n", name);
74 int args_callback_longoption(char*name,char*val)
76 return args_long2shortoption(options, name, val);
78 void args_callback_usage(char*name)
80 printf("Usage: %s [-o filename] file.sc\n", name);
81 printf("\t-v , --verbose\t\t\t Be more verbose\n");
82 printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
83 printf("\t-V , --version\t\t\t Print program version and exit\n");
85 int args_callback_command(char*name,char*val)
88 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
95 static struct token_t* file;
104 static void syntaxerror(char*format, ...)
108 va_start(arglist, format);
109 vsprintf(buf, format, arglist);
111 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
115 static void warning(char*format, ...)
119 va_start(arglist, format);
120 vsprintf(buf, format, arglist);
122 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
125 static void readToken()
127 type = file[pos].type;
129 syntaxerror("unexpected end of file");
131 text = file[pos].text;
132 textlen = strlen(text);
133 line = file[pos].line;
134 column = file[pos].column;
136 //printf("---> %d(%s) %s\n", type, type_names[type], text);
139 static void pushBack()
142 if(!pos) syntaxerror("internal error 3");
147 textlen = strlen(text);
150 column = file[p].column;
153 static int noMoreTokens()
155 if(file[pos].type == END)
160 // ------------------------------ swf routines ----------------------------
164 int type; //0=swf, 1=sprite, 2=clip
170 /* for sprites (1): */
176 dictionary_t oldinstances;
181 static int stackpos = 0;
183 static dictionary_t characters;
184 static dictionary_t images;
185 static dictionary_t outlines;
186 static dictionary_t gradients;
187 static char idmap[65536];
188 static TAG*tag = 0; //current tag
190 static int id; //current character id
191 static int currentframe; //current frame in current level
192 static SRECT currentrect; //current bounding box in current level
193 static U16 currentdepth;
194 static dictionary_t instances;
195 static dictionary_t fonts;
196 static dictionary_t sounds;
198 typedef struct _parameters {
200 float scalex, scaley;
208 typedef struct _character {
214 typedef struct _instance {
215 character_t*character;
217 parameters_t parameters;
218 TAG* lastTag; //last tag which set the object
219 U16 lastFrame; //frame lastTag is in
222 typedef struct _outline {
227 typedef struct _gradient {
232 static void character_init(character_t*c)
234 memset(c, 0, sizeof(character_t));
236 static character_t* character_new()
239 c = (character_t*)malloc(sizeof(character_t));
243 static void instance_init(instance_t*i)
245 memset(i, 0, sizeof(instance_t));
247 static instance_t* instance_new()
250 c = (instance_t*)malloc(sizeof(instance_t));
255 static void incrementid()
259 syntaxerror("Out of character ids.");
264 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
266 character_t* c = character_new();
268 c->definingTag = ctag;
271 if(dictionary_lookup(&characters, name))
272 syntaxerror("character %s defined twice", name);
273 dictionary_put2(&characters, name, c);
275 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
277 swf_SetString(tag, name);
278 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
281 swf_SetString(tag, name);
283 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
285 character_t* c = character_new();
286 c->definingTag = ctag;
290 if(dictionary_lookup(&images, name))
291 syntaxerror("image %s defined twice", name);
292 dictionary_put2(&images, name, c);
294 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
296 instance_t* i = instance_new();
299 //swf_GetMatrix(0, &i->matrix);
300 if(dictionary_lookup(&instances, name))
301 syntaxerror("object %s defined twice", name);
302 dictionary_put2(&instances, name, i);
306 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
309 p->scalex = scalex; p->scaley = scaley;
310 p->pin = pin; p->pivot = pivot;
311 p->rotate = rotate; p->cxform = cxform;
315 static void parameters_clear(parameters_t*p)
318 p->scalex = 1.0; p->scaley = 1.0;
319 p->pin.x = 1; p->pin.y = 0;
320 p->pivot.x = 0; p->pivot.y = 0;
323 swf_GetCXForm(0, &p->cxform, 1);
326 static void makeMatrix(MATRIX*m, parameters_t*p)
335 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
336 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
337 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
338 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
340 m->sx = (int)(sx*65536+0.5);
341 m->r1 = (int)(r1*65536+0.5);
342 m->r0 = (int)(r0*65536+0.5);
343 m->sy = (int)(sy*65536+0.5);
347 h = swf_TurnPoint(p->pin, m);
352 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
357 r = swf_TurnRect(i->character->size, &m);
358 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
359 currentrect.xmax == 0 && currentrect.ymax == 0)
362 swf_ExpandRect2(¤trect, &r);
366 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
368 SWF*swf = (SWF*)malloc(sizeof(SWF));
371 syntaxerror(".swf blocks can't be nested");
373 memset(swf, 0, sizeof(swf));
374 swf->fileVersion = version;
376 swf->frameRate = fps;
377 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
378 swf->compressed = compress;
379 swf_SetRGB(tag,&background);
381 if(stackpos==sizeof(stack)/sizeof(stack[0]))
382 syntaxerror("too many levels of recursion");
384 dictionary_init(&characters);
385 dictionary_init(&images);
386 dictionary_init(&outlines);
387 dictionary_init(&gradients);
388 dictionary_init(&instances);
389 dictionary_init(&fonts);
390 dictionary_init(&sounds);
392 memset(&stack[stackpos], 0, sizeof(stack[0]));
393 stack[stackpos].type = 0;
394 stack[stackpos].filename = strdup(name);
395 stack[stackpos].swf = swf;
396 stack[stackpos].oldframe = -1;
401 memset(¤trect, 0, sizeof(currentrect));
404 memset(idmap, 0, sizeof(idmap));
408 void s_sprite(char*name)
410 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
411 swf_SetU16(tag, id); //id
412 swf_SetU16(tag, 0); //frames
414 memset(&stack[stackpos], 0, sizeof(stack[0]));
415 stack[stackpos].type = 1;
416 stack[stackpos].oldframe = currentframe;
417 stack[stackpos].olddepth = currentdepth;
418 stack[stackpos].oldrect = currentrect;
419 stack[stackpos].oldinstances = instances;
420 stack[stackpos].tag = tag;
421 stack[stackpos].id = id;
422 stack[stackpos].name = strdup(name);
424 /* FIXME: those four fields should be bundled together */
425 dictionary_init(&instances);
428 memset(¤trect, 0, sizeof(currentrect));
434 TAG* removeFromTo(TAG*from, TAG*to)
436 TAG*save = from->prev;
438 TAG*next = from->next;
446 static void s_endSprite()
448 SRECT r = currentrect;
450 if(stack[stackpos].cut)
451 tag = removeFromTo(stack[stackpos].cut, tag);
455 /* TODO: before clearing, prepend "<spritename>." to names and
456 copy into old instances dict */
457 dictionary_clear(&instances);
459 currentframe = stack[stackpos].oldframe;
460 currentrect = stack[stackpos].oldrect;
461 currentdepth = stack[stackpos].olddepth;
462 instances = stack[stackpos].oldinstances;
464 tag = swf_InsertTag(tag, ST_END);
466 tag = stack[stackpos].tag;
469 syntaxerror("internal error(7)");
471 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
472 free(stack[stackpos].name);
475 static void s_endSWF()
481 if(stack[stackpos].cut)
482 tag = removeFromTo(stack[stackpos].cut, tag);
486 swf = stack[stackpos].swf;
487 filename = stack[stackpos].filename;
489 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
491 tag = swf_InsertTag(tag, ST_END);
493 swf_OptimizeTagOrder(swf);
495 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
496 swf->movieSize = currentrect; /* "autocrop" */
498 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
499 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
500 swf->movieSize.ymax += 20;
503 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
505 syntaxerror("couldn't create output file %s", filename);
508 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
510 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
514 dictionary_clear(&instances);
515 dictionary_clear(&characters);
516 dictionary_clear(&images);
517 dictionary_clear(&outlines);
518 dictionary_clear(&gradients);
519 dictionary_clear(&fonts);
520 dictionary_clear(&sounds);
530 if(stack[stackpos-1].type == 0)
531 syntaxerror("End of file encountered in .flash block");
532 if(stack[stackpos-1].type == 1)
533 syntaxerror("End of file encountered in .sprite block");
534 if(stack[stackpos-1].type == 2)
535 syntaxerror("End of file encountered in .clip block");
544 void s_frame(int nr, int cut)
549 for(t=currentframe;t<nr;t++) {
550 tag = swf_InsertTag(tag, ST_SHOWFRAME);
555 syntaxerror("Can't cut, frame empty");
557 stack[stackpos].cut = tag;
563 int parseColor2(char*str, RGBA*color);
565 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
570 if(texture[0] == '#') {
571 parseColor2(texture, &color);
572 return swf_ShapeAddSolidFillStyle(s, &color);
573 } else if((image = dictionary_lookup(&images, texture))) {
575 swf_GetMatrix(0, &m);
576 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
577 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
580 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
581 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
582 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
584 swf_GetMatrix(0, &m);
585 m.sx = (r->xmax - r->xmin)*2;
586 m.sy = (r->ymax - r->ymin)*2;
587 m.tx = r->xmin + (r->xmax - r->xmin)/2;
588 m.ty = r->ymin + (r->ymax - r->ymin)/2;
589 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
590 } else if (parseColor2(texture, &color)) {
591 return swf_ShapeAddSolidFillStyle(s, &color);
593 syntaxerror("not a color/fillstyle: %s", texture);
598 RGBA black={r:0,g:0,b:0,a:0};
599 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
608 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
610 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
613 fs1 = addFillStyle(s, &r2, texture);
616 r.xmin = r2.xmin-linewidth-linewidth/2;
617 r.ymin = r2.ymin-linewidth-linewidth/2;
618 r.xmax = r2.xmax+linewidth+linewidth/2;
619 r.ymax = r2.ymax+linewidth+linewidth/2;
621 swf_SetShapeHeader(tag,s);
622 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
623 swf_ShapeSetLine(tag,s,width,0);
624 swf_ShapeSetLine(tag,s,0,height);
625 swf_ShapeSetLine(tag,s,-width,0);
626 swf_ShapeSetLine(tag,s,0,-height);
627 swf_ShapeSetEnd(tag);
630 s_addcharacter(name, id, tag, r);
634 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
640 outline = dictionary_lookup(&outlines, outlinename);
642 syntaxerror("outline %s not defined", outlinename);
646 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
648 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
650 fs1 = addFillStyle(s, &r2, texture);
652 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
654 rect.xmin = r2.xmin-linewidth-linewidth/2;
655 rect.ymin = r2.ymin-linewidth-linewidth/2;
656 rect.xmax = r2.xmax+linewidth+linewidth/2;
657 rect.ymax = r2.ymax+linewidth+linewidth/2;
659 swf_SetRect(tag,&rect);
660 swf_SetShapeStyles(tag, s);
661 swf_SetShapeBits(tag, outline->shape); //does not count bits!
662 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
665 s_addcharacter(name, id, tag, rect);
669 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
674 r2.xmin = r2.ymin = 0;
678 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
680 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
682 fs1 = addFillStyle(s, &r2, texture);
684 rect.xmin = r2.xmin-linewidth-linewidth/2;
685 rect.ymin = r2.ymin-linewidth-linewidth/2;
686 rect.xmax = r2.xmax+linewidth+linewidth/2;
687 rect.ymax = r2.ymax+linewidth+linewidth/2;
689 swf_SetRect(tag,&rect);
690 swf_SetShapeHeader(tag,s);
691 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
692 swf_ShapeSetCircle(tag, s, r,r,r,r);
693 swf_ShapeSetEnd(tag);
696 s_addcharacter(name, id, tag, rect);
700 void s_textshape(char*name, char*fontname, char*_text)
703 U8*text = (U8*)_text;
707 font = dictionary_lookup(&fonts, fontname);
709 syntaxerror("font \"%s\" not known!", fontname);
711 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
712 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
713 s_box(name, 0, 0, black, 20, 0);
716 g = font->ascii2glyph[text[0]];
718 outline = malloc(sizeof(outline_t));
719 memset(outline, 0, sizeof(outline_t));
720 outline->shape = font->glyph[g].shape;
721 outline->bbox = font->layout->bounds[g];
725 swf_Shape11DrawerInit(&draw, 0);
726 swf_DrawText(&draw, font, _text);
728 outline->shape = swf_ShapeDrawerToShape(&draw);
729 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
733 if(dictionary_lookup(&outlines, name))
734 syntaxerror("outline %s defined twice", name);
735 dictionary_put2(&outlines, name, outline);
738 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
743 font = dictionary_lookup(&fonts, fontname);
745 syntaxerror("font \"%s\" not known!", fontname);
747 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
749 if(!font->numchars) {
750 s_box(name, 0, 0, black, 20, 0);
753 r = swf_SetDefineText(tag, font, &color, text, size);
755 s_addcharacter(name, id, tag, r);
759 /* type: either "jpeg" or "png"
761 void s_image(char*name, char*type, char*filename, int quality)
763 /* an image is actually two folded: 1st bitmap, 2nd character.
764 Both of them can be used separately */
766 /* step 1: the bitmap */
771 warning("image type \"png\" not supported yet!");
772 s_box(name, 0, 0, black, 20, 0);
775 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
776 swf_SetU16(tag, imageID);
778 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
779 syntaxerror("Image \"%s\" not found, or contains errors", filename);
782 swf_GetJPEGSize(filename, &width, &height);
789 s_addimage(name, id, tag, r);
792 /* step 2: the character */
793 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
795 swf_ShapeSetBitmapRect(tag, imageID, width, height);
797 s_addcharacter(name, id, tag, r);
801 void dumpSWF(SWF*swf)
803 TAG* tag = swf->firstTag;
804 printf("vvvvvvvvvvvvvvvvvvvvv\n");
806 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
809 printf("^^^^^^^^^^^^^^^^^^^^^\n");
812 void s_font(char*name, char*filename)
817 font = swf_LoadFont(filename);
819 /*f = open(filename,O_RDONLY|O_BINARY);
821 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
822 font = (SWFFONT*)malloc(sizeof(SWFFONT));
823 memset(font, 0, sizeof(SWFFONT));
824 dictionary_put2(&fonts, name, font);
828 if (swf_ReadSWF(f,&swf)>=0) {
829 swf_FontExtract(&swf, 0x4e46, &font);
834 syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
839 /* fix the layout. Only needed for old fonts */
841 for(t=0;t<font->numchars;t++) {
842 font->glyph[t].advance = 0;
845 swf_FontCreateLayout(font);
849 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
850 swf_FontSetDefine2(tag, font);
853 if(dictionary_lookup(&fonts, name))
854 syntaxerror("font %s defined twice", name);
855 dictionary_put2(&fonts, name, font);
860 typedef struct _sound_t
866 void s_sound(char*name, char*filename)
868 struct WAV wav, wav2;
873 if(!readWAV(filename, &wav)) {
874 warning("Couldn't read wav file \"%s\"", filename);
878 convertWAV2mono(&wav, &wav2, 44100);
879 samples = (U16*)wav2.data;
880 numsamples = wav2.size/2;
884 tag = swf_InsertTag(tag, ST_DEFINESOUND);
885 swf_SetU16(tag, id); //id
886 swf_SetSoundDefine(tag, samples, numsamples);
888 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
892 if(dictionary_lookup(&sounds, name))
893 syntaxerror("sound %s defined twice", name);
894 dictionary_put2(&sounds, name, sound);
902 static char* gradient_getToken(const char**p)
906 while(**p && strchr(" \t\n\r", **p)) {
910 while(**p && !strchr(" \t\n\r", **p)) {
913 result = malloc((*p)-start+1);
914 memcpy(result,start,(*p)-start+1);
915 result[(*p)-start] = 0;
919 float parsePercent(char*str);
920 RGBA parseColor(char*str);
922 GRADIENT parseGradient(const char*str)
926 memset(&gradient, 0, sizeof(GRADIENT));
928 char*posstr,*colorstr;
931 posstr = gradient_getToken(&p);
934 pos = parsePercent(posstr);
935 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
936 colorstr = gradient_getToken(&p);
937 color = parseColor(colorstr);
938 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
939 warning("gradient record too big- max size is 8, rest ignored");
942 gradient.ratios[gradient.num] = (int)(pos*255.0);
943 gradient.rgba[gradient.num] = color;
951 void s_gradient(char*name, const char*text, int radial)
953 gradient_t* gradient;
954 gradient = malloc(sizeof(gradient_t));
955 memset(gradient, 0, sizeof(gradient_t));
956 gradient->gradient = parseGradient(text);
957 gradient->radial = radial;
959 if(dictionary_lookup(&gradients, name))
960 syntaxerror("gradient %s defined twice", name);
961 dictionary_put2(&gradients, name, gradient);
964 void s_action(const char*text)
967 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
969 tag = swf_InsertTag(tag, ST_DOACTION);
971 swf_ActionSet(tag, a);
976 void s_outline(char*name, char*format, char*source)
985 swf_Shape11DrawerInit(&draw, 0);
986 draw_string(&draw, source);
988 shape = swf_ShapeDrawerToShape(&draw);
989 //shape2 = swf_ShapeToShape2(shape);
990 //bounds = swf_GetShapeBoundingBox(shape2);
991 //swf_Shape2Free(shape2);
992 bounds = swf_ShapeDrawerGetBBox(&draw);
995 outline = (outline_t*)malloc(sizeof(outline_t));
996 memset(outline, 0, sizeof(outline_t));
997 outline->shape = shape;
998 outline->bbox = bounds;
1000 if(dictionary_lookup(&outlines, name))
1001 syntaxerror("outline %s defined twice", name);
1002 dictionary_put2(&outlines, name, outline);
1005 void s_playsound(char*name, int loops, int nomultiple, int stop)
1007 sound_t* sound = dictionary_lookup(&sounds, name);
1010 syntaxerror("Don't know anything about sound \"%s\"", name);
1012 tag = swf_InsertTag(tag, ST_STARTSOUND);
1013 swf_SetU16(tag, sound->id); //id
1014 memset(&info, 0, sizeof(info));
1017 info.nomultiple = nomultiple;
1018 swf_SetSoundInfo(tag, &info);
1021 void s_includeswf(char*name, char*filename)
1029 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1030 f = open(filename,O_RDONLY);
1032 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1033 s_box(name, 0, 0, black, 20, 0);
1036 if (swf_ReadSWF(f,&swf)<0) {
1037 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1038 s_box(name, 0, 0, black, 20, 0);
1043 /* FIXME: The following sets the bounding Box for the character.
1044 It is wrong for two reasons:
1045 a) It may be too small (in case objects in the movie clip at the borders)
1046 b) it may be too big (because the poor movie never got autocropped)
1050 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1051 swf_SetU16(tag, id);
1054 swf_Relocate(&swf, idmap);
1056 ftag = swf.firstTag;
1060 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1061 if(cutout[t] == ftag->id) {
1065 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1067 if(ftag->id == ST_END)
1071 /* We simply dump all tags right after the sprite
1072 header, relying on the fact that swf_OptimizeTagOrder() will
1073 sort things out for us later.
1074 We also rely on the fact that the imported SWF is well-formed.
1076 tag = swf_InsertTag(tag, ftag->id);
1077 swf_SetBlock(tag, ftag->data, ftag->len);
1081 syntaxerror("Included file %s contains errors", filename);
1082 tag = swf_InsertTag(tag, ST_END);
1086 s_addcharacter(name, id, tag, r);
1089 SRECT s_getCharBBox(char*name)
1091 character_t* c = dictionary_lookup(&characters, name);
1092 if(!c) syntaxerror("character '%s' unknown(2)", name);
1095 SRECT s_getInstanceBBox(char*name)
1097 instance_t * i = dictionary_lookup(&instances, name);
1099 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1101 if(!c) syntaxerror("internal error(5)");
1104 parameters_t s_getParameters(char*name)
1106 instance_t * i = dictionary_lookup(&instances, name);
1107 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1108 return i->parameters;
1110 void s_startclip(char*instance, char*character, parameters_t p)
1112 character_t* c = dictionary_lookup(&characters, character);
1116 syntaxerror("character %s not known", character);
1118 i = s_addinstance(instance, c, currentdepth);
1120 m = s_instancepos(i, &p);
1122 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1123 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1124 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1126 i->lastFrame= currentframe;
1128 stack[stackpos].tag = tag;
1129 stack[stackpos].type = 2;
1138 swf_SetTagPos(stack[stackpos].tag, 0);
1139 swf_GetPlaceObject(stack[stackpos].tag, &p);
1140 p.clipdepth = currentdepth;
1141 swf_ClearTag(stack[stackpos].tag);
1142 swf_SetPlaceObject(stack[stackpos].tag, &p);
1146 void s_put(char*instance, char*character, parameters_t p)
1148 character_t* c = dictionary_lookup(&characters, character);
1152 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1155 i = s_addinstance(instance, c, currentdepth);
1157 m = s_instancepos(i, &p);
1159 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1160 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1162 i->lastFrame = currentframe;
1166 void s_jump(char*instance, parameters_t p)
1168 instance_t* i = dictionary_lookup(&instances, instance);
1171 syntaxerror("instance %s not known", instance);
1175 m = s_instancepos(i, &p);
1177 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1178 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1180 i->lastFrame = currentframe;
1183 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1187 if(num==0 || num==1)
1189 ratio = (float)pos/(float)num;
1191 p.x = (p2->x-p1->x)*ratio + p1->x;
1192 p.y = (p2->y-p1->y)*ratio + p1->y;
1193 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1194 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1195 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1196 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1198 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1199 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1200 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1201 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1203 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1204 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1205 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1206 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1208 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1209 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1210 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1211 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1215 void s_change(char*instance, parameters_t p2)
1217 instance_t* i = dictionary_lookup(&instances, instance);
1221 int frame, allframes;
1223 syntaxerror("instance %s not known", instance);
1227 allframes = currentframe - i->lastFrame - 1;
1229 warning(".change ignored. can only .put/.change an object once per frame.");
1233 m = s_instancepos(i, &p2);
1234 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1235 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1238 /* o.k., we got the start and end point set. Now iterate though all the
1239 tags in between, inserting object changes after each new frame */
1242 if(!t) syntaxerror("internal error(6)");
1244 while(frame < allframes) {
1245 if(t->id == ST_SHOWFRAME) {
1250 p = s_interpolate(&p1, &p2, frame, allframes);
1251 m = s_instancepos(i, &p); //needed?
1252 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1253 i->lastFrame = currentframe;
1254 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1256 if(frame == allframes)
1261 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1265 void s_delinstance(char*instance)
1267 instance_t* i = dictionary_lookup(&instances, instance);
1269 syntaxerror("instance %s not known", instance);
1271 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1272 swf_SetU16(tag, i->depth);
1273 dictionary_del(&instances, instance);
1276 void s_qchange(char*instance, parameters_t p)
1283 syntaxerror(".end unexpected");
1284 if(stack[stackpos-1].type == 0)
1286 else if(stack[stackpos-1].type == 1)
1288 else if(stack[stackpos-1].type == 2)
1290 else syntaxerror("internal error 1");
1293 // ------------------------------------------------------------------------
1295 typedef int command_func_t(map_t*args);
1297 SRECT parseBox(char*str)
1300 float xmin, xmax, ymin, ymax;
1301 char*x = strchr(str, 'x');
1303 if(!strcmp(str, "autocrop")) {
1304 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1308 d1 = strchr(x+1, ':');
1310 d2 = strchr(d1+1, ':');
1312 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1316 else if(d1 && !d2) {
1317 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1323 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1328 r.xmin = (SCOORD)(xmin*20);
1329 r.ymin = (SCOORD)(ymin*20);
1330 r.xmax = (SCOORD)(xmax*20);
1331 r.ymax = (SCOORD)(ymax*20);
1334 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1337 float parseFloat(char*str)
1341 int parseInt(char*str)
1346 if(str[0]=='+' || str[0]=='-')
1350 if(str[t]<'0' || str[t]>'9')
1351 syntaxerror("Not an Integer: \"%s\"", str);
1354 int parseTwip(char*str)
1358 if(str[0]=='+' || str[0]=='-') {
1363 dot = strchr(str, '.');
1367 return sign*parseInt(str)*20;
1369 int l=strlen(++dot);
1371 for(s=str;s<dot-1;s++)
1372 if(*s<'0' || *s>'9')
1373 syntaxerror("Not a coordinate: \"%s\"", str);
1375 if(*s<'0' || *s>'9')
1376 syntaxerror("Not a coordinate: \"%s\"", str);
1378 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1379 warning("precision loss: %s converted to twip", str);
1384 return sign*atoi(str)*20;
1386 return sign*atoi(str)*20+atoi(dot)*2;
1388 return sign*atoi(str)*20+atoi(dot)/5;
1393 int isPoint(char*str)
1395 if(strchr(str, '('))
1401 SPOINT parsePoint(char*str)
1405 int l = strlen(str);
1406 char*comma = strchr(str, ',');
1407 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1408 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1409 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1410 p.x = parseTwip(tmp);
1411 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1412 p.y = parseTwip(tmp);
1416 int parseColor2(char*str, RGBA*color)
1418 int l = strlen(str);
1422 struct {unsigned char r,g,b;char*name;} colors[] =
1423 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1424 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1425 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1426 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1427 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1428 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1429 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1430 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1431 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1432 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1433 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1434 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1438 if(str[0]=='#' && (l==7 || l==9)) {
1439 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1441 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1443 color->r = r; color->g = g; color->b = b; color->a = a;
1446 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1447 if(!strcmp(str, colors[t].name)) {
1452 color->r = r; color->g = g; color->b = b; color->a = a;
1458 RGBA parseColor(char*str)
1461 if(!parseColor2(str, &c))
1462 syntaxerror("Expression '%s' is not a color", str);
1466 typedef struct _muladd {
1471 MULADD parseMulAdd(char*str)
1474 char* str2 = (char*)malloc(strlen(str)+5);
1481 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1482 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1483 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1484 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1485 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1486 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1487 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1488 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1489 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1490 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1492 syntaxerror("'%s' is not a valid color transform expression", str);
1494 m.add = (int)(add*256);
1495 m.mul = (int)(mul*256);
1500 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1502 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1503 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1505 if(a<-32768) a=-32768;
1506 if(a>32767) a=32767;
1507 if(m<-32768) m=-32768;
1508 if(m>32767) m=32767;
1514 float parsePercent(char*str)
1516 int l = strlen(str);
1520 return atoi(str)/100.0;
1522 syntaxerror("Expression '%s' is not a percentage", str);
1525 int isPercent(char*str)
1527 return str[strlen(str)-1]=='%';
1529 int parseNewSize(char*str, int size)
1532 return parsePercent(str)*size;
1534 return (int)(atof(str)*20);
1537 int isColor(char*str)
1540 return parseColor2(str, &c);
1543 static char* lu(map_t* args, char*name)
1545 char* value = map_lookup(args, name);
1547 map_dump(args, stdout, "");
1548 syntaxerror("internal error 2: value %s should be set", name);
1553 static int c_flash(map_t*args)
1555 char* name = lu(args, "name");
1556 char* compressstr = lu(args, "compress");
1557 SRECT bbox = parseBox(lu(args, "bbox"));
1558 int version = parseInt(lu(args, "version"));
1559 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1561 RGBA color = parseColor(lu(args, "background"));
1562 if(!strcmp(name, "!default!") || override_outputname)
1565 if(!strcmp(compressstr, "default"))
1566 compress = version==6;
1567 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1569 else if(!strcmp(compressstr, "no"))
1571 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1573 s_swf(name, bbox, version, fps, compress, color);
1576 int isRelative(char*str)
1578 return !strncmp(str, "<plus>", 6) ||
1579 !strncmp(str, "<minus>", 7);
1581 char* getOffset(char*str)
1583 if(!strncmp(str, "<plus>", 6))
1585 if(!strncmp(str, "<minus>", 7))
1587 syntaxerror("internal error (347)");
1590 int getSign(char*str)
1592 if(!strncmp(str, "<plus>", 6))
1594 if(!strncmp(str, "<minus>", 7))
1596 syntaxerror("internal error (348)");
1599 static dictionary_t points;
1600 static mem_t mpoints;
1601 int points_initialized = 0;
1603 SPOINT getPoint(SRECT r, char*name)
1606 if(!strcmp(name, "center")) {
1608 p.x = (r.xmin + r.xmax)/2;
1609 p.y = (r.ymin + r.ymax)/2;
1613 if(points_initialized)
1614 l = (int)dictionary_lookup(&points, name);
1616 syntaxerror("Invalid point: \"%s\".", name);
1619 return *(SPOINT*)&mpoints.buffer[l];
1621 static int c_gradient(map_t*args)
1623 char*name = lu(args, "name");
1624 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1628 syntaxerror("colon (:) expected");
1630 s_gradient(name, text, radial);
1633 static int c_point(map_t*args)
1635 char*name = lu(args, "name");
1639 if(!points_initialized) {
1640 dictionary_init(&points);
1642 points_initialized = 1;
1644 p.x = parseTwip(lu(args, "x"));
1645 p.y = parseTwip(lu(args, "y"));
1646 pos = mem_put(&mpoints, &p, sizeof(p));
1647 string_set(&s1, name);
1649 dictionary_put(&points, s1, (void*)pos);
1652 static int c_play(map_t*args)
1654 char*name = lu(args, "sound");
1655 char*loop = lu(args, "loop");
1656 char*nomultiple = lu(args, "nomultiple");
1658 if(!strcmp(nomultiple, "nomultiple"))
1661 nm = parseInt(nomultiple);
1663 s_playsound(name, parseInt(loop), nm, 0);
1667 static int c_stop(map_t*args)
1669 char*name = lu(args, "sound");
1670 s_playsound(name, 0,0,1);
1674 static int c_placement(map_t*args, int type)
1676 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1679 char* luminancestr = lu(args, "luminance");
1680 char* scalestr = lu(args, "scale");
1681 char* scalexstr = lu(args, "scalex");
1682 char* scaleystr = lu(args, "scaley");
1683 char* rotatestr = lu(args, "rotate");
1684 char* shearstr = lu(args, "shear");
1685 char* xstr="", *pivotstr="";
1686 char* ystr="", *anglestr="";
1687 char*above = lu(args, "above"); /*FIXME*/
1688 char*below = lu(args, "below");
1689 char* rstr = lu(args, "red");
1690 char* gstr = lu(args, "green");
1691 char* bstr = lu(args, "blue");
1692 char* astr = lu(args, "alpha");
1693 char* pinstr = lu(args, "pin");
1702 pivotstr = lu(args, "pivot");
1703 anglestr = lu(args, "angle");
1705 xstr = lu(args, "x");
1706 ystr = lu(args, "y");
1709 luminance = parseMulAdd(luminancestr);
1712 luminance.mul = 256;
1716 if(scalexstr[0]||scaleystr[0])
1717 syntaxerror("scalex/scaley and scale cannot both be set");
1718 scalexstr = scaleystr = scalestr;
1721 if(type == 0 || type == 4) {
1723 character = lu(args, "character");
1724 parameters_clear(&p);
1726 p = s_getParameters(instance);
1731 if(isRelative(xstr)) {
1732 if(type == 0 || type == 4)
1733 syntaxerror("relative x values not allowed for initial put or startclip");
1734 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1736 p.x = parseTwip(xstr);
1740 if(isRelative(ystr)) {
1741 if(type == 0 || type == 4)
1742 syntaxerror("relative y values not allowed for initial put or startclip");
1743 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1745 p.y = parseTwip(ystr);
1749 /* scale, scalex, scaley */
1751 oldbbox = s_getCharBBox(character);
1753 oldbbox = s_getInstanceBBox(instance);
1755 oldwidth = oldbbox.xmax - oldbbox.xmin;
1756 oldheight = oldbbox.ymax - oldbbox.ymin;
1758 if(oldwidth==0) p.scalex = 1.0;
1761 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1765 if(oldheight==0) p.scaley = 1.0;
1768 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1774 if(isRelative(rotatestr)) {
1775 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1777 p.rotate = parseFloat(rotatestr);
1783 if(isRelative(shearstr)) {
1784 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1786 p.shear = parseFloat(shearstr);
1791 if(isPoint(pivotstr))
1792 p.pivot = parsePoint(pivotstr);
1794 p.pivot = getPoint(oldbbox, pivotstr);
1798 p.pin = parsePoint(pinstr);
1800 p.pin = getPoint(oldbbox, pinstr);
1803 /* color transform */
1805 if(rstr[0] || luminancestr[0]) {
1808 r = parseMulAdd(rstr);
1810 r.add = p.cxform.r0;
1811 r.mul = p.cxform.r1;
1813 r = mergeMulAdd(r, luminance);
1814 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1816 if(gstr[0] || luminancestr[0]) {
1819 g = parseMulAdd(gstr);
1821 g.add = p.cxform.g0;
1822 g.mul = p.cxform.g1;
1824 g = mergeMulAdd(g, luminance);
1825 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1827 if(bstr[0] || luminancestr[0]) {
1830 b = parseMulAdd(bstr);
1832 b.add = p.cxform.b0;
1833 b.mul = p.cxform.b1;
1835 b = mergeMulAdd(b, luminance);
1836 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1839 MULADD a = parseMulAdd(astr);
1840 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1844 s_put(instance, character, p);
1846 s_change(instance, p);
1848 s_qchange(instance, p);
1850 s_jump(instance, p);
1852 s_startclip(instance, character, p);
1855 static int c_put(map_t*args)
1857 c_placement(args, 0);
1860 static int c_change(map_t*args)
1862 c_placement(args, 1);
1865 static int c_qchange(map_t*args)
1867 c_placement(args, 2);
1870 static int c_arcchange(map_t*args)
1872 c_placement(args, 2);
1875 static int c_jump(map_t*args)
1877 c_placement(args, 3);
1880 static int c_startclip(map_t*args)
1882 c_placement(args, 4);
1885 static int c_del(map_t*args)
1887 char*instance = lu(args, "name");
1888 s_delinstance(instance);
1891 static int c_end(map_t*args)
1896 static int c_sprite(map_t*args)
1898 char* name = lu(args, "name");
1902 static int c_frame(map_t*args)
1904 char*framestr = lu(args, "n");
1905 char*cutstr = lu(args, "cut");
1908 if(strcmp(cutstr, "no"))
1910 if(isRelative(framestr)) {
1911 frame = s_getframe();
1912 if(getSign(framestr)<0)
1913 syntaxerror("relative frame expressions must be positive");
1914 frame += parseInt(getOffset(framestr));
1917 frame = parseInt(framestr);
1918 if(s_getframe() >= frame
1919 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1920 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1922 s_frame(frame, cut);
1925 static int c_primitive(map_t*args)
1927 char*name = lu(args, "name");
1928 char*command = lu(args, "commandname");
1929 int width=0, height=0, r=0;
1930 int linewidth = parseTwip(lu(args, "line"));
1931 char*colorstr = lu(args, "color");
1932 RGBA color = parseColor(colorstr);
1933 char*fillstr = lu(args, "fill");
1940 if(!strcmp(command, "circle"))
1942 else if(!strcmp(command, "filled"))
1946 width = parseTwip(lu(args, "width"));
1947 height = parseTwip(lu(args, "height"));
1948 } else if (type==1) {
1949 r = parseTwip(lu(args, "r"));
1950 } else if (type==2) {
1951 outline = lu(args, "outline");
1954 if(!strcmp(fillstr, "fill"))
1956 if(!strcmp(fillstr, "none"))
1958 if(width<0 || height<0 || linewidth<0 || r<0)
1959 syntaxerror("values width, height, line, r must be positive");
1961 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1962 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1963 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
1967 static int c_textshape(map_t*args)
1969 char*name = lu(args, "name");
1970 char*text = lu(args, "text");
1971 char*font = lu(args, "font");
1973 s_textshape(name, font, text);
1979 static int c_swf(map_t*args)
1981 char*name = lu(args, "name");
1982 char*filename = lu(args, "filename");
1983 char*command = lu(args, "commandname");
1984 if(!strcmp(command, "shape"))
1985 warning("Please use .swf instead of .shape");
1986 s_includeswf(name, filename);
1990 static int c_font(map_t*args)
1992 char*name = lu(args, "name");
1993 char*filename = lu(args, "filename");
1994 s_font(name, filename);
1998 static int c_sound(map_t*args)
2000 char*name = lu(args, "name");
2001 char*filename = lu(args, "filename");
2002 s_sound(name, filename);
2006 static int c_text(map_t*args)
2008 char*name = lu(args, "name");
2009 char*text = lu(args, "text");
2010 char*font = lu(args, "font");
2011 float size = parsePercent(lu(args, "size"));
2012 RGBA color = parseColor(lu(args, "color"));
2013 s_text(name, font, text, (int)(size*100), color);
2017 static int c_soundtrack(map_t*args)
2022 static int c_image(map_t*args)
2024 char*command = lu(args, "commandname");
2025 char*name = lu(args, "name");
2026 char*filename = lu(args, "filename");
2027 if(!strcmp(command,"jpeg")) {
2028 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2029 s_image(name, "jpeg", filename, quality);
2031 s_image(name, "png", filename, 0);
2036 static int c_outline(map_t*args)
2038 char*name = lu(args, "name");
2039 char*format = lu(args, "format");
2043 syntaxerror("colon (:) expected");
2045 s_outline(name, format, text);
2049 int fakechar(map_t*args)
2051 char*name = lu(args, "name");
2052 s_box(name, 0, 0, black, 20, 0);
2056 static int c_egon(map_t*args) {return fakechar(args);}
2057 static int c_button(map_t*args) {
2060 syntaxerror("colon (:) expected");
2062 return fakechar(args);
2064 static int c_edittext(map_t*args) {return fakechar(args);}
2066 static int c_morphshape(map_t*args) {return fakechar(args);}
2067 static int c_movie(map_t*args) {return fakechar(args);}
2069 static int c_buttonsounds(map_t*args) {return 0;}
2070 static int c_buttonput(map_t*args) {return 0;}
2071 static int c_texture(map_t*args) {return 0;}
2073 static int c_action(map_t*args)
2077 syntaxerror("colon (:) expected");
2086 command_func_t* func;
2089 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2090 {"frame", c_frame, "n=<plus>1 @cut=no"},
2091 // "import" type stuff
2092 {"swf", c_swf, "name filename"},
2093 {"shape", c_swf, "name filename"},
2094 {"jpeg", c_image, "name filename quality=80%"},
2095 {"png", c_image, "name filename"},
2096 {"movie", c_movie, "name filename"},
2097 {"sound", c_sound, "name filename"},
2098 {"font", c_font, "name filename"},
2099 {"soundtrack", c_soundtrack, "filename"},
2101 // generators of primitives
2103 {"point", c_point, "name x=0 y=0"},
2104 {"gradient", c_gradient, "name @radial=0"},
2105 {"outline", c_outline, "name format=simple"},
2106 {"textshape", c_textshape, "name text font"},
2108 // character generators
2109 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2110 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2111 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2113 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2114 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
2115 {"text", c_text, "name text font size=100% color=white"},
2116 {"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"},
2117 {"morphshape", c_morphshape, "name start end"},
2119 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
2122 {"play", c_play, "sound loop=0 @nomultiple=0"},
2123 {"stop", c_stop, "sound"},
2125 // object placement tags
2126 {"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="},
2127 {"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="},
2128 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2129 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2130 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2131 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2132 {"del", c_del, "name"},
2133 // virtual object placement
2134 {"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="},
2135 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2137 // commands which start a block
2138 //startclip (see above)
2139 {"sprite", c_sprite, "name"},
2140 {"action", c_action, ""},
2146 static map_t parseArguments(char*command, char*pattern)
2162 string_set(&t1, "commandname");
2163 string_set(&t2, command);
2164 map_put(&result, t1, t2);
2166 if(!pattern || !*pattern)
2173 if(!strncmp("<i> ", x, 3)) {
2175 if(type == COMMAND || type == RAWDATA) {
2177 syntaxerror("character name expected");
2179 name[pos].str = "instance";
2181 value[pos].str = text;
2182 value[pos].len = strlen(text);
2186 if(type == ASSIGNMENT)
2189 name[pos].str = "character";
2191 value[pos].str = text;
2192 value[pos].len = strlen(text);
2200 isboolean[pos] = (x[0] =='@');
2213 name[pos].len = d-x;
2218 name[pos].len = e-x;
2219 value[pos].str = e+1;
2220 value[pos].len = d-e-1;
2228 /* for(t=0;t<len;t++) {
2229 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2230 isboolean[t]?"(boolean)":"");
2235 if(type == RAWDATA || type == COMMAND) {
2240 // first, search for boolean arguments
2241 for(pos=0;pos<len;pos++)
2243 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2245 if(type == ASSIGNMENT)
2247 value[pos].str = text;
2248 value[pos].len = strlen(text);
2249 /*printf("setting boolean parameter %s (to %s)\n",
2250 strdup_n(name[pos], namelen[pos]),
2251 strdup_n(value[pos], valuelen[pos]));*/
2256 // second, search for normal arguments
2258 for(pos=0;pos<len;pos++)
2260 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2261 (type != ASSIGNMENT && !set[pos])) {
2263 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2265 if(type == ASSIGNMENT)
2268 value[pos].str = text;
2269 value[pos].len = strlen(text);
2271 printf("setting parameter %s (to %s)\n",
2272 strdup_n(name[pos].str, name[pos].len),
2273 strdup_n(value[pos].str, value[pos].len));
2279 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2283 for(t=0;t<len;t++) {
2284 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2287 for(t=0;t<len;t++) {
2288 if(value[t].str && value[t].str[0] == '*') {
2289 //relative default- take value from some other parameter
2291 for(s=0;s<len;s++) {
2292 if(value[s].len == value[t].len-1 &&
2293 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2294 value[t].str = value[s].str;
2297 if(value[t].str == 0) {
2299 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2303 /* ok, now construct the dictionary from the parameters */
2307 map_put(&result, name[t], value[t]);
2311 static void parseArgumentsForCommand(char*command)
2316 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2317 if(!strcmp(arguments[t].command, command)) {
2319 /* ugly hack- will be removed soon (once documentation and .sc generating
2320 utilities have been changed) */
2321 if(!strcmp(command, "swf") && !stackpos) {
2322 warning("Please use .flash instead of .swf- this will be mandatory soon");
2327 args = parseArguments(command, arguments[t].arguments);
2333 syntaxerror("command %s not known", command);
2336 printf(".%s\n", command);fflush(stdout);
2337 map_dump(&args, stdout, "\t");fflush(stdout);
2340 (*arguments[nr].func)(&args);
2342 /*if(!strcmp(command, "button") ||
2343 !strcmp(command, "action")) {
2346 if(type == COMMAND) {
2347 if(!strcmp(text, "end"))
2361 int main (int argc,char ** argv)
2364 processargs(argc, argv);
2365 initLog(0,-1,0,0,-1,verbose);
2368 args_callback_usage(argv[0]);
2371 file = generateTokens(filename);
2373 printf("parser returned error.\n");
2380 while(!noMoreTokens()) {
2383 syntaxerror("command expected");
2384 parseArgumentsForCommand(text);