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;
929 posstr = gradient_getToken(&p);
932 float pos = parsePercent(posstr);
933 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
934 colorstr = gradient_getToken(&p);
935 RGBA color = parseColor(colorstr);
936 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
937 warning("gradient record too big- max size is 8, rest ignored");
940 gradient.ratios[gradient.num] = (int)(pos*255.0);
941 gradient.rgba[gradient.num] = color;
949 void s_gradient(char*name, const char*text, int radial)
951 gradient_t* gradient;
952 gradient = malloc(sizeof(gradient_t));
953 memset(gradient, 0, sizeof(gradient_t));
954 gradient->gradient = parseGradient(text);
955 gradient->radial = radial;
957 if(dictionary_lookup(&gradients, name))
958 syntaxerror("gradient %s defined twice", name);
959 dictionary_put2(&gradients, name, gradient);
962 void s_action(const char*text)
965 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
967 tag = swf_InsertTag(tag, ST_DOACTION);
969 swf_ActionSet(tag, a);
974 void s_outline(char*name, char*format, char*source)
983 swf_Shape11DrawerInit(&draw, 0);
984 draw_string(&draw, source);
986 shape = swf_ShapeDrawerToShape(&draw);
987 //shape2 = swf_ShapeToShape2(shape);
988 //bounds = swf_GetShapeBoundingBox(shape2);
989 //swf_Shape2Free(shape2);
990 bounds = swf_ShapeDrawerGetBBox(&draw);
993 outline = (outline_t*)malloc(sizeof(outline_t));
994 memset(outline, 0, sizeof(outline_t));
995 outline->shape = shape;
996 outline->bbox = bounds;
998 if(dictionary_lookup(&outlines, name))
999 syntaxerror("outline %s defined twice", name);
1000 dictionary_put2(&outlines, name, outline);
1003 void s_playsound(char*name, int loops, int nomultiple, int stop)
1005 sound_t* sound = dictionary_lookup(&sounds, name);
1008 syntaxerror("Don't know anything about sound \"%s\"", name);
1010 tag = swf_InsertTag(tag, ST_STARTSOUND);
1011 swf_SetU16(tag, sound->id); //id
1012 memset(&info, 0, sizeof(info));
1015 info.nomultiple = nomultiple;
1016 swf_SetSoundInfo(tag, &info);
1019 void s_includeswf(char*name, char*filename)
1027 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1028 f = open(filename,O_RDONLY);
1030 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1031 s_box(name, 0, 0, black, 20, 0);
1034 if (swf_ReadSWF(f,&swf)<0) {
1035 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1036 s_box(name, 0, 0, black, 20, 0);
1041 /* FIXME: The following sets the bounding Box for the character.
1042 It is wrong for two reasons:
1043 a) It may be too small (in case objects in the movie clip at the borders)
1044 b) it may be too big (because the poor movie never got autocropped)
1048 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1049 swf_SetU16(tag, id);
1052 swf_Relocate(&swf, idmap);
1054 ftag = swf.firstTag;
1058 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1059 if(cutout[t] == ftag->id) {
1063 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1065 if(ftag->id == ST_END)
1069 /* We simply dump all tags right after the sprite
1070 header, relying on the fact that swf_OptimizeTagOrder() will
1071 sort things out for us later.
1072 We also rely on the fact that the imported SWF is well-formed.
1074 tag = swf_InsertTag(tag, ftag->id);
1075 swf_SetBlock(tag, ftag->data, ftag->len);
1079 syntaxerror("Included file %s contains errors", filename);
1080 tag = swf_InsertTag(tag, ST_END);
1084 s_addcharacter(name, id, tag, r);
1087 SRECT s_getCharBBox(char*name)
1089 character_t* c = dictionary_lookup(&characters, name);
1090 if(!c) syntaxerror("character '%s' unknown(2)", name);
1093 SRECT s_getInstanceBBox(char*name)
1095 instance_t * i = dictionary_lookup(&instances, name);
1097 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1099 if(!c) syntaxerror("internal error(5)");
1102 parameters_t s_getParameters(char*name)
1104 instance_t * i = dictionary_lookup(&instances, name);
1105 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1106 return i->parameters;
1108 void s_startclip(char*instance, char*character, parameters_t p)
1110 character_t* c = dictionary_lookup(&characters, character);
1114 syntaxerror("character %s not known", character);
1116 i = s_addinstance(instance, c, currentdepth);
1118 m = s_instancepos(i, &p);
1120 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1121 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1122 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1124 i->lastFrame= currentframe;
1126 stack[stackpos].tag = tag;
1127 stack[stackpos].type = 2;
1136 swf_SetTagPos(stack[stackpos].tag, 0);
1137 swf_GetPlaceObject(stack[stackpos].tag, &p);
1138 p.clipdepth = currentdepth;
1139 swf_ClearTag(stack[stackpos].tag);
1140 swf_SetPlaceObject(stack[stackpos].tag, &p);
1144 void s_put(char*instance, char*character, parameters_t p)
1146 character_t* c = dictionary_lookup(&characters, character);
1150 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1153 i = s_addinstance(instance, c, currentdepth);
1155 m = s_instancepos(i, &p);
1157 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1158 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1160 i->lastFrame = currentframe;
1164 void s_jump(char*instance, parameters_t p)
1166 instance_t* i = dictionary_lookup(&instances, instance);
1169 syntaxerror("instance %s not known", instance);
1173 m = s_instancepos(i, &p);
1175 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1176 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1178 i->lastFrame = currentframe;
1181 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1185 if(num==0 || num==1)
1187 ratio = (float)pos/(float)num;
1189 p.x = (p2->x-p1->x)*ratio + p1->x;
1190 p.y = (p2->y-p1->y)*ratio + p1->y;
1191 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1192 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1193 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1194 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1196 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1197 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1198 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1199 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1201 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1202 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1203 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1204 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1206 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1207 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1208 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1209 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1213 void s_change(char*instance, parameters_t p2)
1215 instance_t* i = dictionary_lookup(&instances, instance);
1219 int frame, allframes;
1221 syntaxerror("instance %s not known", instance);
1225 allframes = currentframe - i->lastFrame - 1;
1227 warning(".change ignored. can only .put/.change an object once per frame.");
1231 m = s_instancepos(i, &p2);
1232 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1233 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1236 /* o.k., we got the start and end point set. Now iterate though all the
1237 tags in between, inserting object changes after each new frame */
1240 if(!t) syntaxerror("internal error(6)");
1242 while(frame < allframes) {
1243 if(t->id == ST_SHOWFRAME) {
1248 p = s_interpolate(&p1, &p2, frame, allframes);
1249 m = s_instancepos(i, &p); //needed?
1250 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1251 i->lastFrame = currentframe;
1252 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1254 if(frame == allframes)
1259 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1263 void s_delinstance(char*instance)
1265 instance_t* i = dictionary_lookup(&instances, instance);
1267 syntaxerror("instance %s not known", instance);
1269 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1270 swf_SetU16(tag, i->depth);
1271 dictionary_del(&instances, instance);
1274 void s_qchange(char*instance, parameters_t p)
1281 syntaxerror(".end unexpected");
1282 if(stack[stackpos-1].type == 0)
1284 else if(stack[stackpos-1].type == 1)
1286 else if(stack[stackpos-1].type == 2)
1288 else syntaxerror("internal error 1");
1291 // ------------------------------------------------------------------------
1293 typedef int command_func_t(map_t*args);
1295 SRECT parseBox(char*str)
1298 float xmin, xmax, ymin, ymax;
1299 char*x = strchr(str, 'x');
1301 if(!strcmp(str, "autocrop")) {
1302 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1306 d1 = strchr(x+1, ':');
1308 d2 = strchr(d1+1, ':');
1310 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1314 else if(d1 && !d2) {
1315 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1321 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1326 r.xmin = (SCOORD)(xmin*20);
1327 r.ymin = (SCOORD)(ymin*20);
1328 r.xmax = (SCOORD)(xmax*20);
1329 r.ymax = (SCOORD)(ymax*20);
1332 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1335 float parseFloat(char*str)
1339 int parseInt(char*str)
1344 if(str[0]=='+' || str[0]=='-')
1348 if(str[t]<'0' || str[t]>'9')
1349 syntaxerror("Not an Integer: \"%s\"", str);
1352 int parseTwip(char*str)
1356 if(str[0]=='+' || str[0]=='-') {
1361 dot = strchr(str, '.');
1365 return sign*parseInt(str)*20;
1367 int l=strlen(++dot);
1369 for(s=str;s<dot-1;s++)
1370 if(*s<'0' || *s>'9')
1371 syntaxerror("Not a coordinate: \"%s\"", str);
1373 if(*s<'0' || *s>'9')
1374 syntaxerror("Not a coordinate: \"%s\"", str);
1376 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1377 warning("precision loss: %s converted to twip", str);
1382 return sign*atoi(str)*20;
1384 return sign*atoi(str)*20+atoi(dot)*2;
1386 return sign*atoi(str)*20+atoi(dot)/5;
1391 int isPoint(char*str)
1393 if(strchr(str, '('))
1399 SPOINT parsePoint(char*str)
1403 int l = strlen(str);
1404 char*comma = strchr(str, ',');
1405 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1406 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1407 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1408 p.x = parseTwip(tmp);
1409 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1410 p.y = parseTwip(tmp);
1414 int parseColor2(char*str, RGBA*color)
1416 int l = strlen(str);
1420 struct {unsigned char r,g,b;char*name;} colors[] =
1421 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1422 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1423 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1424 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1425 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1426 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1427 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1428 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1429 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1430 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1431 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1432 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1436 if(str[0]=='#' && (l==7 || l==9)) {
1437 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1439 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1441 color->r = r; color->g = g; color->b = b; color->a = a;
1444 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1445 if(!strcmp(str, colors[t].name)) {
1450 color->r = r; color->g = g; color->b = b; color->a = a;
1456 RGBA parseColor(char*str)
1459 if(!parseColor2(str, &c))
1460 syntaxerror("Expression '%s' is not a color", str);
1464 typedef struct _muladd {
1469 MULADD parseMulAdd(char*str)
1472 char* str2 = (char*)malloc(strlen(str)+5);
1479 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1480 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1481 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1482 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1483 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1484 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1485 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1486 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1487 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1488 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1490 syntaxerror("'%s' is not a valid color transform expression", str);
1492 m.add = (int)(add*256);
1493 m.mul = (int)(mul*256);
1498 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1500 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1501 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1503 if(a<-32768) a=-32768;
1504 if(a>32767) a=32767;
1505 if(m<-32768) m=-32768;
1506 if(m>32767) m=32767;
1512 float parsePercent(char*str)
1514 int l = strlen(str);
1518 return atoi(str)/100.0;
1520 syntaxerror("Expression '%s' is not a percentage", str);
1523 int isPercent(char*str)
1525 return str[strlen(str)-1]=='%';
1527 int parseNewSize(char*str, int size)
1530 return parsePercent(str)*size;
1532 return (int)(atof(str)*20);
1535 int isColor(char*str)
1538 return parseColor2(str, &c);
1541 static char* lu(map_t* args, char*name)
1543 char* value = map_lookup(args, name);
1545 map_dump(args, stdout, "");
1546 syntaxerror("internal error 2: value %s should be set", name);
1551 static int c_flash(map_t*args)
1553 char* name = lu(args, "name");
1554 char* compressstr = lu(args, "compress");
1555 SRECT bbox = parseBox(lu(args, "bbox"));
1556 int version = parseInt(lu(args, "version"));
1557 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1559 RGBA color = parseColor(lu(args, "background"));
1560 if(!strcmp(name, "!default!") || override_outputname)
1563 if(!strcmp(compressstr, "default"))
1564 compress = version==6;
1565 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1567 else if(!strcmp(compressstr, "no"))
1569 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1571 s_swf(name, bbox, version, fps, compress, color);
1574 int isRelative(char*str)
1576 return !strncmp(str, "<plus>", 6) ||
1577 !strncmp(str, "<minus>", 7);
1579 char* getOffset(char*str)
1581 if(!strncmp(str, "<plus>", 6))
1583 if(!strncmp(str, "<minus>", 7))
1585 syntaxerror("internal error (347)");
1588 int getSign(char*str)
1590 if(!strncmp(str, "<plus>", 6))
1592 if(!strncmp(str, "<minus>", 7))
1594 syntaxerror("internal error (348)");
1597 static dictionary_t points;
1598 static mem_t mpoints;
1599 int points_initialized = 0;
1601 SPOINT getPoint(SRECT r, char*name)
1604 if(!strcmp(name, "center")) {
1606 p.x = (r.xmin + r.xmax)/2;
1607 p.y = (r.ymin + r.ymax)/2;
1611 if(points_initialized)
1612 l = (int)dictionary_lookup(&points, name);
1614 syntaxerror("Invalid point: \"%s\".", name);
1617 return *(SPOINT*)&mpoints.buffer[l];
1619 static int c_gradient(map_t*args)
1621 char*name = lu(args, "name");
1622 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1626 syntaxerror("colon (:) expected");
1628 s_gradient(name, text, radial);
1631 static int c_point(map_t*args)
1633 char*name = lu(args, "name");
1637 if(!points_initialized) {
1638 dictionary_init(&points);
1640 points_initialized = 1;
1642 p.x = parseTwip(lu(args, "x"));
1643 p.y = parseTwip(lu(args, "y"));
1644 pos = mem_put(&mpoints, &p, sizeof(p));
1645 string_set(&s1, name);
1647 dictionary_put(&points, s1, (void*)pos);
1650 static int c_play(map_t*args)
1652 char*name = lu(args, "sound");
1653 char*loop = lu(args, "loop");
1654 char*nomultiple = lu(args, "nomultiple");
1656 if(!strcmp(nomultiple, "nomultiple"))
1659 nm = parseInt(nomultiple);
1661 s_playsound(name, parseInt(loop), nm, 0);
1665 static int c_stop(map_t*args)
1667 char*name = lu(args, "sound");
1668 s_playsound(name, 0,0,1);
1672 static int c_placement(map_t*args, int type)
1674 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1677 char* luminancestr = lu(args, "luminance");
1678 char* scalestr = lu(args, "scale");
1679 char* scalexstr = lu(args, "scalex");
1680 char* scaleystr = lu(args, "scaley");
1681 char* rotatestr = lu(args, "rotate");
1682 char* shearstr = lu(args, "shear");
1683 char* xstr="", *pivotstr="";
1684 char* ystr="", *anglestr="";
1685 char*above = lu(args, "above"); /*FIXME*/
1686 char*below = lu(args, "below");
1687 char* rstr = lu(args, "red");
1688 char* gstr = lu(args, "green");
1689 char* bstr = lu(args, "blue");
1690 char* astr = lu(args, "alpha");
1691 char* pinstr = lu(args, "pin");
1700 pivotstr = lu(args, "pivot");
1701 anglestr = lu(args, "angle");
1703 xstr = lu(args, "x");
1704 ystr = lu(args, "y");
1707 luminance = parseMulAdd(luminancestr);
1710 luminance.mul = 256;
1714 if(scalexstr[0]||scaleystr[0])
1715 syntaxerror("scalex/scaley and scale cannot both be set");
1716 scalexstr = scaleystr = scalestr;
1719 if(type == 0 || type == 4) {
1721 character = lu(args, "character");
1722 parameters_clear(&p);
1724 p = s_getParameters(instance);
1729 if(isRelative(xstr)) {
1730 if(type == 0 || type == 4)
1731 syntaxerror("relative x values not allowed for initial put or startclip");
1732 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1734 p.x = parseTwip(xstr);
1738 if(isRelative(ystr)) {
1739 if(type == 0 || type == 4)
1740 syntaxerror("relative y values not allowed for initial put or startclip");
1741 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1743 p.y = parseTwip(ystr);
1747 /* scale, scalex, scaley */
1749 oldbbox = s_getCharBBox(character);
1751 oldbbox = s_getInstanceBBox(instance);
1753 oldwidth = oldbbox.xmax - oldbbox.xmin;
1754 oldheight = oldbbox.ymax - oldbbox.ymin;
1756 if(oldwidth==0) p.scalex = 1.0;
1759 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1763 if(oldheight==0) p.scaley = 1.0;
1766 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1772 if(isRelative(rotatestr)) {
1773 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1775 p.rotate = parseFloat(rotatestr);
1781 if(isRelative(shearstr)) {
1782 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1784 p.shear = parseFloat(shearstr);
1789 if(isPoint(pivotstr))
1790 p.pivot = parsePoint(pivotstr);
1792 p.pivot = getPoint(oldbbox, pivotstr);
1796 p.pin = parsePoint(pinstr);
1798 p.pin = getPoint(oldbbox, pinstr);
1801 /* color transform */
1803 if(rstr[0] || luminancestr[0]) {
1806 r = parseMulAdd(rstr);
1808 r.add = p.cxform.r0;
1809 r.mul = p.cxform.r1;
1811 r = mergeMulAdd(r, luminance);
1812 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1814 if(gstr[0] || luminancestr[0]) {
1817 g = parseMulAdd(gstr);
1819 g.add = p.cxform.g0;
1820 g.mul = p.cxform.g1;
1822 g = mergeMulAdd(g, luminance);
1823 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1825 if(bstr[0] || luminancestr[0]) {
1828 b = parseMulAdd(bstr);
1830 b.add = p.cxform.b0;
1831 b.mul = p.cxform.b1;
1833 b = mergeMulAdd(b, luminance);
1834 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1837 MULADD a = parseMulAdd(astr);
1838 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1842 s_put(instance, character, p);
1844 s_change(instance, p);
1846 s_qchange(instance, p);
1848 s_jump(instance, p);
1850 s_startclip(instance, character, p);
1853 static int c_put(map_t*args)
1855 c_placement(args, 0);
1858 static int c_change(map_t*args)
1860 c_placement(args, 1);
1863 static int c_qchange(map_t*args)
1865 c_placement(args, 2);
1868 static int c_arcchange(map_t*args)
1870 c_placement(args, 2);
1873 static int c_jump(map_t*args)
1875 c_placement(args, 3);
1878 static int c_startclip(map_t*args)
1880 c_placement(args, 4);
1883 static int c_del(map_t*args)
1885 char*instance = lu(args, "name");
1886 s_delinstance(instance);
1889 static int c_end(map_t*args)
1894 static int c_sprite(map_t*args)
1896 char* name = lu(args, "name");
1900 static int c_frame(map_t*args)
1902 char*framestr = lu(args, "n");
1903 char*cutstr = lu(args, "cut");
1906 if(strcmp(cutstr, "no"))
1908 if(isRelative(framestr)) {
1909 frame = s_getframe();
1910 if(getSign(framestr)<0)
1911 syntaxerror("relative frame expressions must be positive");
1912 frame += parseInt(getOffset(framestr));
1915 frame = parseInt(framestr);
1916 if(s_getframe() >= frame
1917 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1918 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1920 s_frame(frame, cut);
1923 static int c_primitive(map_t*args)
1925 char*name = lu(args, "name");
1926 char*command = lu(args, "commandname");
1927 int width=0, height=0, r=0;
1928 int linewidth = parseTwip(lu(args, "line"));
1929 char*colorstr = lu(args, "color");
1930 RGBA color = parseColor(colorstr);
1931 char*fillstr = lu(args, "fill");
1938 if(!strcmp(command, "circle"))
1940 else if(!strcmp(command, "filled"))
1944 width = parseTwip(lu(args, "width"));
1945 height = parseTwip(lu(args, "height"));
1946 } else if (type==1) {
1947 r = parseTwip(lu(args, "r"));
1948 } else if (type==2) {
1949 outline = lu(args, "outline");
1952 if(!strcmp(fillstr, "fill"))
1954 if(!strcmp(fillstr, "none"))
1956 if(width<0 || height<0 || linewidth<0 || r<0)
1957 syntaxerror("values width, height, line, r must be positive");
1959 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1960 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1961 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
1965 static int c_textshape(map_t*args)
1967 char*name = lu(args, "name");
1968 char*text = lu(args, "text");
1969 char*font = lu(args, "font");
1971 s_textshape(name, font, text);
1977 static int c_swf(map_t*args)
1979 char*name = lu(args, "name");
1980 char*filename = lu(args, "filename");
1981 char*command = lu(args, "commandname");
1982 if(!strcmp(command, "shape"))
1983 warning("Please use .swf instead of .shape");
1984 s_includeswf(name, filename);
1988 static int c_font(map_t*args)
1990 char*name = lu(args, "name");
1991 char*filename = lu(args, "filename");
1992 s_font(name, filename);
1996 static int c_sound(map_t*args)
1998 char*name = lu(args, "name");
1999 char*filename = lu(args, "filename");
2000 s_sound(name, filename);
2004 static int c_text(map_t*args)
2006 char*name = lu(args, "name");
2007 char*text = lu(args, "text");
2008 char*font = lu(args, "font");
2009 float size = parsePercent(lu(args, "size"));
2010 RGBA color = parseColor(lu(args, "color"));
2011 s_text(name, font, text, (int)(size*100), color);
2015 static int c_soundtrack(map_t*args)
2020 static int c_image(map_t*args)
2022 char*command = lu(args, "commandname");
2023 char*name = lu(args, "name");
2024 char*filename = lu(args, "filename");
2025 if(!strcmp(command,"jpeg")) {
2026 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2027 s_image(name, "jpeg", filename, quality);
2029 s_image(name, "png", filename, 0);
2034 static int c_outline(map_t*args)
2036 char*name = lu(args, "name");
2037 char*format = lu(args, "format");
2041 syntaxerror("colon (:) expected");
2043 s_outline(name, format, text);
2047 int fakechar(map_t*args)
2049 char*name = lu(args, "name");
2050 s_box(name, 0, 0, black, 20, 0);
2054 static int c_egon(map_t*args) {return fakechar(args);}
2055 static int c_button(map_t*args) {
2058 syntaxerror("colon (:) expected");
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)
2075 syntaxerror("colon (:) expected");
2084 command_func_t* func;
2087 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2088 {"frame", c_frame, "n=<plus>1 @cut=no"},
2089 // "import" type stuff
2090 {"swf", c_swf, "name filename"},
2091 {"shape", c_swf, "name filename"},
2092 {"jpeg", c_image, "name filename quality=80%"},
2093 {"png", c_image, "name filename"},
2094 {"movie", c_movie, "name filename"},
2095 {"sound", c_sound, "name filename"},
2096 {"font", c_font, "name filename"},
2097 {"soundtrack", c_soundtrack, "filename"},
2099 // generators of primitives
2101 {"point", c_point, "name x=0 y=0"},
2102 {"gradient", c_gradient, "name @radial=0"},
2103 {"outline", c_outline, "name format=simple"},
2104 {"textshape", c_textshape, "name text font"},
2106 // character generators
2107 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2108 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2109 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2111 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2112 {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
2113 {"text", c_text, "name text font size=100% color=white"},
2114 {"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"},
2115 {"morphshape", c_morphshape, "name start end"},
2117 {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
2120 {"play", c_play, "sound loop=0 @nomultiple=0"},
2121 {"stop", c_stop, "sound"},
2123 // object placement tags
2124 {"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="},
2125 {"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="},
2126 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2127 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2128 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2129 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2130 {"del", c_del, "name"},
2131 // virtual object placement
2132 {"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="},
2133 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2135 // commands which start a block
2136 //startclip (see above)
2137 {"sprite", c_sprite, "name"},
2138 {"action", c_action, ""},
2144 static map_t parseArguments(char*command, char*pattern)
2160 string_set(&t1, "commandname");
2161 string_set(&t2, command);
2162 map_put(&result, t1, t2);
2164 if(!pattern || !*pattern)
2171 if(!strncmp("<i> ", x, 3)) {
2173 if(type == COMMAND || type == RAWDATA) {
2175 syntaxerror("character name expected");
2177 name[pos].str = "instance";
2179 value[pos].str = text;
2180 value[pos].len = strlen(text);
2184 if(type == ASSIGNMENT)
2187 name[pos].str = "character";
2189 value[pos].str = text;
2190 value[pos].len = strlen(text);
2198 isboolean[pos] = (x[0] =='@');
2211 name[pos].len = d-x;
2216 name[pos].len = e-x;
2217 value[pos].str = e+1;
2218 value[pos].len = d-e-1;
2226 /* for(t=0;t<len;t++) {
2227 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2228 isboolean[t]?"(boolean)":"");
2233 if(type == RAWDATA || type == COMMAND) {
2238 // first, search for boolean arguments
2239 for(pos=0;pos<len;pos++)
2241 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2243 if(type == ASSIGNMENT)
2245 value[pos].str = text;
2246 value[pos].len = strlen(text);
2247 /*printf("setting boolean parameter %s (to %s)\n",
2248 strdup_n(name[pos], namelen[pos]),
2249 strdup_n(value[pos], valuelen[pos]));*/
2254 // second, search for normal arguments
2256 for(pos=0;pos<len;pos++)
2258 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2259 (type != ASSIGNMENT && !set[pos])) {
2261 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2263 if(type == ASSIGNMENT)
2266 value[pos].str = text;
2267 value[pos].len = strlen(text);
2269 printf("setting parameter %s (to %s)\n",
2270 strdup_n(name[pos].str, name[pos].len),
2271 strdup_n(value[pos].str, value[pos].len));
2277 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2281 for(t=0;t<len;t++) {
2282 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2285 for(t=0;t<len;t++) {
2286 if(value[t].str && value[t].str[0] == '*') {
2287 //relative default- take value from some other parameter
2289 for(s=0;s<len;s++) {
2290 if(value[s].len == value[t].len-1 &&
2291 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2292 value[t].str = value[s].str;
2295 if(value[t].str == 0) {
2297 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2301 /* ok, now construct the dictionary from the parameters */
2305 map_put(&result, name[t], value[t]);
2309 static void parseArgumentsForCommand(char*command)
2314 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2315 if(!strcmp(arguments[t].command, command)) {
2317 /* ugly hack- will be removed soon (once documentation and .sc generating
2318 utilities have been changed) */
2319 if(!strcmp(command, "swf") && !stackpos) {
2320 warning("Please use .flash instead of .swf- this will be mandatory soon");
2325 args = parseArguments(command, arguments[t].arguments);
2331 syntaxerror("command %s not known", command);
2334 printf(".%s\n", command);fflush(stdout);
2335 map_dump(&args, stdout, "\t");fflush(stdout);
2338 (*arguments[nr].func)(&args);
2340 /*if(!strcmp(command, "button") ||
2341 !strcmp(command, "action")) {
2344 if(type == COMMAND) {
2345 if(!strcmp(text, "end"))
2359 int main (int argc,char ** argv)
2362 processargs(argc, argv);
2363 initLog(0,-1,0,0,-1,verbose);
2366 args_callback_usage(argv[0]);
2369 file = generateTokens(filename);
2371 printf("parser returned error.\n");
2378 while(!noMoreTokens()) {
2381 syntaxerror("command expected");
2382 parseArgumentsForCommand(text);