c0e8ec153478d297d138ff0cd555366739939158
[swftools.git] / src / swfc.c
1 /* swfc.c
2    Compiles swf code (.sc) files into .swf files.
3
4    Part of the swftools package.
5
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
7
8    This file is distributed under the GPL, see file COPYING for details */
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <string.h>
14 #include <memory.h>
15 #include <errno.h>
16 #define logf logarithmf // logf is also used by ../lib/log.h
17 #include <math.h>
18 #undef logf
19 #include "../config.h"
20 #include "../lib/rfxswf.h"
21 #include "../lib/log.h"
22 #include "../lib/args.h"
23 #include "q.h"
24 #include "parser.h"
25
26 //#define DEBUG
27
28 static char * filename = 0;
29 static char * outputname = "output.swf";
30 static int verbose = 2;
31
32 static struct options_t options[] =
33 {
34  {"o","output"},
35  {"v","verbose"},
36  {"V","version"},
37  {0,0}
38 };
39     
40 int args_callback_option(char*name,char*val)
41 {
42     if(!strcmp(name, "V")) {
43         printf("swfc - part of %s %s\n", PACKAGE, VERSION);
44         exit(0);
45     }
46     else if(!strcmp(name, "o")) {
47         outputname = val;
48         return 1;
49     }
50     else if(!strcmp(name, "v")) {
51         verbose ++;
52         return 0;
53     }
54     else {
55         printf("Unknown option: -%s\n", name);
56         exit(1);
57     }
58     return 0;
59 }
60 int args_callback_longoption(char*name,char*val)
61 {
62     return args_long2shortoption(options, name, val);
63 }
64 void args_callback_usage(char*name)
65 {
66     printf("Usage: %s [-o filename] file.wav\n", name);
67     printf("\t-v , --verbose\t\t\t Be more verbose\n");
68     printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
69     printf("\t-V , --version\t\t\t Print program version and exit\n");
70 }
71 int args_callback_command(char*name,char*val)
72 {
73     if(filename) {
74         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
75                  filename, name);
76     }
77     filename = name;
78     return 0;
79 }
80
81 static struct token_t* file;
82
83 static int pos;
84 static char*text;
85 static int textlen;
86 static int type;
87 static int line;
88 static int column;
89
90 static void syntaxerror(char*format, ...)
91 {
92     char buf[1024];
93     va_list arglist;
94     va_start(arglist, format);
95     vsprintf(buf, format, arglist);
96     va_end(arglist);
97     printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
98     exit(1);
99 }
100
101 static void warning(char*format, ...)
102 {
103     char buf[1024];
104     va_list arglist;
105     va_start(arglist, format);
106     vsprintf(buf, format, arglist);
107     va_end(arglist);
108     printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
109 }
110    
111 static void readToken()
112 {
113     type = file[pos].type;
114     if(type == END) {
115         syntaxerror("unexpected end of file");
116     }
117     text = file[pos].text;
118     textlen = strlen(text);
119     line = file[pos].line;
120     column = file[pos].column;
121     pos++;
122     //printf("---> %d(%s) %s\n", type, type_names[type], text);
123 }
124
125 static void pushBack()
126 {
127     int p;
128     if(!pos) syntaxerror("internal error 3");
129     pos--;
130     p = pos;
131     if(p) p--;
132     text = file[p].text;
133     textlen = strlen(text);
134     type = file[p].type;
135     line = file[p].line;
136     column = file[p].column;
137 }
138
139 static int noMoreTokens()
140 {
141     if(file[pos].type == END)
142         return 1;
143     return 0;
144 }
145
146 // ------------------------------ swf routines ----------------------------
147 struct _character;
148 static struct level
149 {
150    int type; //0=swf, 1=sprite, 2=clip
151
152    /* for swf (0): */
153    SWF*swf;
154    char*filename; 
155
156    /* for sprites (1): */
157    TAG*tag;
158    U16 id;
159    char*name;
160    U16 olddepth;
161    int oldframe;
162    dictionary_t oldinstances;
163    SRECT oldrect;
164
165 } stack[256];
166 static int stackpos = 0;
167
168 static dictionary_t characters;
169 static char idmap[65536];
170 static TAG*tag = 0; //current tag
171
172 static int id; //current character id
173 static int currentframe; //current frame in current level
174 static SRECT currentrect; //current bounding box in current level
175 static U16 currentdepth;
176 static dictionary_t instances;
177 static dictionary_t fonts;
178
179 typedef struct _parameters {
180     int x,y; 
181     float scalex, scaley; 
182     CXFORM cxform;
183     float rotate;
184     SPOINT pivot;
185     SPOINT pin;
186 } parameters_t;
187
188 typedef struct _character {
189     TAG*definingTag;
190     U16 id;
191     SRECT size;
192 } character_t;
193
194 typedef struct _instance {
195     character_t*character;
196     U16 depth;
197     parameters_t parameters;
198     TAG* lastTag; //last tag which set the object
199     U16 lastFrame; //frame lastTag is in
200 } instance_t;
201
202 static void character_init(character_t*c)
203 {
204     memset(c, 0, sizeof(character_t));
205 }
206 static character_t* character_new()
207 {
208     character_t*c;
209     c = (character_t*)malloc(sizeof(character_t));
210     character_init(c);
211     return c;
212 }
213 static void instance_init(instance_t*i)
214 {
215     memset(i, 0, sizeof(instance_t));
216 }
217 static instance_t* instance_new()
218 {
219     instance_t*c;
220     c = (instance_t*)malloc(sizeof(instance_t));
221     instance_init(c);
222     return c;
223 }
224
225 static void incrementid()
226 {
227     while(idmap[++id]) {
228         if(id==65535)
229             syntaxerror("Out of character ids.");
230     }
231     idmap[id] = 1;
232 }
233
234 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
235 {
236     character_t* c = character_new();
237     
238     c->definingTag = ctag;
239     c->id = id;
240     c->size = r;
241     if(dictionary_lookup(&characters, name))
242         syntaxerror("character %s defined twice", name);
243     dictionary_put2(&characters, name, c);
244
245     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
246     swf_SetU16(tag, id);
247     swf_SetString(tag, name);
248     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
249     swf_SetU16(tag, 1);
250     swf_SetU16(tag, id);
251     swf_SetString(tag, name);
252 }
253 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
254 {
255     instance_t* i = instance_new();
256     i->character = c;
257     i->depth = depth;
258     //swf_GetMatrix(0, &i->matrix);
259     if(dictionary_lookup(&instances, name))
260         syntaxerror("object %s defined twice", name);
261     dictionary_put2(&instances, name, i);
262     return i;
263 }
264
265 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, SPOINT pivot, SPOINT pin, CXFORM cxform)
266 {
267     p->x = x; p->y = y; 
268     p->scalex = scalex; p->scaley = scaley;
269     p->pin    = pin; p->pivot = pivot;
270     p->rotate = rotate; p->cxform = cxform;
271 }
272
273 static void parameters_clear(parameters_t*p)
274 {
275     p->x = 0; p->y = 0; 
276     p->scalex = 1.0; p->scaley = 1.0;
277     p->pin.x = 1; p->pin.y = 0;
278     p->pivot.x = 0; p->pivot.y = 0;
279     p->rotate = 0; 
280     swf_GetCXForm(0, &p->cxform, 1);
281 }
282
283 static void makeMatrix(MATRIX*m, parameters_t*p)
284 {
285     SPOINT h;
286     m->sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979)*65535;
287     m->r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)*65535;
288     m->r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979)*65535;
289     m->sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)*65535;
290     m->tx = m->ty = 0;
291
292     h = swf_TurnPoint(p->pin, m);
293     m->tx = p->x - h.x;
294     m->ty = p->y - h.y;
295 }
296
297 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
298 {
299     MATRIX m;
300     SRECT r;
301     makeMatrix(&m, p);
302     r = swf_TurnRect(i->character->size, &m);
303     if(currentrect.xmin == 0 && currentrect.ymin == 0 && 
304        currentrect.xmax == 0 && currentrect.ymax == 0)
305         currentrect = r;
306     else
307         swf_ExpandRect2(&currentrect, &r);
308     return m;
309 }
310
311 void s_swf(char*name, SRECT r, int version, int fps, int compress)
312 {
313     SWF*swf = (SWF*)malloc(sizeof(SWF));
314     RGBA rgb;
315
316     if(stackpos)
317         syntaxerror(".swf blocks can't be nested");
318
319     memset(swf, 0, sizeof(swf));
320     swf->fileVersion = version;
321     swf->movieSize = r;
322     swf->frameRate = fps;
323     swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
324     swf->compressed = compress;
325     rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
326     swf_SetRGB(tag,&rgb);
327     
328     if(stackpos==sizeof(stack)/sizeof(stack[0]))
329         syntaxerror("too many levels of recursion");
330     
331     dictionary_init(&characters);
332     dictionary_init(&instances);
333     dictionary_init(&fonts);
334
335     memset(&stack[stackpos], 0, sizeof(stack[0]));
336     stack[stackpos].type = 0;
337     stack[stackpos].filename = strdup(name);
338     stack[stackpos].swf = swf;
339     stack[stackpos].oldframe = -1;
340     stackpos++;
341     id = 0;
342
343     currentframe = 0;
344     memset(&currentrect, 0, sizeof(currentrect));
345     currentdepth = 1;
346     
347     memset(idmap, 0, sizeof(idmap));
348     incrementid();
349 }
350
351 void s_sprite(char*name)
352 {
353     tag = swf_InsertTag(tag, ST_DEFINESPRITE);
354     swf_SetU16(tag, id); //id
355     swf_SetU16(tag, 0); //frames
356
357     memset(&stack[stackpos], 0, sizeof(stack[0]));
358     stack[stackpos].type = 1;
359     stack[stackpos].oldframe = currentframe;
360     stack[stackpos].olddepth = currentdepth;
361     stack[stackpos].oldrect = currentrect;
362     stack[stackpos].oldinstances = instances;
363     stack[stackpos].tag = tag;
364     stack[stackpos].id = id;
365     stack[stackpos].name = strdup(name);
366    
367     /* FIXME: those four fields should be bundled together */
368     dictionary_init(&instances);
369     currentframe = 0;
370     currentdepth = 1;
371     memset(&currentrect, 0, sizeof(currentrect));
372
373     stackpos++;
374     incrementid();
375 }
376
377 static void s_endSprite()
378 {
379     SRECT r = currentrect;
380     stackpos--;
381    
382     /* TODO: before clearing, prepend "<spritename>." to names and
383              copy into old instances dict */
384     dictionary_clear(&instances);
385
386     currentframe = stack[stackpos].oldframe;
387     currentrect = stack[stackpos].oldrect;
388     currentdepth = stack[stackpos].olddepth;
389     instances = stack[stackpos].oldinstances;
390     tag = swf_InsertTag(tag, ST_END);
391
392     tag = stack[stackpos].tag;
393     swf_FoldSprite(tag);
394     if(tag->next != 0)
395         syntaxerror("internal error(7)");
396
397     s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
398     free(stack[stackpos].name);
399 }
400
401 static void s_endSWF()
402 {
403     int fi;
404     SWF* swf;
405     char*filename;
406     stackpos--;
407
408     swf = stack[stackpos].swf;
409     filename = stack[stackpos].filename;
410     
411     tag = swf_InsertTag(tag, ST_SHOWFRAME);
412     tag = swf_InsertTag(tag, ST_END);
413
414     swf_OptimizeTagOrder(swf);
415
416     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
417         swf->movieSize = currentrect; /* "autocrop" */
418
419     fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
420     if(fi<0) {
421         syntaxerror("couldn't create output file %s", filename);
422     }
423     if(swf->compressed) 
424         {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
425     else
426         {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
427
428     close(fi);
429     
430     dictionary_clear(&instances);
431     dictionary_clear(&characters);
432     dictionary_clear(&fonts);
433
434     swf_FreeTags(swf);
435     free(swf);
436     free(filename);
437 }
438 void s_close()
439 {
440     if(stackpos) {
441         if(stack[stackpos-1].type == 0)
442             syntaxerror("End of file encountered in .swf block");
443         if(stack[stackpos-1].type == 1)
444             syntaxerror("End of file encountered in .sprite block");
445         if(stack[stackpos-1].type == 2)
446             syntaxerror("End of file encountered in .clip block");
447     }
448 }
449
450 int s_getframe()
451 {
452     return currentframe;
453 }
454
455 void s_frame(int nr)
456 {
457     int t;
458     for(t=currentframe;t<nr;t++) {
459         tag = swf_InsertTag(tag, ST_SHOWFRAME);
460     }
461     currentframe = nr;
462 }
463         
464 RGBA black={r:0,g:0,b:0,a:0};
465 void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill)
466 {
467     SRECT r;
468     SHAPE* s;
469     int ls1,fs1=0;
470     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
471     swf_ShapeNew(&s);
472     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
473     if(dofill)
474         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
475     swf_SetU16(tag,id);
476     r.xmin = -linewidth-linewidth/2;
477     r.ymin = -linewidth-linewidth/2;
478     r.xmax = width+linewidth+linewidth/2;
479     r.ymax = height+linewidth+linewidth/2;
480     swf_SetRect(tag,&r);
481     swf_SetShapeHeader(tag,s);
482     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
483     swf_ShapeSetLine(tag,s,width,0);
484     swf_ShapeSetLine(tag,s,0,height);
485     swf_ShapeSetLine(tag,s,-width,0);
486     swf_ShapeSetLine(tag,s,0,-height);
487     swf_ShapeSetEnd(tag);
488     swf_ShapeFree(s);
489    
490     s_addcharacter(name, id, tag, r);
491     incrementid();
492 }
493
494 void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill)
495 {
496     SRECT rect;
497     SHAPE* s;
498     int ls1,fs1=0;
499     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
500     swf_ShapeNew(&s);
501     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
502     if(dofill)
503         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
504     swf_SetU16(tag,id);
505     rect.xmin = -linewidth-linewidth/2;
506     rect.ymin = -linewidth-linewidth/2;
507     rect.xmax = 2*r+linewidth+linewidth/2;
508     rect.ymax = 2*r+linewidth+linewidth/2;
509
510     swf_SetRect(tag,&rect);
511     swf_SetShapeHeader(tag,s);
512     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
513     swf_ShapeSetCircle(tag, s, r,r,r,r);
514     swf_ShapeSetEnd(tag);
515     swf_ShapeFree(s);
516    
517     s_addcharacter(name, id, tag, rect);
518     incrementid();
519 }
520
521 void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill)
522 {
523     SRECT rect;
524     SHAPE* s;
525     int ls1,fs1=0;
526     int g;
527     U8*text = (U8*)_text;
528
529     SWFFONT*font;
530     font = dictionary_lookup(&fonts, fontname);
531     if(!font)
532         syntaxerror("font \"%s\" not known!", fontname);
533     if(!dofill)
534         syntaxerror("textshapes must be filled", fontname);
535
536     if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
537         warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
538         s_box(name, 0, 0, black, 20, black, 0);
539         return;
540     }
541     g = font->ascii2glyph[text[0]];
542     rect = font->layout->bounds[g];
543
544     swf_ShapeNew(&s);
545     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
546     if(dofill)
547         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
548      
549     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
550     swf_SetU16(tag,id);
551     swf_SetRect(tag, &rect);
552     swf_SetShapeStyles(tag, s);
553     swf_SetSimpleShape(tag, font->glyph[g].shape);
554     swf_ShapeFree(s);
555     
556     s_addcharacter(name, id, tag, rect);
557     incrementid();
558 }
559 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
560 {
561     SRECT r;
562
563     SWFFONT*font;
564     font = dictionary_lookup(&fonts, fontname);
565     if(!font)
566         syntaxerror("font \"%s\" not known!", fontname);
567     
568     tag = swf_InsertTag(tag, ST_DEFINETEXT2);
569     swf_SetU16(tag, id);
570     if(!font->numchars) {
571         s_box(name, 0, 0, black, 20, black, 0);
572         return;
573     }
574     r = swf_SetDefineText(tag, font, &color, text, size);
575    
576     s_addcharacter(name, id, tag, r);
577     incrementid();
578 }
579
580 void dumpSWF(SWF*swf)
581 {
582     TAG* tag = swf->firstTag;
583     printf("vvvvvvvvvvvvvvvvvvvvv\n");
584     while(tag) {
585         printf("%8d %s\n", tag->len, swf_TagGetName(tag));
586         tag = tag->next;
587     }
588     printf("^^^^^^^^^^^^^^^^^^^^^\n");
589 }
590     
591 void s_font(char*name, char*filename)
592 {
593     int f;
594     SWF swf;
595     SWFFONT* font;
596     f = open(filename,O_RDONLY);
597     if (f<0) { 
598         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
599         font = (SWFFONT*)malloc(sizeof(SWFFONT));
600         memset(font, 0, sizeof(SWFFONT));
601         dictionary_put2(&fonts, name, font);
602         return;
603     }
604     font = 0;
605     if (swf_ReadSWF(f,&swf)>=0) { 
606         swf_FontExtract(&swf, 0x4e46, &font);
607         swf_FreeTags(&swf);
608     }
609     close(f);
610     if (font==0) { 
611         syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
612     }
613
614     if(0)
615     {
616         /* fix the layout. Only needed for old fonts */
617         int t;
618         for(t=0;t<font->numchars;t++) {
619             font->glyph[t].advance = 0;
620         }
621         font->layout = 0;
622         swf_FontCreateLayout(font);
623     }
624
625     font->id = id;
626     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
627     swf_FontSetDefine2(tag, font);
628     incrementid();
629
630     if(dictionary_lookup(&fonts, name))
631         syntaxerror("font %s defined twice", name);
632     dictionary_put2(&fonts, name, font);
633 }
634
635 void s_shape(char*name, char*filename)
636 {
637     int f;
638     SWF swf;
639     TAG* ftag;
640     SRECT r;
641     TAG* s;
642     int level = 0;
643     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
644     f = open(filename,O_RDONLY);
645     if (f<0) { 
646         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
647         s_box(name, 0, 0, black, 20, black, 0);
648         return;
649     }
650     if (swf_ReadSWF(f,&swf)<0) { 
651         warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
652         s_box(name, 0, 0, black, 20, black, 0);
653         return;
654     }
655     close(f);
656
657     /* FIXME: The following sets the bounding Box for the character. 
658               It is wrong for two reasons:
659               a) It may be too small (in case objects in the movie clip at the borders)
660               b) it may be too big (because the poor movie never got autocropped)
661     */
662     r = swf.movieSize;
663     
664     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
665     swf_SetU16(tag, id);
666     swf_SetU16(tag, 0);
667
668     swf_Relocate(&swf, idmap);
669
670     ftag = swf.firstTag;
671     level = 1;
672     while(ftag) {
673         int t;
674         for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
675             if(cutout[t] == ftag->id) {
676                 ftag = ftag->next;
677                 continue;
678             }
679         if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
680             level++;
681         if(ftag->id == ST_END)
682             level--;
683         if(!level)
684             break;
685         /* We simply dump all tags right after the sprite
686            header, relying on the fact that swf_OptimizeTagOrder() will
687            sort things out for us later. 
688            We also rely on the fact that the imported SWF is well-formed.
689          */
690         tag = swf_InsertTag(tag, ftag->id);
691         swf_SetBlock(tag, ftag->data, ftag->len);
692         ftag = ftag->next;
693     }
694     if(!ftag)
695         syntaxerror("Included file %s contains errors", filename);
696     tag = swf_InsertTag(tag, ST_END);
697
698     swf_FreeTags(&swf);
699
700     s_addcharacter(name, id, tag, r);
701     incrementid();
702 }
703 SRECT s_getCharBBox(char*name)
704 {
705     character_t* c = dictionary_lookup(&characters, name);
706     if(!c) syntaxerror("character '%s' unknown(2)", name);
707     return c->size;
708 }
709 SRECT s_getInstanceBBox(char*name)
710 {
711     instance_t * i = dictionary_lookup(&instances, name);
712     character_t * c;
713     if(!i) syntaxerror("instance '%s' unknown(4)", name);
714     c = i->character;
715     if(!c) syntaxerror("internal error(5)");
716     return c->size;
717 }
718 parameters_t s_getParameters(char*name)
719 {
720     instance_t * i = dictionary_lookup(&instances, name);
721     if(!i) syntaxerror("instance '%s' unknown(10)", name);
722     return i->parameters;
723 }
724 void s_startclip(char*instance, char*character, parameters_t p)
725 {
726     character_t* c = dictionary_lookup(&characters, character);
727     instance_t* i;
728     MATRIX m;
729     if(!c) {
730         syntaxerror("character %s not known", character);
731     }
732     i = s_addinstance(instance, c, currentdepth);
733     i->parameters = p;
734     m = s_instancepos(i, &p);
735     
736     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
737     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
738     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
739     i->lastTag = tag;
740     i->lastFrame= currentframe;
741
742     stack[stackpos].tag = tag;
743     stack[stackpos].type = 2;
744     stackpos++;
745
746     currentdepth++;
747 }
748 void s_endClip()
749 {
750     SWFPLACEOBJECT p;
751     stackpos--;
752     swf_SetTagPos(stack[stackpos].tag, 0);
753     swf_GetPlaceObject(stack[stackpos].tag, &p);
754     p.clipdepth = currentdepth;
755     swf_ClearTag(stack[stackpos].tag);
756     swf_SetPlaceObject(stack[stackpos].tag, &p);
757     currentdepth++;
758 }
759
760 void s_put(char*instance, char*character, parameters_t p)
761 {
762     character_t* c = dictionary_lookup(&characters, character);
763     instance_t* i;
764     MATRIX m;
765     if(!c) {
766         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
767     }
768     
769     i = s_addinstance(instance, c, currentdepth);
770     i->parameters = p;
771     m = s_instancepos(i, &p);
772     
773     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
774     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
775     i->lastTag = tag;
776     i->lastFrame = currentframe;
777     currentdepth++;
778 }
779
780 void s_jump(char*instance, parameters_t p)
781 {
782     instance_t* i = dictionary_lookup(&instances, instance);
783     MATRIX m;
784     if(!i) {
785         syntaxerror("instance %s not known", instance);
786     }
787
788     i->parameters = p;
789     m = s_instancepos(i, &p);
790
791     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
792     swf_ObjectMove(tag, i->depth, &m, &p.cxform);
793     i->lastTag = tag;
794     i->lastFrame = currentframe;
795 }
796
797 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
798 {
799     parameters_t p;
800     float ratio;
801     if(num==0 || num==1)
802         return *p1;
803     ratio = (float)pos/(float)num;
804     
805     p.x = (p2->x-p1->x)*ratio + p1->x;
806     p.y = (p2->y-p1->y)*ratio + p1->y;
807     p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
808     p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
809     p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
810
811     p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
812     p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
813     p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
814     p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
815
816     p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
817     p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
818     p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
819     p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
820
821     p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
822     p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
823     p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
824     p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
825     return p;
826 }
827
828 void s_change(char*instance, parameters_t p2)
829 {
830     instance_t* i = dictionary_lookup(&instances, instance);
831     MATRIX m;
832     parameters_t p1;
833     TAG*t;
834     int frame, allframes;
835     if(!i) {
836         syntaxerror("instance %s not known", instance);
837     }
838     p1 = i->parameters;
839     
840     allframes = currentframe - i->lastFrame - 1;
841     if(!allframes) {
842         warning(".change ignored. can only .put/.change an object once per frame.");
843         return;
844     }
845     
846     m = s_instancepos(i, &p2);
847     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
848     swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
849     i->parameters = p2;
850
851     /* o.k., we got the start and end point set. Now iterate though all the
852        tags in between, inserting object changes after each new frame */
853     t = i->lastTag;
854     i->lastTag = tag;
855     if(!t) syntaxerror("internal error(6)");
856     frame = 0;
857     while(frame < allframes) {
858         if(t->id == ST_SHOWFRAME) {
859             parameters_t p;
860             MATRIX m;
861             TAG*lt;
862             frame ++;
863             p = s_interpolate(&p1, &p2, frame, allframes);
864             m = s_instancepos(i, &p); //needed?
865             lt = swf_InsertTag(t, ST_PLACEOBJECT2);
866             i->lastFrame = currentframe;
867             swf_ObjectMove(lt, i->depth, &m, &p.cxform);
868             t = lt;
869             if(frame == allframes)
870                 break;
871         }
872         t = t->next;
873         if(!t) 
874             syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
875     }
876 }
877
878 void s_delinstance(char*instance)
879 {
880     instance_t* i = dictionary_lookup(&instances, instance);
881     if(!i) {
882         syntaxerror("instance %s not known", instance);
883     }
884     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
885     swf_SetU16(tag, i->depth);
886     dictionary_del(&instances, instance);
887 }
888
889 void s_qchange(char*instance, parameters_t p)
890 {
891 }
892
893 void s_end()
894 {
895     if(!stackpos)
896         syntaxerror(".end unexpected");
897     if(stack[stackpos-1].type == 0)
898         s_endSWF();
899     else if(stack[stackpos-1].type == 1)
900         s_endSprite();
901     else if(stack[stackpos-1].type == 2)
902         s_endClip();
903     else syntaxerror("internal error 1");
904 }
905
906 // ------------------------------------------------------------------------
907
908 typedef int command_func_t(map_t*args);
909
910 SRECT parseBox(char*str)
911 {
912     SRECT r;
913     float xmin, xmax, ymin, ymax;
914     char*x = strchr(str, 'x');
915     char*d1=0,*d2=0;
916     if(!strcmp(str, "autocrop")) {
917         r.xmin = r.ymin = r.xmax = r.ymax = 0;
918         return r;
919     }
920     if(!x) goto error;
921     d1 = strchr(x+1, ':');
922     if(d1)
923         d2 = strchr(d1+1, ':');
924     if(!d1) {
925         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
926             goto error;
927         xmin = ymin = 0;
928     }
929     else if(d1 && !d2) {
930         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
931             goto error;
932         xmax += xmin;
933         ymin = 0;
934     }
935     else {
936         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
937             goto error;
938         xmax += xmin;
939         ymax += ymin;
940     }
941     r.xmin = (SCOORD)(xmin*20);
942     r.ymin = (SCOORD)(ymin*20);
943     r.xmax = (SCOORD)(xmax*20);
944     r.ymax = (SCOORD)(ymax*20);
945     return r;
946 error:
947     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
948     return r;
949 }
950 float parseFloat(char*str)
951 {
952     return atof(str);
953 }
954 int parseInt(char*str)
955 {
956     int t;
957     int l=strlen(str);
958     int s=0;
959     if(str[0]=='+' || str[0]=='-')
960         s++;
961
962     for(t=s;t<l;t++)
963         if(str[t]<'0' || str[t]>'9')
964             syntaxerror("Not an Integer: \"%s\"", str);
965     return atoi(str);
966 }
967 int parseTwip(char*str)
968 {
969     char*dot = strchr(str, '.');
970     if(!dot) {
971         int l=strlen(str);
972         int t;
973         return parseInt(str)*20;
974     } else {
975         int l=strlen(++dot);
976         char*s;
977         for(s=str;s<dot-1;s++)
978             if(*s<'0' || *s>'9')
979                 syntaxerror("Not a coordinate: \"%s\"", str);
980         for(s=dot;*s;s++) {
981             if(*s<'0' || *s>'9')
982                 syntaxerror("Not a coordinate: \"%s\"", str);
983         }
984         if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
985             warning("precision loss: %s converted to twip", str);
986             dot[2] = 0;
987             l=2;
988         }
989         if(l==0)
990             return atoi(str)*20;
991         if(l==1)
992             return atoi(str)*20+atoi(dot)*2;
993         if(l==2)
994             return atoi(str)*20+atoi(dot)/5;
995     }
996     return 0;
997 }
998
999 int isPoint(char*str)
1000 {
1001     if(strchr(str, '('))
1002         return 1;
1003     else
1004         return 0;
1005 }
1006
1007 SPOINT parsePoint(char*str)
1008 {
1009     SPOINT p;
1010     char tmp[80];
1011     int l = strlen(str);
1012     char*comma = strchr(str, ',');
1013     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1014         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1015     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1016     p.x = parseTwip(tmp);
1017     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1018     p.y = parseTwip(tmp);
1019     return p;
1020 }
1021
1022 int parseColor2(char*str, RGBA*color)
1023 {
1024     int l = strlen(str);
1025     int r,g,b,a;
1026     int t;
1027     char*names[8] = {"black", "blue", "green", "cyan",
1028                      "red", "violet", "yellow", "white"};
1029     a=255;
1030     r=g=b=0;
1031     if(str[0]=='#' && (l==7 || l==9)) {
1032         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1033             return 0;
1034         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1035             return 0;
1036         color->r = r; color->g = g; color->b = b; color->a = a;
1037         return 1;
1038     }
1039     for(t=0;t<8;t++)
1040         if(!strcmp(str, names[t])) {
1041             if(t&1)
1042                 b = 255;
1043             if(t&2)
1044                 g = 255;
1045             if(t&4)
1046                 r = 255;
1047             color->r = r; color->g = g; color->b = b; color->a = a;
1048             return 1;
1049         }
1050     return 0;
1051
1052 }
1053 RGBA parseColor(char*str)
1054 {
1055     RGBA c;
1056     if(!parseColor2(str, &c))
1057         syntaxerror("Expression '%s' is not a color", str);
1058     return c;
1059 }
1060
1061 typedef struct _muladd {
1062     S16 mul;
1063     S16 add;
1064 } MULADD;
1065
1066 MULADD parseMulAdd(char*str)
1067 {
1068     float add, mul;
1069     char* str2 = (char*)malloc(strlen(str)+5);
1070     int i;
1071     MULADD m;
1072     strcpy(str2, str);
1073     strcat(str2, " 0");
1074     add = 0;
1075     mul = 1.0;
1076     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1077     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1078     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1079     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1080     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1081     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1082     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1083     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1084     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1085     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1086     else {
1087         syntaxerror("'%s' is not a valid color transform expression", str);
1088     }
1089     m.add = (int)(add*256);
1090     m.mul = (int)(mul*256);
1091     free(str2);
1092     return m;
1093 }
1094
1095 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1096 {
1097     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1098     double m = ((double)m1.mul*(double)m2.mul)/256.0;
1099     MULADD r;
1100     if(a<-32768) a=-32768;
1101     if(a>32767) a=32767;
1102     if(m<-32768) m=-32768;
1103     if(m>32767) m=32767;
1104     r.add = a;
1105     r.mul = (int)m;
1106     return r;
1107 }
1108
1109 float parsePercent(char*str)
1110 {
1111     int l = strlen(str);
1112     if(!l)
1113         return 1.0;
1114     if(str[l-1]=='%') {
1115         return atoi(str)/100.0;
1116     }
1117     syntaxerror("Expression '%s' is not a percentage", str);
1118     return 0;
1119 }
1120 int isPercent(char*str)
1121 {
1122     return str[strlen(str)-1]=='%';
1123 }
1124 int parseNewSize(char*str, int size)
1125 {
1126     if(isPercent(str))
1127         return parsePercent(str)*size;
1128     else
1129         return (int)(atof(str)*20);
1130 }
1131
1132 int isColor(char*str)
1133 {
1134     RGBA c;
1135     return parseColor2(str, &c);
1136 }
1137
1138 static char* lu(map_t* args, char*name)
1139 {
1140     char* value = map_lookup(args, name);
1141     if(!value) {
1142         syntaxerror("internal error 2: value %s should be set", name);
1143     }
1144     return value;
1145 }
1146
1147 static int c_swf(map_t*args) 
1148 {
1149     char* name = lu(args, "name");
1150     char* compressstr = lu(args, "compress");
1151     SRECT bbox = parseBox(lu(args, "bbox"));
1152     int version = parseInt(lu(args, "version"));
1153     int fps = (int)(parseFloat(lu(args, "fps"))*256);
1154     int compress = 0;
1155     if(!strcmp(name, "!default!"))
1156         name = outputname;
1157     
1158     if(!strcmp(compressstr, "default"))
1159         compress = version==6;
1160     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1161         compress = 1;
1162     else if(!strcmp(compressstr, "no"))
1163         compress = 0;
1164     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1165
1166     s_swf(name, bbox, version, fps, compress);
1167     return 0;
1168 }
1169 int isRelative(char*str)
1170 {
1171     return !strncmp(str, "<plus>", 6) ||
1172            !strncmp(str, "<minus>", 7);
1173 }
1174 char* getOffset(char*str)
1175 {
1176     if(!strncmp(str, "<plus>", 6))
1177         return str+6;
1178     if(!strncmp(str, "<minus>", 7))
1179         return str+7;
1180     syntaxerror("internal error (347)");
1181     return 0;
1182 }
1183 int getSign(char*str)
1184 {
1185     if(!strncmp(str, "<plus>", 6))
1186         return 1;
1187     if(!strncmp(str, "<minus>", 7))
1188         return -1;
1189     syntaxerror("internal error (348)");
1190     return 0;
1191 }
1192 static dictionary_t points;
1193 static mem_t mpoints;
1194 int points_initialized = 0;
1195
1196 SPOINT getPoint(SRECT r, char*name)
1197 {
1198     int l;
1199     if(!strcmp(name, "center")) {
1200         SPOINT p;
1201         p.x = (r.xmin + r.xmax)/2;
1202         p.y = (r.ymin + r.ymax)/2;
1203         return p;
1204     }
1205
1206     l = (int)dictionary_lookup(&points, name);
1207     if(l==0) {
1208         syntaxerror("Couldn't find point \"%s\".", name);
1209     }
1210     l--;
1211     return *(SPOINT*)&mpoints.buffer[l];
1212 }
1213 static int c_point(map_t*args) 
1214 {
1215     char*name = lu(args, "name");
1216     int pos;
1217     string_t s1;
1218     SPOINT p;
1219     if(!points_initialized) {
1220         dictionary_init(&points);
1221         mem_init(&mpoints);
1222         points_initialized = 1;
1223     }
1224     p.x = parseTwip(lu(args, "x"));
1225     p.y = parseTwip(lu(args, "y"));
1226     pos = mem_put(&mpoints, &p, sizeof(p));
1227     string_set(&s1, name);
1228     pos++;
1229     dictionary_put(&points, s1, (void*)pos);
1230     return 0;
1231 }
1232 static int c_placement(map_t*args, int type)
1233 {
1234     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1235     char*character = 0;
1236
1237     char* luminancestr = lu(args, "luminance");
1238     char* scalestr = lu(args, "scale");
1239     char* scalexstr = lu(args, "scalex");
1240     char* scaleystr = lu(args, "scaley");
1241     char* rotatestr = lu(args, "rotate");
1242     char* xstr="", *pivotstr="";
1243     char* ystr="", *anglestr="";
1244     char*above = lu(args, "above"); /*FIXME*/
1245     char*below = lu(args, "below");
1246     char* rstr = lu(args, "red");
1247     char* gstr = lu(args, "green");
1248     char* bstr = lu(args, "blue");
1249     char* astr = lu(args, "alpha");
1250     char* pinstr = lu(args, "pin");
1251     MULADD r,g,b,a;
1252     float oldwidth;
1253     float oldheight;
1254     SRECT oldbbox;
1255     MULADD luminance;
1256     parameters_t p;
1257
1258     if(type==5) {
1259         pivotstr = lu(args, "pivot");
1260         anglestr = lu(args, "angle");
1261     } else {
1262         xstr = lu(args, "x");
1263         ystr = lu(args, "y");
1264     }
1265     if(luminancestr[0])
1266         luminance = parseMulAdd(luminancestr);
1267     else {
1268         luminance.add = 0;
1269         luminance.mul = 256;
1270     }
1271
1272     if(scalestr[0]) {
1273         if(scalexstr[0]||scaleystr[0])
1274             syntaxerror("scalex/scaley and scale cannot both be set");
1275         scalexstr = scaleystr = scalestr;
1276     }
1277     
1278     if(type == 0 || type == 4)  {
1279         // put or startclip
1280         character = lu(args, "character");
1281         parameters_clear(&p);
1282     } else {
1283         p = s_getParameters(instance);
1284     }
1285
1286     /* x,y position */
1287     if(xstr[0]) {
1288         if(isRelative(xstr)) {
1289             if(type == 0 || type == 4)
1290                 syntaxerror("relative x values not allowed for initial put or startclip");
1291             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1292         } else {
1293             p.x = parseTwip(xstr);
1294         }
1295     }
1296     if(ystr[0]) {
1297         if(isRelative(ystr)) {
1298             if(type == 0 || type == 4)
1299                 syntaxerror("relative y values not allowed for initial put or startclip");
1300             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1301         } else {
1302             p.y = parseTwip(ystr);
1303         }
1304     }
1305
1306     /* scale, scalex, scaley */
1307     if(character) {
1308         oldbbox = s_getCharBBox(character);
1309     } else {
1310         oldbbox = s_getInstanceBBox(instance);
1311     }
1312     oldwidth = oldbbox.xmax - oldbbox.xmin;
1313     oldheight = oldbbox.ymax - oldbbox.ymin;
1314     if(scalexstr[0]) {
1315         if(oldwidth==0) p.scalex = 1.0;
1316         else {      
1317             if(scalexstr[0])
1318                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1319         }
1320     }
1321     if(scaleystr[0]) {
1322         if(oldheight==0) p.scaley = 1.0;
1323         else {
1324             if(scaleystr[0])
1325                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1326         }
1327     }
1328    
1329     /* rotation */
1330     if(rotatestr[0]) {
1331         if(isRelative(rotatestr)) {
1332             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1333         } else {
1334             p.rotate = parseFloat(rotatestr);
1335         }
1336     }
1337
1338     if(pivotstr[0]) {
1339         if(isPoint(pivotstr)) 
1340             p.pivot = parsePoint(pivotstr);
1341         else 
1342             p.pivot = getPoint(oldbbox, pivotstr);
1343     }
1344     if(pinstr[0]) {
1345         if(isPoint(pinstr))
1346             p.pin = parsePoint(pinstr);
1347         else
1348             p.pin = getPoint(oldbbox, pinstr);
1349     }
1350         
1351     /* color transform */
1352
1353     if(rstr[0] || luminancestr[0]) {
1354         MULADD r;
1355         if(rstr[0])
1356             r = parseMulAdd(rstr);
1357         else {
1358             r.add = p.cxform.r0;
1359             r.mul = p.cxform.r1;
1360         }
1361         r = mergeMulAdd(r, luminance);
1362         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1363     }
1364     if(gstr[0] || luminancestr[0]) {
1365         MULADD g;
1366         if(gstr[0])
1367             g = parseMulAdd(gstr);
1368         else {
1369             g.add = p.cxform.g0;
1370             g.mul = p.cxform.g1;
1371         }
1372         g = mergeMulAdd(g, luminance);
1373         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1374     }
1375     if(bstr[0] || luminancestr[0]) {
1376         MULADD b;
1377         if(bstr[0])
1378             b = parseMulAdd(bstr);
1379         else {
1380             b.add = p.cxform.b0;
1381             b.mul = p.cxform.b1;
1382         }
1383         b = mergeMulAdd(b, luminance);
1384         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1385     }
1386     if(astr[0]) {
1387         MULADD a = parseMulAdd(astr);
1388         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1389     }
1390
1391     if(type == 0)
1392         s_put(instance, character, p);
1393     if(type == 1)
1394         s_change(instance, p);
1395     if(type == 2)
1396         s_qchange(instance, p);
1397     if(type == 3)
1398         s_jump(instance, p);
1399     if(type == 4)
1400         s_startclip(instance, character, p);
1401     return 0;
1402 }
1403 static int c_put(map_t*args) 
1404 {
1405     c_placement(args, 0);
1406     return 0;
1407 }
1408 static int c_change(map_t*args) 
1409 {
1410     c_placement(args, 1);
1411     return 0;
1412 }
1413 static int c_qchange(map_t*args) 
1414 {
1415     c_placement(args, 2);
1416     return 0;
1417 }
1418 static int c_arcchange(map_t*args) 
1419 {
1420     c_placement(args, 2);
1421     return 0;
1422 }
1423 static int c_jump(map_t*args) 
1424 {
1425     c_placement(args, 3);
1426     return 0;
1427 }
1428 static int c_startclip(map_t*args) 
1429 {
1430     c_placement(args, 4);
1431     return 0;
1432 }
1433 static int c_del(map_t*args) 
1434 {
1435     char*instance = lu(args, "name");
1436     s_delinstance(instance);
1437     return 0;
1438 }
1439 static int c_end(map_t*args) 
1440 {
1441     s_end();
1442     return 0;
1443 }
1444 static int c_sprite(map_t*args) 
1445 {
1446     char* name = lu(args, "name");
1447     s_sprite(name);
1448     return 0;
1449 }
1450 static int c_frame(map_t*args) 
1451 {
1452     char*framestr = lu(args, "n");
1453     int frame;
1454     if(framestr[0]=='+') {
1455         frame = s_getframe();
1456         frame += parseInt(framestr+1);
1457     }
1458     else {
1459         frame = parseInt(framestr);
1460         if(s_getframe() >= frame
1461                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1462             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1463     }
1464     s_frame(frame);
1465     return 0;
1466 }
1467 static int c_primitive(map_t*args) 
1468 {
1469     char*name = lu(args, "name");
1470     char*command = lu(args, "commandname");
1471     int width=0, height=0, r=0;
1472     int linewidth = parseTwip(lu(args, "line"));
1473     char*colorstr = lu(args, "color");
1474     RGBA color = parseColor(colorstr);
1475     char*fillstr = lu(args, "fill");
1476     int dofill = 1;
1477     int type=0;
1478     char* font;
1479     char* text;
1480     RGBA fill;
1481     if(!strcmp(command, "circle"))
1482         type = 1;
1483     else if(!strcmp(command, "textshape"))
1484         type = 2;
1485    
1486     if(type==0) {
1487         width = parseTwip(lu(args, "width"));
1488         height = parseTwip(lu(args, "height"));
1489     } else if (type==1) {
1490         r = parseTwip(lu(args, "r"));
1491     } else if (type==2) {
1492         text = lu(args, "text");
1493         font = lu(args, "font");
1494     }
1495
1496     if(!strcmp(fillstr, "fill"))
1497         fillstr = colorstr;
1498     if(!strcmp(fillstr, "none"))
1499         dofill = 0;
1500     if(width<0 || height<0 || linewidth<0 || r<0)
1501         syntaxerror("values width, height, line, r must be positive");
1502     if(!dofill || isColor(fillstr)) {
1503         if(dofill) 
1504             fill = parseColor(fillstr);
1505     } else {
1506         /* FIXME - texture fill */
1507         fill.r = fill.g = 0;
1508         fill.b = fill.a = 255;
1509         warning("texture fill not supported yet. Filling with black.");
1510     }
1511     if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1512     else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1513     else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1514     return 0;
1515 }
1516
1517 static int c_shape(map_t*args) 
1518 {
1519     char*name = lu(args, "name");
1520     char*filename = lu(args, "filename");
1521     s_shape(name, filename);
1522     return 0;
1523 }
1524
1525 static int c_font(map_t*args) 
1526 {
1527     char*name = lu(args, "name");
1528     char*filename = lu(args, "filename");
1529     s_font(name, filename);
1530     return 0;
1531 }
1532
1533 static int c_text(map_t*args) 
1534 {
1535     char*name = lu(args, "name");
1536     char*text = lu(args, "text");
1537     char*font = lu(args, "font");
1538     float size = parsePercent(lu(args, "size"));
1539     RGBA color = parseColor(lu(args, "color"));
1540     s_text(name, font, text, (int)(size*100), color);
1541     return 0;
1542 }
1543
1544 int fakechar(map_t*args)
1545 {
1546     char*name = lu(args, "name");
1547     s_box(name, 0, 0, black, 20, black, 0);
1548     return 0;
1549 }
1550 static int c_circle(map_t*args) {return fakechar(args);}
1551
1552 static int c_egon(map_t*args) {return fakechar(args);}
1553 static int c_button(map_t*args) {return fakechar(args);}
1554 static int c_edittext(map_t*args) {return fakechar(args);}
1555
1556 static int c_morphshape(map_t*args) {return fakechar(args);}
1557 static int c_image(map_t*args) {return fakechar(args);}
1558 static int c_movie(map_t*args) {return fakechar(args);}
1559 static int c_sound(map_t*args) {return fakechar(args);}
1560
1561 static int c_play(map_t*args) {return 0;}
1562 static int c_stop(map_t*args) {return 0;}
1563
1564 static int c_soundtrack(map_t*args) {return 0;}
1565 static int c_buttonsounds(map_t*args) {return 0;}
1566 static int c_buttonput(map_t*args) {return 0;}
1567 static int c_texture(map_t*args) {return 0;}
1568 static int c_action(map_t*args) {return 0;}
1569
1570 static struct {
1571     char*command;
1572     command_func_t* func;
1573     char*arguments;
1574 } arguments[] =
1575 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1576  {"frame", c_frame, "n=+1"},
1577
1578     // "import" type stuff
1579  {"shape", c_shape, "name filename"},
1580  {"morphshape", c_morphshape, "name start end"},
1581  {"jpeg", c_image, "name filename quality=80%"},
1582  {"png", c_image, "name filename"},
1583  {"movie", c_movie, "name filename"},
1584  {"sound", c_sound, "name filename"},
1585  {"font", c_font, "name filename"},
1586  {"soundtrack", c_soundtrack, "filename"},
1587
1588     // character generators
1589  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1590  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1591  {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1592  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1593  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1594  {"text", c_text, "name text font size=100% color=white"},
1595  {"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"},
1596  
1597  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1598
1599     // control tags
1600  {"play", c_play, "sound loop=0 @nomultiple=0"},
1601  {"stop", c_stop, "sound"},
1602
1603     // object placement tags
1604  {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1605  {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1606  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1607  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1608  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1609  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="},
1610  {"del", c_del, "name"},
1611     // virtual object placement
1612  {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% rotate=0 above= below="},
1613  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% rotate=0"},
1614  {"point", c_point, "name x=0 y=0"},
1615
1616     // commands which start a block
1617 //startclip (see above)
1618  {"sprite", c_sprite, "name"},
1619  {"action", c_action, ""},
1620
1621  {"end", c_end, ""}
1622 };
1623
1624
1625 static map_t parseArguments(char*command, char*pattern)
1626 {
1627     char*x;
1628     char*d,*e;
1629    
1630     string_t name[64];
1631     string_t value[64];
1632     int set[64];
1633     int isboolean[64];
1634     int pos;
1635     int len;
1636     int t;
1637     string_t t1,t2;
1638     map_t result;
1639     map_init(&result);
1640
1641     string_set(&t1, "commandname");
1642     string_set(&t2, command);
1643     map_put(&result, t1, t2);
1644
1645     if(!pattern || !*pattern)
1646         return result;
1647
1648     x = pattern;
1649
1650     pos = 0;
1651
1652     if(!strncmp("<i> ", x, 3)) {
1653         readToken();
1654         if(type == COMMAND || type == LABEL) {
1655             pushBack();
1656             syntaxerror("character name expected");
1657         }
1658         name[pos].str = "instance";
1659         name[pos].len = 8;
1660         value[pos].str = text;
1661         value[pos].len = strlen(text);
1662         set[pos] = 1;
1663         pos++;
1664
1665         if(type == ASSIGNMENT)
1666             readToken();
1667
1668         name[pos].str = "character";
1669         name[pos].len = 9;
1670         value[pos].str = text;
1671         value[pos].len = strlen(text);
1672         set[pos] = 1;
1673         pos++;
1674
1675         x+=4;
1676     }
1677
1678     while(*x) {
1679         isboolean[pos] = (x[0] =='@');
1680         if(isboolean[pos])
1681             x++;
1682
1683         d = strchr(x, ' ');
1684         e = strchr(x, '=');
1685         if(!d)
1686             d=&x[strlen(x)];
1687         set[pos] = 0;
1688
1689         if(!e || d<e) { 
1690             // no default
1691             name[pos].str = x;
1692             name[pos].len = d-x;
1693             value[pos].str = 0;
1694             value[pos].len = 0;
1695         } else {
1696             name[pos].str = x;
1697             name[pos].len = e-x;
1698             value[pos].str = e+1;
1699             value[pos].len = d-e-1;
1700         }
1701         pos++;
1702         if(!*d) break;
1703         x=d+1;
1704     }
1705     len = pos;
1706
1707 /*    for(t=0;t<len;t++) {
1708         printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1709                 isboolean[t]?"(boolean)":"");
1710     }*/
1711
1712     while(1) {
1713         readToken();
1714         if(type == LABEL || type == COMMAND) {
1715             pushBack();
1716             break;
1717         }
1718
1719         // first, search for boolean arguments
1720         for(pos=0;pos<len;pos++)
1721         {
1722             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1723                 set[pos] = 1;
1724                 if(type == ASSIGNMENT)
1725                     readToken();
1726                 value[pos].str = text;
1727                 value[pos].len = strlen(text);
1728                 /*printf("setting boolean parameter %s (to %s)\n",
1729                         strndup(name[pos], namelen[pos]),
1730                         strndup(value[pos], valuelen[pos]));*/
1731                 break;
1732             }
1733         }
1734
1735         // second, search for normal arguments
1736         if(pos==len)
1737         for(pos=0;pos<len;pos++)
1738         {
1739             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1740                (type != ASSIGNMENT && !set[pos])) {
1741                 if(set[pos]) {
1742                     syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1743                 }
1744                 if(type == ASSIGNMENT)
1745                     readToken();
1746                 set[pos] = 1;
1747                 value[pos].str = text;
1748                 value[pos].len = strlen(text);
1749 #if 0//def DEBUG
1750                 printf("setting parameter %s (to %s)\n",
1751                         strndup(name[pos].str, name[pos].len),
1752                         strndup(value[pos].str, value[pos].len));
1753 #endif
1754                 break;
1755             }
1756         }
1757         if(pos==len) {
1758             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1759         }
1760     }
1761 #if 0//def DEBUG
1762     for(t=0;t<len;t++) {
1763         printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1764     }
1765 #endif
1766     for(t=0;t<len;t++) {
1767         if(value[t].str && value[t].str[0] == '*') {
1768             //relative default- take value from some other parameter
1769             int s;
1770             for(s=0;s<len;s++) {
1771                 if(value[s].len == value[t].len-1 &&
1772                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
1773                     value[t].str = value[s].str;
1774             }
1775         }
1776         if(value[t].str == 0) {
1777             pushBack();
1778             syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1779         }
1780     }
1781
1782     /* ok, now construct the dictionary from the parameters */
1783
1784     for(t=0;t<len;t++) 
1785     {
1786         map_put(&result, name[t], value[t]);
1787     }
1788     return result;
1789 }
1790 static void parseArgumentsForCommand(char*command)
1791 {
1792     int t;
1793     map_t args;
1794     int nr = -1;
1795     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1796         if(!strcmp(arguments[t].command, command)) {
1797             args = parseArguments(command, arguments[t].arguments);
1798             nr = t;
1799         }
1800     }
1801     if(nr<0)
1802         syntaxerror("command %s not known", command);
1803
1804 #ifdef DEBUG
1805     printf(".%s\n", command);fflush(stdout);
1806     map_dump(&args, stdout, "\t");fflush(stdout);
1807 #endif
1808
1809     (*arguments[nr].func)(&args);
1810
1811     if(!strcmp(command, "button") ||
1812        !strcmp(command, "action")) {
1813         while(1) {
1814             readToken();
1815             if(type == COMMAND) {
1816                 if(!strcmp(text, "end"))
1817                     break;
1818                 else {
1819                     pushBack();
1820                     break;
1821                 }
1822             }
1823         }
1824     }
1825
1826     map_clear(&args);
1827     return;
1828 }
1829
1830 int main (int argc,char ** argv)
1831
1832     int t;
1833     processargs(argc, argv);
1834     initLog(0,-1,0,0,-1,verbose);
1835
1836     if(!filename) {
1837         args_callback_usage(argv[0]);
1838         exit(1);
1839     }
1840     file = generateTokens(filename);
1841     if(!file) {
1842         printf("parser returned error.\n");
1843         return 1;
1844     }
1845     pos=0;
1846
1847     t=0;
1848
1849     while(!noMoreTokens()) {
1850         readToken();
1851         if(type != COMMAND)
1852             syntaxerror("command expected");
1853         parseArgumentsForCommand(text);
1854     }
1855
1856     s_close();
1857     freeTokens(file);
1858
1859     return 0;
1860 }
1861