2 Compiles swf code (.sc) files into .swf files.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/drawer.h"
32 #include "../lib/log.h"
33 #include "../lib/args.h"
40 static char * filename = 0;
41 static char * outputname = "output.swf";
42 static int verbose = 2;
43 static int override_outputname = 0;
45 static struct options_t options[] = {
53 int args_callback_option(char*name,char*val)
55 if(!strcmp(name, "V")) {
56 printf("swfc - part of %s %s\n", PACKAGE, VERSION);
59 else if(!strcmp(name, "o")) {
61 override_outputname = 1;
64 else if(!strcmp(name, "v")) {
69 printf("Unknown option: -%s\n", name);
74 int args_callback_longoption(char*name,char*val)
76 return args_long2shortoption(options, name, val);
78 void args_callback_usage(char *name)
81 printf("Usage: %s [-o file.swf] file.sc\n", name);
83 printf("-h , --help Print short help message and exit\n");
84 printf("-V , --version Print version info and exit\n");
85 printf("-v , --verbose Increase verbosity. \n");
86 printf("-o , --output <filename> Set output file to <filename>.\n");
89 int args_callback_command(char*name,char*val)
92 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
99 static struct token_t* file;
108 static void syntaxerror(char*format, ...)
112 va_start(arglist, format);
113 vsprintf(buf, format, arglist);
115 printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
119 static void warning(char*format, ...)
123 va_start(arglist, format);
124 vsprintf(buf, format, arglist);
126 printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
129 static void readToken()
131 type = file[pos].type;
133 syntaxerror("unexpected end of file");
135 text = file[pos].text;
136 textlen = strlen(text);
137 line = file[pos].line;
138 column = file[pos].column;
140 //printf("---> %d(%s) %s\n", type, type_names[type], text);
143 static void pushBack()
146 if(!pos) syntaxerror("internal error 3");
151 textlen = strlen(text);
154 column = file[p].column;
157 static int noMoreTokens()
159 if(file[pos].type == END)
164 // ------------------------------ swf routines ----------------------------
168 int type; //0=swf, 1=sprite, 2=clip, 3=button
174 /* for sprites (1): */
180 dictionary_t oldinstances;
185 static int stackpos = 0;
187 static dictionary_t characters;
188 static dictionary_t images;
189 static dictionary_t outlines;
190 static dictionary_t gradients;
191 static char idmap[65536];
192 static TAG*tag = 0; //current tag
194 static int id; //current character id
195 static int currentframe; //current frame in current level
196 static SRECT currentrect; //current bounding box in current level
197 static U16 currentdepth;
198 static dictionary_t instances;
199 static dictionary_t fonts;
200 static dictionary_t sounds;
202 typedef struct _parameters {
204 float scalex, scaley;
212 typedef struct _character {
218 typedef struct _instance {
219 character_t*character;
221 parameters_t parameters;
222 TAG* lastTag; //last tag which set the object
223 U16 lastFrame; //frame lastTag is in
226 typedef struct _outline {
231 typedef struct _gradient {
236 static void character_init(character_t*c)
238 memset(c, 0, sizeof(character_t));
240 static character_t* character_new()
243 c = (character_t*)malloc(sizeof(character_t));
247 static void instance_init(instance_t*i)
249 memset(i, 0, sizeof(instance_t));
251 static instance_t* instance_new()
254 c = (instance_t*)malloc(sizeof(instance_t));
259 static void incrementid()
263 syntaxerror("Out of character ids.");
268 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
270 character_t* c = character_new();
272 c->definingTag = ctag;
275 if(dictionary_lookup(&characters, name))
276 syntaxerror("character %s defined twice", name);
277 dictionary_put2(&characters, name, c);
279 tag = swf_InsertTag(tag, ST_NAMECHARACTER);
281 swf_SetString(tag, name);
282 tag = swf_InsertTag(tag, ST_EXPORTASSETS);
285 swf_SetString(tag, name);
287 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
289 character_t* c = character_new();
290 c->definingTag = ctag;
294 if(dictionary_lookup(&images, name))
295 syntaxerror("image %s defined twice", name);
296 dictionary_put2(&images, name, c);
298 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
300 instance_t* i = instance_new();
303 //swf_GetMatrix(0, &i->matrix);
304 if(dictionary_lookup(&instances, name))
305 syntaxerror("object %s defined twice", name);
306 dictionary_put2(&instances, name, i);
310 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
313 p->scalex = scalex; p->scaley = scaley;
314 p->pin = pin; p->pivot = pivot;
315 p->rotate = rotate; p->cxform = cxform;
319 static void parameters_clear(parameters_t*p)
322 p->scalex = 1.0; p->scaley = 1.0;
323 p->pin.x = 1; p->pin.y = 0;
324 p->pivot.x = 0; p->pivot.y = 0;
327 swf_GetCXForm(0, &p->cxform, 1);
330 static void makeMatrix(MATRIX*m, parameters_t*p)
339 sx = p->scalex*cos(p->rotate/360*2*3.14159265358979);
340 r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
341 r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979);
342 sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
344 m->sx = (int)(sx*65536+0.5);
345 m->r1 = (int)(r1*65536+0.5);
346 m->r0 = (int)(r0*65536+0.5);
347 m->sy = (int)(sy*65536+0.5);
351 h = swf_TurnPoint(p->pin, m);
356 static MATRIX s_instancepos(SRECT rect, parameters_t*p)
361 r = swf_TurnRect(rect, &m);
362 if(currentrect.xmin == 0 && currentrect.ymin == 0 &&
363 currentrect.xmax == 0 && currentrect.ymax == 0)
366 swf_ExpandRect2(¤trect, &r);
370 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
372 SWF*swf = (SWF*)malloc(sizeof(SWF));
375 syntaxerror(".swf blocks can't be nested");
377 memset(swf, 0, sizeof(swf));
378 swf->fileVersion = version;
380 swf->frameRate = fps;
381 swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
382 swf->compressed = compress;
383 swf_SetRGB(tag,&background);
385 if(stackpos==sizeof(stack)/sizeof(stack[0]))
386 syntaxerror("too many levels of recursion");
388 dictionary_init(&characters);
389 dictionary_init(&images);
390 dictionary_init(&outlines);
391 dictionary_init(&gradients);
392 dictionary_init(&instances);
393 dictionary_init(&fonts);
394 dictionary_init(&sounds);
396 memset(&stack[stackpos], 0, sizeof(stack[0]));
397 stack[stackpos].type = 0;
398 stack[stackpos].filename = strdup(name);
399 stack[stackpos].swf = swf;
400 stack[stackpos].oldframe = -1;
405 memset(¤trect, 0, sizeof(currentrect));
408 memset(idmap, 0, sizeof(idmap));
412 void s_sprite(char*name)
414 tag = swf_InsertTag(tag, ST_DEFINESPRITE);
415 swf_SetU16(tag, id); //id
416 swf_SetU16(tag, 0); //frames
418 memset(&stack[stackpos], 0, sizeof(stack[0]));
419 stack[stackpos].type = 1;
420 stack[stackpos].oldframe = currentframe;
421 stack[stackpos].olddepth = currentdepth;
422 stack[stackpos].oldrect = currentrect;
423 stack[stackpos].oldinstances = instances;
424 stack[stackpos].tag = tag;
425 stack[stackpos].id = id;
426 stack[stackpos].name = strdup(name);
428 /* FIXME: those four fields should be bundled together */
429 dictionary_init(&instances);
432 memset(¤trect, 0, sizeof(currentrect));
438 typedef struct _buttonrecord
446 typedef struct _button
450 buttonrecord_t records[4];
453 static button_t mybutton;
455 void s_button(char*name)
457 tag = swf_InsertTag(tag, ST_DEFINEBUTTON2);
458 swf_SetU16(tag, id); //id
459 swf_ButtonSetFlags(tag, 0); //menu=no
461 memset(&mybutton, 0, sizeof(mybutton));
463 memset(&stack[stackpos], 0, sizeof(stack[0]));
464 stack[stackpos].type = 3;
465 stack[stackpos].tag = tag;
466 stack[stackpos].id = id;
467 stack[stackpos].name = strdup(name);
468 stack[stackpos].oldrect = currentrect;
469 memset(¤trect, 0, sizeof(currentrect));
474 void s_buttonput(char*character, char*as, parameters_t p)
476 character_t* c = dictionary_lookup(&characters, character);
481 if(!stackpos || (stack[stackpos-1].type != 3)) {
482 syntaxerror(".show may only appear in .button");
485 syntaxerror("character %s not known (in .shape %s)", character, character);
487 if(mybutton.endofshapes) {
488 syntaxerror("a .do may not precede a .show", character, character);
491 m = s_instancepos(c->size, &p);
499 if(*s==',' || *s==0) {
500 if(!strncmp(o,"idle",s-o)) mybutton.records[0]=r;
501 else if(!strncmp(o,"shape",s-o)) mybutton.records[0]=r;
502 else if(!strncmp(o,"hover",s-o)) mybutton.records[1]=r;
503 else if(!strncmp(o,"pressed",s-o)) mybutton.records[2]=r;
504 else if(!strncmp(o,"area",s-o)) mybutton.records[3]=r;
505 else syntaxerror("unknown \"as\" argument: \"%s\"", strdup_n(o,s-o));
512 swf_DumpMatrix(stdout,&mybutton.records[0].matrix);
515 static void setbuttonrecords(TAG*tag)
517 int flags[] = {BS_UP,BS_OVER,BS_DOWN,BS_HIT};
518 if(!mybutton.endofshapes) {
521 if(!mybutton.records[3].set) {
522 memcpy(&mybutton.records[3], &mybutton.records[0], sizeof(buttonrecord_t));
526 if(mybutton.records[t].set) {
527 swf_ButtonSetRecord(tag,flags[t],mybutton.records[t].id,1,&mybutton.records[t].matrix,&mybutton.records[t].cxform);
530 swf_SetU8(tag,0); // end of button records
531 mybutton.endofshapes = 1;
535 void s_buttonaction(int flags, char*action)
541 setbuttonrecords(stack[stackpos-1].tag);
543 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
545 syntaxerror("Couldn't compile ActionScript");
548 swf_ButtonSetCondition(stack[stackpos-1].tag, flags);
549 swf_ActionSet(stack[stackpos-1].tag, a);
550 mybutton.nr_actions++;
555 static void s_endButton()
558 setbuttonrecords(stack[stackpos-1].tag);
561 swf_ButtonPostProcess(stack[stackpos].tag, mybutton.nr_actions);
565 tag = stack[stackpos].tag;
566 currentrect = stack[stackpos].oldrect;
568 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
569 free(stack[stackpos].name);
572 TAG* removeFromTo(TAG*from, TAG*to)
574 TAG*save = from->prev;
576 TAG*next = from->next;
584 static void s_endSprite()
586 SRECT r = currentrect;
588 if(stack[stackpos].cut)
589 tag = removeFromTo(stack[stackpos].cut, tag);
593 /* TODO: before clearing, prepend "<spritename>." to names and
594 copy into old instances dict */
595 dictionary_clear(&instances);
597 currentframe = stack[stackpos].oldframe;
598 currentrect = stack[stackpos].oldrect;
599 currentdepth = stack[stackpos].olddepth;
600 instances = stack[stackpos].oldinstances;
602 tag = swf_InsertTag(tag, ST_END);
604 tag = stack[stackpos].tag;
607 syntaxerror("internal error(7)");
609 s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
610 free(stack[stackpos].name);
613 static void s_endSWF()
619 if(stack[stackpos].cut)
620 tag = removeFromTo(stack[stackpos].cut, tag);
624 swf = stack[stackpos].swf;
625 filename = stack[stackpos].filename;
627 //tag = swf_InsertTag(tag, ST_SHOWFRAME); //?
629 tag = swf_InsertTag(tag, ST_END);
631 swf_OptimizeTagOrder(swf);
633 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
634 swf->movieSize = currentrect; /* "autocrop" */
637 if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
638 swf->movieSize.xmax += 20; /* 1 by 1 pixels */
639 swf->movieSize.ymax += 20;
642 fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
644 syntaxerror("couldn't create output file %s", filename);
647 {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
649 {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
653 dictionary_clear(&instances);
654 dictionary_clear(&characters);
655 dictionary_clear(&images);
656 dictionary_clear(&outlines);
657 dictionary_clear(&gradients);
658 dictionary_clear(&fonts);
659 dictionary_clear(&sounds);
669 if(stack[stackpos-1].type == 0)
670 syntaxerror("End of file encountered in .flash block");
671 if(stack[stackpos-1].type == 1)
672 syntaxerror("End of file encountered in .sprite block");
673 if(stack[stackpos-1].type == 2)
674 syntaxerror("End of file encountered in .clip block");
683 void s_frame(int nr, int cut)
688 for(t=currentframe;t<nr;t++) {
689 tag = swf_InsertTag(tag, ST_SHOWFRAME);
694 syntaxerror("Can't cut, frame empty");
696 stack[stackpos].cut = tag;
702 int parseColor2(char*str, RGBA*color);
704 int addFillStyle(SHAPE*s, SRECT*r, char*texture)
709 if(texture[0] == '#') {
710 parseColor2(texture, &color);
711 return swf_ShapeAddSolidFillStyle(s, &color);
712 } else if((image = dictionary_lookup(&images, texture))) {
714 swf_GetMatrix(0, &m);
715 m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
716 m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
719 return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
720 } /*else if ((texture = dictionary_lookup(&textures, texture))) {
721 } */ else if ((gradient = dictionary_lookup(&gradients, texture))) {
723 swf_GetMatrix(0, &m);
724 m.sx = (r->xmax - r->xmin)*2;
725 m.sy = (r->ymax - r->ymin)*2;
726 m.tx = r->xmin + (r->xmax - r->xmin)/2;
727 m.ty = r->ymin + (r->ymax - r->ymin)/2;
728 return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
729 } else if (parseColor2(texture, &color)) {
730 return swf_ShapeAddSolidFillStyle(s, &color);
732 syntaxerror("not a color/fillstyle: %s", texture);
737 RGBA black={r:0,g:0,b:0,a:0};
738 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
747 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
749 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
752 fs1 = addFillStyle(s, &r2, texture);
755 r.xmin = r2.xmin-linewidth-linewidth/2;
756 r.ymin = r2.ymin-linewidth-linewidth/2;
757 r.xmax = r2.xmax+linewidth+linewidth/2;
758 r.ymax = r2.ymax+linewidth+linewidth/2;
760 swf_SetShapeHeader(tag,s);
761 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
762 swf_ShapeSetLine(tag,s,width,0);
763 swf_ShapeSetLine(tag,s,0,height);
764 swf_ShapeSetLine(tag,s,-width,0);
765 swf_ShapeSetLine(tag,s,0,-height);
766 swf_ShapeSetEnd(tag);
769 s_addcharacter(name, id, tag, r);
773 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
779 outline = dictionary_lookup(&outlines, outlinename);
781 syntaxerror("outline %s not defined", outlinename);
785 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
787 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
789 fs1 = addFillStyle(s, &r2, texture);
791 syntaxerror("non filled outlines not yet supported- please supply a fill=<color/texture> argument");
793 rect.xmin = r2.xmin-linewidth-linewidth/2;
794 rect.ymin = r2.ymin-linewidth-linewidth/2;
795 rect.xmax = r2.xmax+linewidth+linewidth/2;
796 rect.ymax = r2.ymax+linewidth+linewidth/2;
798 swf_SetRect(tag,&rect);
799 swf_SetShapeStyles(tag, s);
800 swf_SetShapeBits(tag, outline->shape); //does not count bits!
801 swf_SetBlock(tag, outline->shape->data, (outline->shape->bitlen+7)/8);
804 s_addcharacter(name, id, tag, rect);
808 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
813 r2.xmin = r2.ymin = 0;
817 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
819 ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
821 fs1 = addFillStyle(s, &r2, texture);
823 rect.xmin = r2.xmin-linewidth-linewidth/2;
824 rect.ymin = r2.ymin-linewidth-linewidth/2;
825 rect.xmax = r2.xmax+linewidth+linewidth/2;
826 rect.ymax = r2.ymax+linewidth+linewidth/2;
828 swf_SetRect(tag,&rect);
829 swf_SetShapeHeader(tag,s);
830 swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
831 swf_ShapeSetCircle(tag, s, r,r,r,r);
832 swf_ShapeSetEnd(tag);
835 s_addcharacter(name, id, tag, rect);
839 void s_textshape(char*name, char*fontname, char*_text)
842 U8*text = (U8*)_text;
846 font = dictionary_lookup(&fonts, fontname);
848 syntaxerror("font \"%s\" not known!", fontname);
850 if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
851 warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
852 s_box(name, 0, 0, black, 20, 0);
855 g = font->ascii2glyph[text[0]];
857 outline = malloc(sizeof(outline_t));
858 memset(outline, 0, sizeof(outline_t));
859 outline->shape = font->glyph[g].shape;
860 outline->bbox = font->layout->bounds[g];
864 swf_Shape11DrawerInit(&draw, 0);
865 swf_DrawText(&draw, font, _text);
867 outline->shape = swf_ShapeDrawerToShape(&draw);
868 outline->bbox = swf_ShapeDrawerGetBBox(&draw);
872 if(dictionary_lookup(&outlines, name))
873 syntaxerror("outline %s defined twice", name);
874 dictionary_put2(&outlines, name, outline);
877 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
882 font = dictionary_lookup(&fonts, fontname);
884 syntaxerror("font \"%s\" not known!", fontname);
886 tag = swf_InsertTag(tag, ST_DEFINETEXT2);
888 if(!font->numchars) {
889 s_box(name, 0, 0, black, 20, 0);
892 r = swf_SetDefineText(tag, font, &color, text, size);
894 s_addcharacter(name, id, tag, r);
898 /* type: either "jpeg" or "png"
900 void s_image(char*name, char*type, char*filename, int quality)
902 /* an image is actually two folded: 1st bitmap, 2nd character.
903 Both of them can be used separately */
905 /* step 1: the bitmap */
910 warning("image type \"png\" not supported yet!");
911 s_box(name, 0, 0, black, 20, 0);
916 warning("no jpeg support compiled in");
917 s_box(name, 0, 0, black, 20, 0);
920 tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
921 swf_SetU16(tag, imageID);
923 if(swf_SetJPEGBits(tag, filename, quality) < 0) {
924 syntaxerror("Image \"%s\" not found, or contains errors", filename);
927 swf_GetJPEGSize(filename, &width, &height);
934 s_addimage(name, id, tag, r);
939 /* step 2: the character */
940 tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
942 swf_ShapeSetBitmapRect(tag, imageID, width, height);
944 s_addcharacter(name, id, tag, r);
948 void dumpSWF(SWF*swf)
950 TAG* tag = swf->firstTag;
951 printf("vvvvvvvvvvvvvvvvvvvvv\n");
953 printf("%8d %s\n", tag->len, swf_TagGetName(tag));
956 printf("^^^^^^^^^^^^^^^^^^^^^\n");
959 void s_font(char*name, char*filename)
962 font = swf_LoadFont(filename);
965 warning("Couldn't open font file \"%s\"", filename);
966 font = (SWFFONT*)malloc(sizeof(SWFFONT));
967 memset(font, 0, sizeof(SWFFONT));
968 dictionary_put2(&fonts, name, font);
974 /* fix the layout. Only needed for old fonts */
976 for(t=0;t<font->numchars;t++) {
977 font->glyph[t].advance = 0;
980 swf_FontCreateLayout(font);
984 tag = swf_InsertTag(tag, ST_DEFINEFONT2);
985 swf_FontSetDefine2(tag, font);
988 if(dictionary_lookup(&fonts, name))
989 syntaxerror("font %s defined twice", name);
990 dictionary_put2(&fonts, name, font);
995 typedef struct _sound_t
1001 void s_sound(char*name, char*filename)
1003 struct WAV wav, wav2;
1008 if(!readWAV(filename, &wav)) {
1009 warning("Couldn't read wav file \"%s\"", filename);
1013 convertWAV2mono(&wav, &wav2, 44100);
1014 samples = (U16*)wav2.data;
1015 numsamples = wav2.size/2;
1019 tag = swf_InsertTag(tag, ST_DEFINESOUND);
1020 swf_SetU16(tag, id); //id
1021 swf_SetSoundDefine(tag, samples, numsamples);
1023 sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1027 if(dictionary_lookup(&sounds, name))
1028 syntaxerror("sound %s defined twice", name);
1029 dictionary_put2(&sounds, name, sound);
1037 static char* gradient_getToken(const char**p)
1041 while(**p && strchr(" \t\n\r", **p)) {
1045 while(**p && !strchr(" \t\n\r", **p)) {
1048 result = malloc((*p)-start+1);
1049 memcpy(result,start,(*p)-start+1);
1050 result[(*p)-start] = 0;
1054 float parsePercent(char*str);
1055 RGBA parseColor(char*str);
1057 GRADIENT parseGradient(const char*str)
1060 const char* p = str;
1061 memset(&gradient, 0, sizeof(GRADIENT));
1063 char*posstr,*colorstr;
1066 posstr = gradient_getToken(&p);
1069 pos = parsePercent(posstr);
1070 if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1071 colorstr = gradient_getToken(&p);
1072 color = parseColor(colorstr);
1073 if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
1074 warning("gradient record too big- max size is 8, rest ignored");
1077 gradient.ratios[gradient.num] = (int)(pos*255.0);
1078 gradient.rgba[gradient.num] = color;
1086 void s_gradient(char*name, const char*text, int radial)
1088 gradient_t* gradient;
1089 gradient = malloc(sizeof(gradient_t));
1090 memset(gradient, 0, sizeof(gradient_t));
1091 gradient->gradient = parseGradient(text);
1092 gradient->radial = radial;
1094 if(dictionary_lookup(&gradients, name))
1095 syntaxerror("gradient %s defined twice", name);
1096 dictionary_put2(&gradients, name, gradient);
1099 void s_action(const char*text)
1102 a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1104 syntaxerror("Couldn't compile ActionScript");
1107 tag = swf_InsertTag(tag, ST_DOACTION);
1109 swf_ActionSet(tag, a);
1114 int s_swf3action(char*name, char*action)
1117 instance_t* object = dictionary_lookup(&instances, name);
1121 a = action_SetTarget(0, name);
1122 if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1123 else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1124 else if(!strcmp(action, "stop")) a = action_Stop(a);
1125 else if(!strcmp(action, "play")) a = action_Play(a);
1126 a = action_SetTarget(a, "");
1129 tag = swf_InsertTag(tag, ST_DOACTION);
1130 swf_ActionSet(tag, a);
1135 void s_outline(char*name, char*format, char*source)
1144 swf_Shape11DrawerInit(&draw, 0);
1145 draw_string(&draw, source);
1147 shape = swf_ShapeDrawerToShape(&draw);
1148 //shape2 = swf_ShapeToShape2(shape);
1149 //bounds = swf_GetShapeBoundingBox(shape2);
1150 //swf_Shape2Free(shape2);
1151 bounds = swf_ShapeDrawerGetBBox(&draw);
1152 draw.dealloc(&draw);
1154 outline = (outline_t*)malloc(sizeof(outline_t));
1155 memset(outline, 0, sizeof(outline_t));
1156 outline->shape = shape;
1157 outline->bbox = bounds;
1159 if(dictionary_lookup(&outlines, name))
1160 syntaxerror("outline %s defined twice", name);
1161 dictionary_put2(&outlines, name, outline);
1164 int s_playsound(char*name, int loops, int nomultiple, int stop)
1166 sound_t* sound = dictionary_lookup(&sounds, name);
1171 tag = swf_InsertTag(tag, ST_STARTSOUND);
1172 swf_SetU16(tag, sound->id); //id
1173 memset(&info, 0, sizeof(info));
1176 info.nomultiple = nomultiple;
1177 swf_SetSoundInfo(tag, &info);
1181 void s_includeswf(char*name, char*filename)
1189 U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1190 f = open(filename,O_RDONLY);
1192 warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1193 s_box(name, 0, 0, black, 20, 0);
1196 if (swf_ReadSWF(f,&swf)<0) {
1197 warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1198 s_box(name, 0, 0, black, 20, 0);
1203 /* FIXME: The following sets the bounding Box for the character.
1204 It is wrong for two reasons:
1205 a) It may be too small (in case objects in the movie clip at the borders)
1206 b) it may be too big (because the poor movie never got autocropped)
1210 s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1211 swf_SetU16(tag, id);
1214 swf_Relocate(&swf, idmap);
1216 ftag = swf.firstTag;
1220 for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1221 if(cutout[t] == ftag->id) {
1225 if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1227 if(ftag->id == ST_END)
1231 /* We simply dump all tags right after the sprite
1232 header, relying on the fact that swf_OptimizeTagOrder() will
1233 sort things out for us later.
1234 We also rely on the fact that the imported SWF is well-formed.
1236 tag = swf_InsertTag(tag, ftag->id);
1237 swf_SetBlock(tag, ftag->data, ftag->len);
1241 syntaxerror("Included file %s contains errors", filename);
1242 tag = swf_InsertTag(tag, ST_END);
1246 s_addcharacter(name, id, tag, r);
1249 SRECT s_getCharBBox(char*name)
1251 character_t* c = dictionary_lookup(&characters, name);
1252 if(!c) syntaxerror("character '%s' unknown(2)", name);
1255 SRECT s_getInstanceBBox(char*name)
1257 instance_t * i = dictionary_lookup(&instances, name);
1259 if(!i) syntaxerror("instance '%s' unknown(4)", name);
1261 if(!c) syntaxerror("internal error(5)");
1264 parameters_t s_getParameters(char*name)
1266 instance_t * i = dictionary_lookup(&instances, name);
1267 if(!i) syntaxerror("instance '%s' unknown(10)", name);
1268 return i->parameters;
1270 void s_startclip(char*instance, char*character, parameters_t p)
1272 character_t* c = dictionary_lookup(&characters, character);
1276 syntaxerror("character %s not known", character);
1278 i = s_addinstance(instance, c, currentdepth);
1280 m = s_instancepos(i->character->size, &p);
1282 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1283 /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1284 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1286 i->lastFrame= currentframe;
1288 stack[stackpos].tag = tag;
1289 stack[stackpos].type = 2;
1298 swf_SetTagPos(stack[stackpos].tag, 0);
1299 swf_GetPlaceObject(stack[stackpos].tag, &p);
1300 p.clipdepth = currentdepth;
1301 swf_ClearTag(stack[stackpos].tag);
1302 swf_SetPlaceObject(stack[stackpos].tag, &p);
1306 void s_put(char*instance, char*character, parameters_t p)
1308 character_t* c = dictionary_lookup(&characters, character);
1312 syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1315 i = s_addinstance(instance, c, currentdepth);
1317 m = s_instancepos(i->character->size, &p);
1319 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1320 swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1322 i->lastFrame = currentframe;
1326 void s_jump(char*instance, parameters_t p)
1328 instance_t* i = dictionary_lookup(&instances, instance);
1331 syntaxerror("instance %s not known", instance);
1335 m = s_instancepos(i->character->size, &p);
1337 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1338 swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1340 i->lastFrame = currentframe;
1343 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1347 if(num==0 || num==1)
1349 ratio = (float)pos/(float)num;
1351 p.x = (p2->x-p1->x)*ratio + p1->x;
1352 p.y = (p2->y-p1->y)*ratio + p1->y;
1353 p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1354 p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1355 p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1356 p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1358 p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1359 p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1360 p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1361 p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1363 p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1364 p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1365 p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1366 p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1368 p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1369 p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1370 p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1371 p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1375 void s_change(char*instance, parameters_t p2)
1377 instance_t* i = dictionary_lookup(&instances, instance);
1381 int frame, allframes;
1383 syntaxerror("instance %s not known", instance);
1387 allframes = currentframe - i->lastFrame - 1;
1389 warning(".change ignored. can only .put/.change an object once per frame.");
1393 m = s_instancepos(i->character->size, &p2);
1394 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1395 swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1398 /* o.k., we got the start and end point set. Now iterate though all the
1399 tags in between, inserting object changes after each new frame */
1402 if(!t) syntaxerror("internal error(6)");
1404 while(frame < allframes) {
1405 if(t->id == ST_SHOWFRAME) {
1410 p = s_interpolate(&p1, &p2, frame, allframes);
1411 m = s_instancepos(i->character->size, &p); //needed?
1412 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1413 i->lastFrame = currentframe;
1414 swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1416 if(frame == allframes)
1421 syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1425 void s_delinstance(char*instance)
1427 instance_t* i = dictionary_lookup(&instances, instance);
1429 syntaxerror("instance %s not known", instance);
1431 tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1432 swf_SetU16(tag, i->depth);
1433 dictionary_del(&instances, instance);
1436 void s_qchange(char*instance, parameters_t p)
1443 syntaxerror(".end unexpected");
1444 if(stack[stackpos-1].type == 0)
1446 else if(stack[stackpos-1].type == 1)
1448 else if(stack[stackpos-1].type == 2)
1450 else if(stack[stackpos-1].type == 3)
1452 else syntaxerror("internal error 1");
1455 // ------------------------------------------------------------------------
1457 typedef int command_func_t(map_t*args);
1459 SRECT parseBox(char*str)
1462 float xmin, xmax, ymin, ymax;
1463 char*x = strchr(str, 'x');
1465 if(!strcmp(str, "autocrop")) {
1466 r.xmin = r.ymin = r.xmax = r.ymax = 0;
1470 d1 = strchr(x+1, ':');
1472 d2 = strchr(d1+1, ':');
1474 if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1478 else if(d1 && !d2) {
1479 if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1485 if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1490 r.xmin = (SCOORD)(xmin*20);
1491 r.ymin = (SCOORD)(ymin*20);
1492 r.xmax = (SCOORD)(xmax*20);
1493 r.ymax = (SCOORD)(ymax*20);
1496 syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1499 float parseFloat(char*str)
1503 int parseInt(char*str)
1508 if(str[0]=='+' || str[0]=='-')
1512 if(str[t]<'0' || str[t]>'9')
1513 syntaxerror("Not an Integer: \"%s\"", str);
1516 int parseTwip(char*str)
1520 if(str[0]=='+' || str[0]=='-') {
1525 dot = strchr(str, '.');
1529 return sign*parseInt(str)*20;
1531 int l=strlen(++dot);
1533 for(s=str;s<dot-1;s++)
1534 if(*s<'0' || *s>'9')
1535 syntaxerror("Not a coordinate: \"%s\"", str);
1537 if(*s<'0' || *s>'9')
1538 syntaxerror("Not a coordinate: \"%s\"", str);
1540 if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1541 warning("precision loss: %s converted to twip", str);
1546 return sign*atoi(str)*20;
1548 return sign*atoi(str)*20+atoi(dot)*2;
1550 return sign*atoi(str)*20+atoi(dot)/5;
1555 int isPoint(char*str)
1557 if(strchr(str, '('))
1563 SPOINT parsePoint(char*str)
1567 int l = strlen(str);
1568 char*comma = strchr(str, ',');
1569 if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1570 syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1571 strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1572 p.x = parseTwip(tmp);
1573 strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1574 p.y = parseTwip(tmp);
1578 int parseColor2(char*str, RGBA*color)
1580 int l = strlen(str);
1584 struct {unsigned char r,g,b;char*name;} colors[] =
1585 {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1586 {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1587 {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1588 {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1589 {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1590 {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1591 {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1592 {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1593 {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1594 {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1595 {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1596 {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1600 if(str[0]=='#' && (l==7 || l==9)) {
1601 if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1603 if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1605 color->r = r; color->g = g; color->b = b; color->a = a;
1608 for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1609 if(!strcmp(str, colors[t].name)) {
1614 color->r = r; color->g = g; color->b = b; color->a = a;
1620 RGBA parseColor(char*str)
1623 if(!parseColor2(str, &c))
1624 syntaxerror("Expression '%s' is not a color", str);
1628 typedef struct _muladd {
1633 MULADD parseMulAdd(char*str)
1636 char* str2 = (char*)malloc(strlen(str)+5);
1643 if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1644 else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1645 else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1646 else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1647 else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1648 else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1649 else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1650 else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1651 else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1652 else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1654 syntaxerror("'%s' is not a valid color transform expression", str);
1656 m.add = (int)(add*256);
1657 m.mul = (int)(mul*256);
1662 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1664 int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1665 double m = ((double)m1.mul*(double)m2.mul)/256.0;
1667 if(a<-32768) a=-32768;
1668 if(a>32767) a=32767;
1669 if(m<-32768) m=-32768;
1670 if(m>32767) m=32767;
1676 float parsePercent(char*str)
1678 int l = strlen(str);
1682 return atoi(str)/100.0;
1684 syntaxerror("Expression '%s' is not a percentage", str);
1687 int isPercent(char*str)
1689 return str[strlen(str)-1]=='%';
1691 int parseNewSize(char*str, int size)
1694 return parsePercent(str)*size;
1696 return (int)(atof(str)*20);
1699 int isColor(char*str)
1702 return parseColor2(str, &c);
1705 static char* lu(map_t* args, char*name)
1707 char* value = map_lookup(args, name);
1709 map_dump(args, stdout, "");
1710 syntaxerror("internal error 2: value %s should be set", name);
1715 static int c_flash(map_t*args)
1717 char* name = lu(args, "name");
1718 char* compressstr = lu(args, "compress");
1719 SRECT bbox = parseBox(lu(args, "bbox"));
1720 int version = parseInt(lu(args, "version"));
1721 int fps = (int)(parseFloat(lu(args, "fps"))*256);
1723 RGBA color = parseColor(lu(args, "background"));
1724 if(!strcmp(name, "!default!") || override_outputname)
1727 if(!strcmp(compressstr, "default"))
1728 compress = version==6;
1729 else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1731 else if(!strcmp(compressstr, "no"))
1733 else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1735 s_swf(name, bbox, version, fps, compress, color);
1738 int isRelative(char*str)
1740 return !strncmp(str, "<plus>", 6) ||
1741 !strncmp(str, "<minus>", 7);
1743 char* getOffset(char*str)
1745 if(!strncmp(str, "<plus>", 6))
1747 if(!strncmp(str, "<minus>", 7))
1749 syntaxerror("internal error (347)");
1752 int getSign(char*str)
1754 if(!strncmp(str, "<plus>", 6))
1756 if(!strncmp(str, "<minus>", 7))
1758 syntaxerror("internal error (348)");
1761 static dictionary_t points;
1762 static mem_t mpoints;
1763 int points_initialized = 0;
1765 SPOINT getPoint(SRECT r, char*name)
1768 if(!strcmp(name, "center")) {
1770 p.x = (r.xmin + r.xmax)/2;
1771 p.y = (r.ymin + r.ymax)/2;
1775 if(points_initialized)
1776 l = (int)dictionary_lookup(&points, name);
1778 syntaxerror("Invalid point: \"%s\".", name);
1781 return *(SPOINT*)&mpoints.buffer[l];
1783 static int c_gradient(map_t*args)
1785 char*name = lu(args, "name");
1786 int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1790 syntaxerror("colon (:) expected");
1792 s_gradient(name, text, radial);
1795 static int c_point(map_t*args)
1797 char*name = lu(args, "name");
1801 if(!points_initialized) {
1802 dictionary_init(&points);
1804 points_initialized = 1;
1806 p.x = parseTwip(lu(args, "x"));
1807 p.y = parseTwip(lu(args, "y"));
1808 pos = mem_put(&mpoints, &p, sizeof(p));
1809 string_set(&s1, name);
1811 dictionary_put(&points, s1, (void*)pos);
1814 static int c_play(map_t*args)
1816 char*name = lu(args, "name");
1817 char*loop = lu(args, "loop");
1818 char*nomultiple = lu(args, "nomultiple");
1820 if(!strcmp(nomultiple, "nomultiple"))
1823 nm = parseInt(nomultiple);
1825 if(s_playsound(name, parseInt(loop), nm, 0)) {
1827 } else if(s_swf3action(name, "play")) {
1833 static int c_stop(map_t*args)
1835 char*name = lu(args, "name");
1837 if(s_playsound(name, 0,0,1)) {
1839 } else if(s_swf3action(name, "stop")) {
1842 syntaxerror("I don't know anything about sound/movie \"%s\"", name);
1846 static int c_nextframe(map_t*args)
1848 char*name = lu(args, "name");
1850 if(s_swf3action(name, "nextframe")) {
1853 syntaxerror("I don't know anything about movie \"%s\"", name);
1857 static int c_previousframe(map_t*args)
1859 char*name = lu(args, "name");
1861 if(s_swf3action(name, "previousframe")) {
1864 syntaxerror("I don't know anything about movie \"%s\"", name);
1868 static int c_placement(map_t*args, int type)
1870 char*instance = lu(args, (type==0||type==4)?"instance":"name");
1873 char* luminancestr = lu(args, "luminance");
1874 char* scalestr = lu(args, "scale");
1875 char* scalexstr = lu(args, "scalex");
1876 char* scaleystr = lu(args, "scaley");
1877 char* rotatestr = lu(args, "rotate");
1878 char* shearstr = lu(args, "shear");
1879 char* xstr="", *pivotstr="";
1880 char* ystr="", *anglestr="";
1881 char*above = lu(args, "above"); /*FIXME*/
1882 char*below = lu(args, "below");
1883 char* rstr = lu(args, "red");
1884 char* gstr = lu(args, "green");
1885 char* bstr = lu(args, "blue");
1886 char* astr = lu(args, "alpha");
1887 char* pinstr = lu(args, "pin");
1888 char* as = map_lookup(args, "as");
1896 if(type==9) { // (?) .rotate or .arcchange
1897 pivotstr = lu(args, "pivot");
1898 anglestr = lu(args, "angle");
1900 xstr = lu(args, "x");
1901 ystr = lu(args, "y");
1904 luminance = parseMulAdd(luminancestr);
1907 luminance.mul = 256;
1911 if(scalexstr[0]||scaleystr[0])
1912 syntaxerror("scalex/scaley and scale cannot both be set");
1913 scalexstr = scaleystr = scalestr;
1916 if(type == 0 || type == 4) {
1918 character = lu(args, "character");
1919 parameters_clear(&p);
1920 } else if (type == 5) {
1921 character = lu(args, "name");
1922 parameters_clear(&p);
1925 p = s_getParameters(instance);
1930 if(isRelative(xstr)) {
1931 if(type == 0 || type == 4)
1932 syntaxerror("relative x values not allowed for initial put or startclip");
1933 p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1935 p.x = parseTwip(xstr);
1939 if(isRelative(ystr)) {
1940 if(type == 0 || type == 4)
1941 syntaxerror("relative y values not allowed for initial put or startclip");
1942 p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1944 p.y = parseTwip(ystr);
1948 /* scale, scalex, scaley */
1950 oldbbox = s_getCharBBox(character);
1952 oldbbox = s_getInstanceBBox(instance);
1954 oldwidth = oldbbox.xmax - oldbbox.xmin;
1955 oldheight = oldbbox.ymax - oldbbox.ymin;
1957 if(oldwidth==0) p.scalex = 1.0;
1960 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1964 if(oldheight==0) p.scaley = 1.0;
1967 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1973 if(isRelative(rotatestr)) {
1974 p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1976 p.rotate = parseFloat(rotatestr);
1982 if(isRelative(shearstr)) {
1983 p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1985 p.shear = parseFloat(shearstr);
1990 if(isPoint(pivotstr))
1991 p.pivot = parsePoint(pivotstr);
1993 p.pivot = getPoint(oldbbox, pivotstr);
1997 p.pin = parsePoint(pinstr);
1999 p.pin = getPoint(oldbbox, pinstr);
2002 /* color transform */
2004 if(rstr[0] || luminancestr[0]) {
2007 r = parseMulAdd(rstr);
2009 r.add = p.cxform.r0;
2010 r.mul = p.cxform.r1;
2012 r = mergeMulAdd(r, luminance);
2013 p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2015 if(gstr[0] || luminancestr[0]) {
2018 g = parseMulAdd(gstr);
2020 g.add = p.cxform.g0;
2021 g.mul = p.cxform.g1;
2023 g = mergeMulAdd(g, luminance);
2024 p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2026 if(bstr[0] || luminancestr[0]) {
2029 b = parseMulAdd(bstr);
2031 b.add = p.cxform.b0;
2032 b.mul = p.cxform.b1;
2034 b = mergeMulAdd(b, luminance);
2035 p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2038 MULADD a = parseMulAdd(astr);
2039 p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2043 s_put(instance, character, p);
2045 s_change(instance, p);
2047 s_qchange(instance, p);
2049 s_jump(instance, p);
2051 s_startclip(instance, character, p);
2052 else if(type == 5) {
2054 s_buttonput(character, as, p);
2056 s_buttonput(character, "shape", p);
2061 static int c_put(map_t*args)
2063 c_placement(args, 0);
2066 static int c_change(map_t*args)
2068 c_placement(args, 1);
2071 static int c_qchange(map_t*args)
2073 c_placement(args, 2);
2076 static int c_arcchange(map_t*args)
2078 c_placement(args, 2);
2081 static int c_jump(map_t*args)
2083 c_placement(args, 3);
2086 static int c_startclip(map_t*args)
2088 c_placement(args, 4);
2091 static int c_show(map_t*args)
2093 c_placement(args, 5);
2096 static int c_del(map_t*args)
2098 char*instance = lu(args, "name");
2099 s_delinstance(instance);
2102 static int c_end(map_t*args)
2107 static int c_sprite(map_t*args)
2109 char* name = lu(args, "name");
2113 static int c_frame(map_t*args)
2115 char*framestr = lu(args, "n");
2116 char*cutstr = lu(args, "cut");
2119 if(strcmp(cutstr, "no"))
2121 if(isRelative(framestr)) {
2122 frame = s_getframe();
2123 if(getSign(framestr)<0)
2124 syntaxerror("relative frame expressions must be positive");
2125 frame += parseInt(getOffset(framestr));
2128 frame = parseInt(framestr);
2129 if(s_getframe() >= frame
2130 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
2131 syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2133 s_frame(frame, cut);
2136 static int c_primitive(map_t*args)
2138 char*name = lu(args, "name");
2139 char*command = lu(args, "commandname");
2140 int width=0, height=0, r=0;
2141 int linewidth = parseTwip(lu(args, "line"));
2142 char*colorstr = lu(args, "color");
2143 RGBA color = parseColor(colorstr);
2144 char*fillstr = lu(args, "fill");
2151 if(!strcmp(command, "circle"))
2153 else if(!strcmp(command, "filled"))
2157 width = parseTwip(lu(args, "width"));
2158 height = parseTwip(lu(args, "height"));
2159 } else if (type==1) {
2160 r = parseTwip(lu(args, "r"));
2161 } else if (type==2) {
2162 outline = lu(args, "outline");
2165 if(!strcmp(fillstr, "fill"))
2167 if(!strcmp(fillstr, "none"))
2169 if(width<0 || height<0 || linewidth<0 || r<0)
2170 syntaxerror("values width, height, line, r must be positive");
2172 if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2173 else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2174 else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2178 static int c_textshape(map_t*args)
2180 char*name = lu(args, "name");
2181 char*text = lu(args, "text");
2182 char*font = lu(args, "font");
2184 s_textshape(name, font, text);
2188 static int c_swf(map_t*args)
2190 char*name = lu(args, "name");
2191 char*filename = lu(args, "filename");
2192 char*command = lu(args, "commandname");
2193 if(!strcmp(command, "shape"))
2194 warning("Please use .swf instead of .shape");
2195 s_includeswf(name, filename);
2199 static int c_font(map_t*args)
2201 char*name = lu(args, "name");
2202 char*filename = lu(args, "filename");
2203 s_font(name, filename);
2207 static int c_sound(map_t*args)
2209 char*name = lu(args, "name");
2210 char*filename = lu(args, "filename");
2211 s_sound(name, filename);
2215 static int c_text(map_t*args)
2217 char*name = lu(args, "name");
2218 char*text = lu(args, "text");
2219 char*font = lu(args, "font");
2220 float size = parsePercent(lu(args, "size"));
2221 RGBA color = parseColor(lu(args, "color"));
2222 s_text(name, font, text, (int)(size*100), color);
2226 static int c_soundtrack(map_t*args)
2231 static int c_image(map_t*args)
2233 char*command = lu(args, "commandname");
2234 char*name = lu(args, "name");
2235 char*filename = lu(args, "filename");
2236 if(!strcmp(command,"jpeg")) {
2237 int quality = (int)(parsePercent(lu(args, "quality"))*100);
2238 s_image(name, "jpeg", filename, quality);
2240 s_image(name, "png", filename, 0);
2245 static int c_outline(map_t*args)
2247 char*name = lu(args, "name");
2248 char*format = lu(args, "format");
2252 syntaxerror("colon (:) expected");
2254 s_outline(name, format, text);
2258 int fakechar(map_t*args)
2260 char*name = lu(args, "name");
2261 s_box(name, 0, 0, black, 20, 0);
2265 static int c_egon(map_t*args) {return fakechar(args);}
2266 static int c_button(map_t*args) {
2267 char*name = lu(args, "name");
2271 static int current_button_flags = 0;
2272 static int c_on_press(map_t*args)
2274 char*position = lu(args, "position");
2276 if(!strcmp(position, "inside")) {
2277 current_button_flags |= BC_OVERUP_OVERDOWN;
2278 } else if(!strcmp(position, "outside")) {
2279 //current_button_flags |= BC_IDLE_OUTDOWN;
2280 syntaxerror("IDLE_OVERDOWN not supported by SWF");
2281 } else if(!strcmp(position, "anywhere")) {
2282 current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2285 if(type == RAWDATA) {
2287 s_buttonaction(current_button_flags, action);
2288 current_button_flags = 0;
2294 static int c_on_release(map_t*args)
2296 char*position = lu(args, "position");
2298 if(!strcmp(position, "inside")) {
2299 current_button_flags |= BC_OVERDOWN_OVERUP;
2300 } else if(!strcmp(position, "outside")) {
2301 current_button_flags |= BC_OUTDOWN_IDLE;
2302 } else if(!strcmp(position, "anywhere")) {
2303 current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2306 if(type == RAWDATA) {
2308 s_buttonaction(current_button_flags, action);
2309 current_button_flags = 0;
2315 static int c_on_move_in(map_t*args)
2317 char*position = lu(args, "state");
2319 if(!strcmp(position, "pressed")) {
2320 current_button_flags |= BC_OUTDOWN_OVERDOWN;
2321 } else if(!strcmp(position, "not_pressed")) {
2322 current_button_flags |= BC_IDLE_OVERUP;
2323 } else if(!strcmp(position, "any")) {
2324 current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2327 if(type == RAWDATA) {
2329 s_buttonaction(current_button_flags, action);
2330 current_button_flags = 0;
2336 static int c_on_move_out(map_t*args)
2338 char*position = lu(args, "state");
2340 if(!strcmp(position, "pressed")) {
2341 current_button_flags |= BC_OVERDOWN_OUTDOWN;
2342 } else if(!strcmp(position, "not_pressed")) {
2343 current_button_flags |= BC_OVERUP_IDLE;
2344 } else if(!strcmp(position, "any")) {
2345 current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2348 if(type == RAWDATA) {
2350 s_buttonaction(current_button_flags, action);
2351 current_button_flags = 0;
2357 static int c_on_key(map_t*args)
2359 char*key = lu(args, "key");
2361 if(strlen(key)==1) {
2364 current_button_flags |= 0x4000 + (key[0]*0x200);
2366 syntaxerror("invalid character: %c"+key[0]);
2371 <ctrl-x> = 0x200*(x-'a')
2375 syntaxerror("invalid key: %s",key);
2378 if(type == RAWDATA) {
2380 s_buttonaction(current_button_flags, action);
2381 current_button_flags = 0;
2388 static int c_edittext(map_t*args) {return fakechar(args);}
2390 static int c_morphshape(map_t*args) {return fakechar(args);}
2391 static int c_movie(map_t*args) {return fakechar(args);}
2393 static int c_texture(map_t*args) {return 0;}
2395 static int c_action(map_t*args)
2398 if(type != RAWDATA) {
2399 syntaxerror("colon (:) expected");
2409 command_func_t* func;
2412 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2413 {"frame", c_frame, "n=<plus>1 @cut=no"},
2414 // "import" type stuff
2415 {"swf", c_swf, "name filename"},
2416 {"shape", c_swf, "name filename"},
2417 {"jpeg", c_image, "name filename quality=80%"},
2418 {"png", c_image, "name filename"},
2419 {"movie", c_movie, "name filename"},
2420 {"sound", c_sound, "name filename"},
2421 {"font", c_font, "name filename"},
2422 {"soundtrack", c_soundtrack, "filename"},
2424 // generators of primitives
2426 {"point", c_point, "name x=0 y=0"},
2427 {"gradient", c_gradient, "name @radial=0"},
2428 {"outline", c_outline, "name format=simple"},
2429 {"textshape", c_textshape, "name text font"},
2431 // character generators
2432 {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2433 {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2434 {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2436 {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2437 {"text", c_text, "name text font size=100% color=white"},
2438 {"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"},
2439 {"morphshape", c_morphshape, "name start end"},
2440 {"button", c_button, "name"},
2441 {"show", c_show, "name x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below= as="},
2442 {"on_press", c_on_press, "position=inside"},
2443 {"on_release", c_on_release, "position=anywhere"},
2444 {"on_move_in", c_on_move_out, "state=not_pressed"},
2445 {"on_move_out", c_on_move_out, "state=not_pressed"},
2446 {"on_key", c_on_key, "key=any"},
2449 {"play", c_play, "name loop=0 @nomultiple=0"},
2450 {"stop", c_stop, "name"},
2451 {"nextframe", c_nextframe, "name"},
2452 {"previousframe", c_previousframe, "name"},
2454 // object placement tags
2455 {"put", c_put, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2456 {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2457 {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2458 {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2459 {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2460 {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2461 {"del", c_del, "name"},
2462 // virtual object placement
2463 {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2465 // commands which start a block
2466 //startclip (see above)
2467 {"sprite", c_sprite, "name"},
2468 {"action", c_action, ""},
2474 static map_t parseArguments(char*command, char*pattern)
2490 string_set(&t1, "commandname");
2491 string_set(&t2, command);
2492 map_put(&result, t1, t2);
2494 if(!pattern || !*pattern)
2501 if(!strncmp("<i> ", x, 3)) {
2503 if(type == COMMAND || type == RAWDATA) {
2505 syntaxerror("character name expected");
2507 name[pos].str = "instance";
2509 value[pos].str = text;
2510 value[pos].len = strlen(text);
2514 if(type == ASSIGNMENT)
2517 name[pos].str = "character";
2519 value[pos].str = text;
2520 value[pos].len = strlen(text);
2528 isboolean[pos] = (x[0] =='@');
2541 name[pos].len = d-x;
2546 name[pos].len = e-x;
2547 value[pos].str = e+1;
2548 value[pos].len = d-e-1;
2556 /* for(t=0;t<len;t++) {
2557 printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2558 isboolean[t]?"(boolean)":"");
2563 if(type == RAWDATA || type == COMMAND) {
2568 // first, search for boolean arguments
2569 for(pos=0;pos<len;pos++)
2571 if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2573 if(type == ASSIGNMENT)
2575 value[pos].str = text;
2576 value[pos].len = strlen(text);
2577 /*printf("setting boolean parameter %s (to %s)\n",
2578 strdup_n(name[pos], namelen[pos]),
2579 strdup_n(value[pos], valuelen[pos]));*/
2584 // second, search for normal arguments
2586 for(pos=0;pos<len;pos++)
2588 if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2589 (type != ASSIGNMENT && !set[pos])) {
2591 syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2593 if(type == ASSIGNMENT)
2596 value[pos].str = text;
2597 value[pos].len = strlen(text);
2599 printf("setting parameter %s (to %s)\n",
2600 strdup_n(name[pos].str, name[pos].len),
2601 strdup_n(value[pos].str, value[pos].len));
2607 syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2611 for(t=0;t<len;t++) {
2612 printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2615 for(t=0;t<len;t++) {
2616 if(value[t].str && value[t].str[0] == '*') {
2617 //relative default- take value from some other parameter
2619 for(s=0;s<len;s++) {
2620 if(value[s].len == value[t].len-1 &&
2621 !strncmp(&value[t].str[1], value[s].str, value[s].len))
2622 value[t].str = value[s].str;
2625 if(value[t].str == 0) {
2627 syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2631 /* ok, now construct the dictionary from the parameters */
2635 map_put(&result, name[t], value[t]);
2639 static void parseArgumentsForCommand(char*command)
2644 msg("<verbose> parse Command: %s (line %d)", command, line);
2646 for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2647 if(!strcmp(arguments[t].command, command)) {
2649 /* ugly hack- will be removed soon (once documentation and .sc generating
2650 utilities have been changed) */
2651 if(!strcmp(command, "swf") && !stackpos) {
2652 warning("Please use .flash instead of .swf- this will be mandatory soon");
2657 args = parseArguments(command, arguments[t].arguments);
2663 syntaxerror("command %s not known", command);
2665 // catch missing .flash directives at the beginning of a file
2666 if(strcmp(command, "flash") && !stackpos)
2668 syntaxerror("No movie defined- use .flash first");
2672 printf(".%s\n", command);fflush(stdout);
2673 map_dump(&args, stdout, "\t");fflush(stdout);
2676 (*arguments[nr].func)(&args);
2678 /*if(!strcmp(command, "button") ||
2679 !strcmp(command, "action")) {
2682 if(type == COMMAND) {
2683 if(!strcmp(text, "end"))
2697 int main (int argc,char ** argv)
2700 processargs(argc, argv);
2701 initLog(0,-1,0,0,-1,verbose);
2704 args_callback_usage(argv[0]);
2708 file = generateTokens(filename);
2710 printf("parser returned error.\n");
2716 while(!noMoreTokens()) {
2719 syntaxerror("command expected");
2720 parseArgumentsForCommand(text);