file copied from ming 0.3alpha2
[swftools.git] / lib / action / compile.c
1 /*
2     Ming, an SWF output library
3     Copyright (C) 2002  Opaque Industries - http://www.opaque.net/
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #ifndef WIN32
21         #include <unistd.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <string.h>
28
29 #include "libming.h"
30 #include "compile.h"
31 #include "action.h"
32 #include "blocks/error.h"
33
34
35 static int nConstants = {0}, maxConstants = {0}, sizeConstants = {0};
36 static char **constants;
37
38 /* XXX - temp hack until we check at compile time */
39
40 enum
41 {
42         SWF_BIG_ENDIAN,
43         SWF_LITTLE_ENDIAN
44 };
45
46 static int byteorder;
47
48 void checkByteOrder()
49 {
50         unsigned int x;
51         unsigned char *p;
52
53         x = 0x01020304;
54         p = (unsigned char *)&x;
55
56         if(*p == 1)
57                 byteorder = SWF_BIG_ENDIAN;
58         else
59                 byteorder = SWF_LITTLE_ENDIAN;
60 }
61
62
63 char *stringConcat(char *a, char *b)
64 {
65         if ( a != NULL )
66         {
67                 if ( b != NULL )
68                 {
69                         a = (char*)realloc(a, strlen(a)+strlen(b)+1);
70                         strcat(a, b);
71                         free(b);
72                 }
73
74                 return a;
75         }
76         else
77                 return b;
78 }
79
80 void bufferPatchLength(Buffer buffer, int back)
81 {
82         unsigned char *output = buffer->buffer;
83         int len = bufferLength(buffer);
84
85         output[len-back-1] = (back>>8) & 0xff;
86         output[len-back-2] = back & 0xff;
87 }
88
89
90 /* add len more bytes to length of the pushdata opcode pointed to by
91          buffer->pushloc */
92
93 void bufferPatchPushLength(Buffer buffer, int len)
94 {
95         int oldsize;
96
97         if(buffer->pushloc != NULL)
98         {
99                 oldsize = (buffer->pushloc[0] & 0xff) | ((buffer->pushloc[1] & 0xff) << 8);
100                 oldsize += len;
101                 buffer->pushloc[0] = oldsize & 0xff;
102                 buffer->pushloc[1] = (oldsize >> 8) & 0xff;
103         }
104         else
105                 SWF_error("problem with bufferPatchPushLength\n");
106 }
107
108
109 static int useConstants = 1;
110 void Ming_useConstants(int flag)
111 {       useConstants = flag;
112 }
113
114
115 int addConstant(const char *s)
116 {
117         int i;
118
119         for(i=0; i<nConstants; ++i)
120         {
121                 if(strcmp(s, constants[i]) == 0)
122                         return i;
123         }
124
125         /* Don't let constant pool biggern then allowed */
126         if ( sizeConstants+strlen(s)+1 > MAXCONSTANTPOOLSIZE ) return -1;
127
128         if(nConstants == maxConstants)
129                 constants = (char **) realloc(constants, (maxConstants += 64) * sizeof(char *));
130         constants[nConstants] = strdup(s);
131         sizeConstants += (strlen(s)+1);
132         return nConstants++;
133 }
134
135 int bufferWriteConstants(Buffer out)
136 {
137         int i, len=2;
138
139         if(nConstants == 0)
140                 return 0;
141
142         bufferWriteU8(out, SWFACTION_CONSTANTPOOL);
143         bufferWriteS16(out, 0); /* length */
144         bufferWriteS16(out, nConstants);
145
146         for(i=0; i<nConstants; ++i)
147         {
148                 len += bufferWriteHardString(out,(byte*) constants[i], strlen(constants[i])+1);
149                 free(constants[i]);
150         }
151
152         nConstants = 0;
153         sizeConstants = 0;
154         bufferPatchLength(out, len);
155
156         return len+3;
157 }
158
159 Buffer newBuffer()
160 {
161         Buffer out = (Buffer)malloc(BUFFER_SIZE);
162         memset(out, 0, BUFFER_SIZE);
163
164         out->buffer = (byte*)malloc(BUFFER_INCREMENT);
165         out->pos = out->buffer;
166         *(out->pos) = 0;
167         out->buffersize = out->free = BUFFER_INCREMENT;
168         out->pushloc = NULL;
169
170         return out;
171 }
172
173 void destroyBuffer(Buffer out)
174 {
175         free(out->buffer);
176         free(out);
177 }
178
179 int bufferLength(Buffer out)
180 {
181         if(out)
182                 return (out->pos)-(out->buffer);
183         else
184                 return 0;
185 }
186
187 /* make sure there's enough space for bytes bytes */
188 void bufferCheckSize(Buffer out, int bytes)
189 {
190         if(bytes > out->free)
191         {
192                 int New = BUFFER_INCREMENT * ((bytes-out->free-1)/BUFFER_INCREMENT + 1);
193
194                 int num = bufferLength(out); /* in case buffer gets displaced.. */
195                 unsigned char *newbuf = (unsigned char*)realloc(out->buffer, out->buffersize+New);
196
197                 if(newbuf != out->buffer)
198                 {
199                         int pushd;
200
201                         if(out->pushloc)
202         pushd = out->pos - out->pushloc;
203
204                         out->pos = newbuf+num;
205
206                         if(out->pushloc)
207         out->pushloc = out->pos - pushd;
208                 }
209
210                 out->buffer = newbuf;
211                 out->buffersize += New;
212                 out->free += New;
213         }
214 }
215
216 int bufferWriteData(Buffer b, const byte *data, int length)
217 {
218         int i;
219
220         bufferCheckSize(b, length);
221
222         for(i=0; i<length; ++i)
223                 bufferWriteU8(b, data[i]);
224
225         return length;
226 }
227
228 int bufferWriteBuffer(Buffer a, Buffer b)
229 {
230         if(!a)
231                 return 0;
232
233         if(b)
234                 return bufferWriteData(a, b->buffer, bufferLength(b));
235
236         return 0;
237 }
238
239 /* if a's last op and b's first op are both PUSHDATA, concat into one op */
240
241 int bufferWriteDataAndPush(Buffer a, Buffer b)
242 {
243         int i, pushd;
244
245         byte *data = b->buffer;
246         int length = b->pos - b->buffer;
247
248         if(a->pushloc && (b->buffer[0] == SWFACTION_PUSHDATA) && SWF_versionNum > 4)
249         {
250                 pushd = (b->buffer[1] & 0xff) | ((b->buffer[2] & 0xff) << 8);
251                 bufferPatchPushLength(a, pushd);
252                 data += 3;
253                 length -= 3;
254         }
255
256         if(b->pushloc)
257                 pushd = b->pos - b->pushloc;
258
259         bufferCheckSize(a, length);
260
261         for(i=0; i<length; ++i)
262                 bufferWriteU8(a, data[i]);
263
264         if(a->pushloc &&
265                  (b->buffer[0] == SWFACTION_PUSHDATA) && (b->pushloc == b->buffer+1))
266                 ; /* b is just one pushdata, so do nothing.. */
267         else if(b->pushloc)
268                 a->pushloc = a->pos - pushd;
269         else
270                 a->pushloc = 0;
271
272         return length;
273 }
274
275 int bufferConcat(Buffer a, Buffer b)
276 {
277         int len;
278
279         if(!a)
280                 return 0;
281
282         if(b)
283         {       len = bufferWriteDataAndPush(a, b);
284                 destroyBuffer(b);
285         }
286
287         return len;
288 }
289
290 int bufferWriteOp(Buffer out, int data)
291 {
292         bufferWriteU8(out, data);
293         out->pushloc = NULL;
294
295         return 1;
296 }
297
298 int bufferWritePushOp(Buffer out)
299 {
300         bufferWriteU8(out, SWFACTION_PUSHDATA);
301         out->pushloc = out->pos;
302
303         return 1;
304 }
305
306 int bufferWriteU8(Buffer out, int data)
307 {
308         bufferCheckSize(out, 1);
309         *(out->pos) = data;
310         out->pos++;
311         out->free--;
312
313         return 1;
314 }
315
316 int bufferWriteS16(Buffer out, int data)
317 {
318         if(data < 0)
319                 data = (1<<16)+data;
320
321         bufferWriteU8(out, data%256);
322         data >>= 8;
323         bufferWriteU8(out, data%256);
324
325         return 2;
326 }
327
328 int bufferWriteHardString(Buffer out, byte *string, int length)
329 {
330         int i;
331
332         for(i=0; i<length; ++i)
333                 bufferWriteU8(out, string[i]);
334
335         return length;
336 }
337
338 int bufferWriteConstantString(Buffer out, byte *string, int length)
339 {
340         int n;
341
342         if(SWF_versionNum < 5)
343                 return -1;
344
345         if(useConstants)
346                 n = addConstant((char*) string);
347         else
348                 n = -1;
349
350         if(n == -1)
351         {
352                 bufferWriteU8(out, PUSH_STRING);
353                 return bufferWriteHardString(out, string, length) + 1;
354         }
355         else if(n < 256)
356         {
357                 bufferWriteU8(out, PUSH_CONSTANT);
358                 return bufferWriteU8(out, n) + 1;
359         }
360         else
361         {
362                 bufferWriteU8(out, PUSH_CONSTANT16);
363                 return bufferWriteS16(out, n) + 1;
364         }
365 }
366
367 int bufferWriteString(Buffer out, byte *string, int length)
368 {
369         if(SWF_versionNum < 5)
370         {
371                 bufferWritePushOp(out);
372                 bufferWriteS16(out, length+1);
373                 bufferWriteU8(out, PUSH_STRING);
374                 bufferWriteHardString(out, string, length);
375
376                 return 4 + length;
377         }
378         else
379         {
380                 int l;
381
382                 if(out->pushloc == NULL)
383                 {
384                         bufferWritePushOp(out);
385                         bufferWriteS16(out, 0);
386                 }
387
388                 l = bufferWriteConstantString(out, string, length);
389
390                 bufferPatchPushLength(out, l);
391                 return l;
392         }
393 }
394
395 int bufferWriteInt(Buffer out, int i)
396 {
397         int len = 0;
398         unsigned char *p = (unsigned char *)&i;
399
400         if(out->pushloc == NULL || SWF_versionNum < 5)
401         {
402                 len = 3;
403                 bufferWritePushOp(out);
404                 bufferWriteS16(out, 5);
405         }
406         else
407                 bufferPatchPushLength(out, 5);
408
409         bufferWriteU8(out, PUSH_INT);
410
411         if(byteorder == SWF_LITTLE_ENDIAN)
412         {
413                 bufferWriteU8(out, p[0]);
414                 bufferWriteU8(out, p[1]);
415                 bufferWriteU8(out, p[2]);
416                 bufferWriteU8(out, p[3]);
417         }
418         else
419         {
420                 bufferWriteU8(out, p[3]);
421                 bufferWriteU8(out, p[2]);
422                 bufferWriteU8(out, p[1]);
423                 bufferWriteU8(out, p[0]);
424         }
425
426         return len + 5;
427 }
428
429 int bufferWriteDouble(Buffer out, double d)
430 {
431         int len = 0;
432         unsigned char *p = (unsigned char *)&d;
433
434         if(out->pushloc == NULL || SWF_versionNum < 5)
435         {
436                 len = 3;
437                 bufferWritePushOp(out);
438                 bufferWriteS16(out, 9);
439         }
440         else
441                 bufferPatchPushLength(out, 5);
442
443         bufferWriteU8(out, PUSH_DOUBLE);
444
445         if(byteorder == SWF_LITTLE_ENDIAN)
446         {
447                 bufferWriteU8(out, p[4]);
448                 bufferWriteU8(out, p[5]);
449                 bufferWriteU8(out, p[6]);
450                 bufferWriteU8(out, p[7]);
451                 bufferWriteU8(out, p[0]);
452                 bufferWriteU8(out, p[1]);
453                 bufferWriteU8(out, p[2]);
454                 bufferWriteU8(out, p[3]);
455         }
456         else
457         {
458                 bufferWriteU8(out, p[3]);
459                 bufferWriteU8(out, p[2]);
460                 bufferWriteU8(out, p[1]);
461                 bufferWriteU8(out, p[0]);
462                 bufferWriteU8(out, p[7]);
463                 bufferWriteU8(out, p[6]);
464                 bufferWriteU8(out, p[5]);
465                 bufferWriteU8(out, p[4]);
466         }
467
468         return len + 9;
469 }
470
471 int bufferWriteNull(Buffer out)
472 {
473         int len = 0;
474
475         if(out->pushloc == NULL || SWF_versionNum < 5)
476         {
477                 len = 3;
478                 bufferWritePushOp(out);
479                 bufferWriteS16(out, 1);
480         }
481         else
482                 bufferPatchPushLength(out, 1);
483
484         bufferWriteU8(out, PUSH_NULL);
485
486         return len + 1;
487 }
488
489 int bufferWriteBoolean(Buffer out, int val)
490 {
491         int len = 0;
492
493         if(out->pushloc == NULL || SWF_versionNum < 5)
494         {
495                 len = 3;
496                 bufferWritePushOp(out);
497                 bufferWriteS16(out, 2);
498         }
499         else
500                 bufferPatchPushLength(out, 2);
501
502         bufferWriteU8(out, PUSH_BOOLEAN);
503         bufferWriteU8(out, val ? 1 : 0);
504
505         return len + 2;
506 }
507
508 int bufferWriteRegister(Buffer out, int num)
509 {
510         int len = 0;
511
512         if(out->pushloc == NULL || SWF_versionNum < 5)
513         {
514                 len = 3;
515                 bufferWritePushOp(out);
516                 bufferWriteS16(out, 2);
517         }
518         else
519                 bufferPatchPushLength(out, 2);
520
521         bufferWriteU8(out, PUSH_REGISTER);
522         bufferWriteU8(out, num);
523
524         return len + 2;
525 }
526
527 int bufferWriteSetRegister(Buffer out, int num)
528 {
529         bufferWriteU8(out, SWFACTION_SETREGISTER);
530         bufferWriteS16(out, 1);
531         bufferWriteU8(out, num);
532         return 4;
533 }
534
535 void lower(char *s)
536 {
537         while(*s)
538         {
539                 *s = tolower(*s);
540                 ++s;
541         }
542 }
543
544 /* this code will eventually help to pop extra values off the
545  stack and make sure that continue and break address the proper
546  context
547  */
548 static enum ctx *ctx_stack = {0};
549 static int ctx_count = {0}, ctx_len = {0};
550 void addctx(enum ctx val)
551 {       if(ctx_count >= ctx_len)
552                 ctx_stack = (enum ctx*) realloc(ctx_stack, (ctx_len += 10) * sizeof(enum ctx));
553         ctx_stack[ctx_count++] = val;
554 }
555 void delctx(enum ctx val)
556 {       if(ctx_count <= 0 || ctx_stack[--ctx_count] != val)
557                 SWF_error("consistency check in delctx");
558 }
559
560 int chkctx(enum ctx val)
561 {       int n, ret = 0;
562         switch(val)
563         {       case CTX_FUNCTION:
564                         for(n = ctx_count ; --n >= 0 ; )
565                                 switch(ctx_stack[n])
566                                 {       case CTX_SWITCH:
567                                         case CTX_FOR_IN:
568                                                 ret++;
569                                                 break;
570                                         case CTX_FUNCTION:
571                                                 return ret;
572                                         default: ; /* computers are stupid */
573                                 }
574                         return -1;
575                 case CTX_BREAK:
576                         for(n = ctx_count ; --n >= 0 ; )
577                                 switch(ctx_stack[n])
578                                 {       case CTX_SWITCH:
579                                         case CTX_LOOP:
580                                                 return 0;
581                                         case CTX_FOR_IN:
582                                                 return 1;
583                                         case CTX_FUNCTION:
584                                                 return -1;
585                                         default: ; /* computers are stupid */
586                                 }
587                 case CTX_CONTINUE:
588                         for(n = ctx_count ; --n >= 0 ; )
589                                 switch(ctx_stack[n])
590                                 {       case CTX_LOOP:
591                                         case CTX_FOR_IN:
592                                                 return 0;
593                                         case CTX_FUNCTION:
594                                                 return -1;
595                                         default: ; /* computers are stupid */
596                                 }
597                 default: ; /* computers are stupid */
598         }
599         return 0;
600 }
601
602 /* replace MAGIC_CONTINUE_NUMBER and MAGIC_BREAK_NUMBER with jumps to
603          head or tail, respectively */
604 /* jump offset is relative to end of jump instruction */
605 /* I can't believe this actually worked */
606
607 void bufferResolveJumps(Buffer out)
608 {
609         byte *p = out->buffer;
610         int l, target;
611
612         while(p < out->pos)
613         {
614                 if(*p & 0x80) /* then it's a multibyte instruction */
615                 {
616                         if(*p == SWFACTION_BRANCHALWAYS)
617                         {
618         p += 3; /* plus instruction plus two-byte length */
619
620         if(*p == MAGIC_CONTINUE_NUMBER_LO &&
621                  *(p+1) == MAGIC_CONTINUE_NUMBER_HI)
622         {
623                 target = out->buffer - (p+2);
624                 *p = target & 0xff;
625                 *(p+1) = (target>>8) & 0xff;
626         }
627         else if(*p == MAGIC_BREAK_NUMBER_LO &&
628                 *(p+1) == MAGIC_BREAK_NUMBER_HI)
629         {
630                 target = out->pos - (p+2);
631                 *p = target & 0xff;
632                 *(p+1) = (target>>8) & 0xff;
633         }
634
635         p += 2;
636                         }
637                         else
638                         {
639         ++p;
640         l = *p;
641         ++p;
642         l += *p<<8;
643         ++p;
644
645         p += l;
646                         }
647                 }
648                 else
649                         ++p;
650         }
651 }
652
653 // handle SWITCH statement
654
655 void bufferResolveSwitch(Buffer buffer, struct switchcases *slp)
656 {       struct switchcase *scp;
657         int n, len;
658         unsigned char *output;
659                         
660         len = bufferLength(buffer);
661         for(n = 0, scp = slp->list ; n < slp->count ; n++, scp++)
662         {       scp->actlen = bufferLength(scp->action);
663                 if((n < slp->count-1))
664                         scp->actlen += 5;
665                 if(scp->cond)
666                 {       scp->condlen = bufferLength(scp->cond) + 8;
667                         bufferWriteOp(buffer, SWFACTION_DUP);
668                         bufferConcat(buffer, scp->cond);
669                         bufferWriteOp(buffer, SWFACTION_NEWEQUALS);
670                         bufferWriteOp(buffer, SWFACTION_LOGICALNOT);
671                         bufferWriteOp(buffer, SWFACTION_BRANCHIFTRUE);
672                         bufferWriteS16(buffer, 2);
673                         bufferWriteS16(buffer, scp->actlen);
674                 }
675                 else
676                         scp->condlen = 0;
677                 bufferConcat(buffer, scp->action);
678                 bufferWriteOp(buffer, SWFACTION_BRANCHALWAYS);
679                 bufferWriteS16(buffer, 2);
680                 bufferWriteS16(buffer, scp->isbreak ? MAGIC_BREAK_NUMBER : 0);
681                 if(!scp->cond)
682                 {       slp->count = n+1;
683                         break;
684                 }
685         }
686         for(n = 0, scp = slp->list ; n < slp->count ; n++, scp++)
687         {       len += scp->condlen;
688                 output = buffer->buffer + len;
689                 if((n < slp->count-1) && !scp->isbreak)
690                 {       output[scp->actlen-2] = (scp+1)->condlen & 0xff;
691                         output[scp->actlen-1] = (scp+1)->condlen >> 8;
692                 }
693                 len += scp->actlen;
694         }
695 }
696         
697 int lookupSetProperty(char *string)
698 {
699         lower(string);
700
701         if(strcmp(string,"x")==0)               return 0x0000;
702         if(strcmp(string,"y")==0)               return 0x3f80;
703         if(strcmp(string,"xscale")==0)  return 0x4000;
704         if(strcmp(string,"yscale")==0)  return 0x4040;
705         if(strcmp(string,"alpha")==0)           return 0x40c0;
706         if(strcmp(string,"visible")==0) return 0x40e0;
707         if(strcmp(string,"rotation")==0)        return 0x4120;
708         if(strcmp(string,"name")==0)            return 0x4140;
709         if(strcmp(string,"quality")==0) return 0x4180;
710         if(strcmp(string,"focusrect")==0)       return 0x4188;
711         if(strcmp(string,"soundbuftime")==0)    return 0x4190;
712
713         SWF_error("No such property: %s\n", string);
714         return -1;
715 }
716
717 int bufferWriteSetProperty(Buffer out, char *string)
718 {
719         int property = lookupSetProperty(string);
720
721         bufferWriteU8(out, SWFACTION_PUSHDATA);
722         bufferWriteS16(out, 5);
723         bufferWriteU8(out, PUSH_PROPERTY);
724         bufferWriteS16(out, 0);
725         bufferWriteS16(out, property);
726
727         return 8;
728 }
729
730 int bufferWriteWTHITProperty(Buffer out)
731 {
732         bufferWriteU8(out, SWFACTION_PUSHDATA);
733         bufferWriteS16(out, 5);
734         bufferWriteU8(out, PUSH_PROPERTY);
735         bufferWriteS16(out, 0);
736         bufferWriteS16(out, 0x4680);
737
738         return 8;
739 }
740
741 const char *lookupGetProperty(char *string)
742 {
743         lower(string);
744
745         if(strcmp(string,"x")==0)               return "0";
746         if(strcmp(string,"y")==0)               return "1";
747         if(strcmp(string,"xscale")==0)  return "2";
748         if(strcmp(string,"yscale")==0)  return "3";
749         if(strcmp(string,"currentframe")==0)    return "4";
750         if(strcmp(string,"totalframes")==0)     return "5";
751         if(strcmp(string,"alpha")==0)           return "6";
752         if(strcmp(string,"visible")==0) return "7";
753         if(strcmp(string,"width")==0)           return "8";
754         if(strcmp(string,"height")==0)  return "9";
755         if(strcmp(string,"rotation")==0)        return "10";
756         if(strcmp(string,"target")==0)  return "11";
757         if(strcmp(string,"framesloaded")==0)    return "12";
758         if(strcmp(string,"name")==0)            return "13";
759         if(strcmp(string,"droptarget")==0)      return "14";
760         if(strcmp(string,"url")==0)             return "15";
761         if(strcmp(string,"quality")==0) return "16";
762         if(strcmp(string,"focusrect")==0)       return "17";
763         if(strcmp(string,"soundbuftime")==0)    return "18";
764
765         SWF_error("No such property: %s\n", string);
766         return "";
767 }
768
769 int bufferWriteGetProperty(Buffer out, char *string)
770 {
771         const char *property = lookupGetProperty(string);
772
773         bufferWriteU8(out, SWFACTION_PUSHDATA);
774         bufferWriteS16(out, strlen(property)+2);
775         bufferWriteU8(out, PUSH_STRING);
776
777         return 4 + bufferWriteData(out, (byte*) property, strlen(property)+1);
778 }
779
780
781 /*
782  * Local variables:
783  * tab-width: 2
784  * c-basic-offset: 2
785  * End:
786  */