file copied from ming 0.3alpha2
[swftools.git] / lib / action / compile.c
diff --git a/lib/action/compile.c b/lib/action/compile.c
new file mode 100755 (executable)
index 0000000..b658dd2
--- /dev/null
@@ -0,0 +1,786 @@
+/*
+    Ming, an SWF output library
+    Copyright (C) 2002  Opaque Industries - http://www.opaque.net/
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef WIN32
+       #include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "libming.h"
+#include "compile.h"
+#include "action.h"
+#include "blocks/error.h"
+
+
+static int nConstants = {0}, maxConstants = {0}, sizeConstants = {0};
+static char **constants;
+
+/* XXX - temp hack until we check at compile time */
+
+enum
+{
+       SWF_BIG_ENDIAN,
+       SWF_LITTLE_ENDIAN
+};
+
+static int byteorder;
+
+void checkByteOrder()
+{
+       unsigned int x;
+       unsigned char *p;
+
+       x = 0x01020304;
+       p = (unsigned char *)&x;
+
+       if(*p == 1)
+               byteorder = SWF_BIG_ENDIAN;
+       else
+               byteorder = SWF_LITTLE_ENDIAN;
+}
+
+
+char *stringConcat(char *a, char *b)
+{
+       if ( a != NULL )
+       {
+               if ( b != NULL )
+               {
+                       a = (char*)realloc(a, strlen(a)+strlen(b)+1);
+                       strcat(a, b);
+                       free(b);
+               }
+
+               return a;
+       }
+       else
+               return b;
+}
+
+void bufferPatchLength(Buffer buffer, int back)
+{
+       unsigned char *output = buffer->buffer;
+       int len = bufferLength(buffer);
+
+       output[len-back-1] = (back>>8) & 0xff;
+       output[len-back-2] = back & 0xff;
+}
+
+
+/* add len more bytes to length of the pushdata opcode pointed to by
+        buffer->pushloc */
+
+void bufferPatchPushLength(Buffer buffer, int len)
+{
+       int oldsize;
+
+       if(buffer->pushloc != NULL)
+       {
+               oldsize = (buffer->pushloc[0] & 0xff) | ((buffer->pushloc[1] & 0xff) << 8);
+               oldsize += len;
+               buffer->pushloc[0] = oldsize & 0xff;
+               buffer->pushloc[1] = (oldsize >> 8) & 0xff;
+       }
+       else
+               SWF_error("problem with bufferPatchPushLength\n");
+}
+
+
+static int useConstants = 1;
+void Ming_useConstants(int flag)
+{      useConstants = flag;
+}
+
+
+int addConstant(const char *s)
+{
+       int i;
+
+       for(i=0; i<nConstants; ++i)
+       {
+               if(strcmp(s, constants[i]) == 0)
+                       return i;
+       }
+
+       /* Don't let constant pool biggern then allowed */
+       if ( sizeConstants+strlen(s)+1 > MAXCONSTANTPOOLSIZE ) return -1;
+
+       if(nConstants == maxConstants)
+               constants = (char **) realloc(constants, (maxConstants += 64) * sizeof(char *));
+       constants[nConstants] = strdup(s);
+       sizeConstants += (strlen(s)+1);
+       return nConstants++;
+}
+
+int bufferWriteConstants(Buffer out)
+{
+       int i, len=2;
+
+       if(nConstants == 0)
+               return 0;
+
+       bufferWriteU8(out, SWFACTION_CONSTANTPOOL);
+       bufferWriteS16(out, 0); /* length */
+       bufferWriteS16(out, nConstants);
+
+       for(i=0; i<nConstants; ++i)
+       {
+               len += bufferWriteHardString(out,(byte*) constants[i], strlen(constants[i])+1);
+               free(constants[i]);
+       }
+
+       nConstants = 0;
+       sizeConstants = 0;
+       bufferPatchLength(out, len);
+
+       return len+3;
+}
+
+Buffer newBuffer()
+{
+       Buffer out = (Buffer)malloc(BUFFER_SIZE);
+       memset(out, 0, BUFFER_SIZE);
+
+       out->buffer = (byte*)malloc(BUFFER_INCREMENT);
+       out->pos = out->buffer;
+       *(out->pos) = 0;
+       out->buffersize = out->free = BUFFER_INCREMENT;
+       out->pushloc = NULL;
+
+       return out;
+}
+
+void destroyBuffer(Buffer out)
+{
+       free(out->buffer);
+       free(out);
+}
+
+int bufferLength(Buffer out)
+{
+       if(out)
+               return (out->pos)-(out->buffer);
+       else
+               return 0;
+}
+
+/* make sure there's enough space for bytes bytes */
+void bufferCheckSize(Buffer out, int bytes)
+{
+       if(bytes > out->free)
+       {
+               int New = BUFFER_INCREMENT * ((bytes-out->free-1)/BUFFER_INCREMENT + 1);
+
+               int num = bufferLength(out); /* in case buffer gets displaced.. */
+               unsigned char *newbuf = (unsigned char*)realloc(out->buffer, out->buffersize+New);
+
+               if(newbuf != out->buffer)
+               {
+                       int pushd;
+
+                       if(out->pushloc)
+       pushd = out->pos - out->pushloc;
+
+                       out->pos = newbuf+num;
+
+                       if(out->pushloc)
+       out->pushloc = out->pos - pushd;
+               }
+
+               out->buffer = newbuf;
+               out->buffersize += New;
+               out->free += New;
+       }
+}
+
+int bufferWriteData(Buffer b, const byte *data, int length)
+{
+       int i;
+
+       bufferCheckSize(b, length);
+
+       for(i=0; i<length; ++i)
+               bufferWriteU8(b, data[i]);
+
+       return length;
+}
+
+int bufferWriteBuffer(Buffer a, Buffer b)
+{
+       if(!a)
+               return 0;
+
+       if(b)
+               return bufferWriteData(a, b->buffer, bufferLength(b));
+
+       return 0;
+}
+
+/* if a's last op and b's first op are both PUSHDATA, concat into one op */
+
+int bufferWriteDataAndPush(Buffer a, Buffer b)
+{
+       int i, pushd;
+
+       byte *data = b->buffer;
+       int length = b->pos - b->buffer;
+
+       if(a->pushloc && (b->buffer[0] == SWFACTION_PUSHDATA) && SWF_versionNum > 4)
+       {
+               pushd = (b->buffer[1] & 0xff) | ((b->buffer[2] & 0xff) << 8);
+               bufferPatchPushLength(a, pushd);
+               data += 3;
+               length -= 3;
+       }
+
+       if(b->pushloc)
+               pushd = b->pos - b->pushloc;
+
+       bufferCheckSize(a, length);
+
+       for(i=0; i<length; ++i)
+               bufferWriteU8(a, data[i]);
+
+       if(a->pushloc &&
+                (b->buffer[0] == SWFACTION_PUSHDATA) && (b->pushloc == b->buffer+1))
+               ; /* b is just one pushdata, so do nothing.. */
+       else if(b->pushloc)
+               a->pushloc = a->pos - pushd;
+       else
+               a->pushloc = 0;
+
+       return length;
+}
+
+int bufferConcat(Buffer a, Buffer b)
+{
+       int len;
+
+       if(!a)
+               return 0;
+
+       if(b)
+       {       len = bufferWriteDataAndPush(a, b);
+               destroyBuffer(b);
+       }
+
+       return len;
+}
+
+int bufferWriteOp(Buffer out, int data)
+{
+       bufferWriteU8(out, data);
+       out->pushloc = NULL;
+
+       return 1;
+}
+
+int bufferWritePushOp(Buffer out)
+{
+       bufferWriteU8(out, SWFACTION_PUSHDATA);
+       out->pushloc = out->pos;
+
+       return 1;
+}
+
+int bufferWriteU8(Buffer out, int data)
+{
+       bufferCheckSize(out, 1);
+       *(out->pos) = data;
+       out->pos++;
+       out->free--;
+
+       return 1;
+}
+
+int bufferWriteS16(Buffer out, int data)
+{
+       if(data < 0)
+               data = (1<<16)+data;
+
+       bufferWriteU8(out, data%256);
+       data >>= 8;
+       bufferWriteU8(out, data%256);
+
+       return 2;
+}
+
+int bufferWriteHardString(Buffer out, byte *string, int length)
+{
+       int i;
+
+       for(i=0; i<length; ++i)
+               bufferWriteU8(out, string[i]);
+
+       return length;
+}
+
+int bufferWriteConstantString(Buffer out, byte *string, int length)
+{
+       int n;
+
+       if(SWF_versionNum < 5)
+               return -1;
+
+       if(useConstants)
+               n = addConstant((char*) string);
+       else
+               n = -1;
+
+       if(n == -1)
+       {
+               bufferWriteU8(out, PUSH_STRING);
+               return bufferWriteHardString(out, string, length) + 1;
+       }
+       else if(n < 256)
+       {
+               bufferWriteU8(out, PUSH_CONSTANT);
+               return bufferWriteU8(out, n) + 1;
+       }
+       else
+       {
+               bufferWriteU8(out, PUSH_CONSTANT16);
+               return bufferWriteS16(out, n) + 1;
+       }
+}
+
+int bufferWriteString(Buffer out, byte *string, int length)
+{
+       if(SWF_versionNum < 5)
+       {
+               bufferWritePushOp(out);
+               bufferWriteS16(out, length+1);
+               bufferWriteU8(out, PUSH_STRING);
+               bufferWriteHardString(out, string, length);
+
+               return 4 + length;
+       }
+       else
+       {
+               int l;
+
+               if(out->pushloc == NULL)
+               {
+                       bufferWritePushOp(out);
+                       bufferWriteS16(out, 0);
+               }
+
+               l = bufferWriteConstantString(out, string, length);
+
+               bufferPatchPushLength(out, l);
+               return l;
+       }
+}
+
+int bufferWriteInt(Buffer out, int i)
+{
+       int len = 0;
+       unsigned char *p = (unsigned char *)&i;
+
+       if(out->pushloc == NULL || SWF_versionNum < 5)
+       {
+               len = 3;
+               bufferWritePushOp(out);
+               bufferWriteS16(out, 5);
+       }
+       else
+               bufferPatchPushLength(out, 5);
+
+       bufferWriteU8(out, PUSH_INT);
+
+       if(byteorder == SWF_LITTLE_ENDIAN)
+       {
+               bufferWriteU8(out, p[0]);
+               bufferWriteU8(out, p[1]);
+               bufferWriteU8(out, p[2]);
+               bufferWriteU8(out, p[3]);
+       }
+       else
+       {
+               bufferWriteU8(out, p[3]);
+               bufferWriteU8(out, p[2]);
+               bufferWriteU8(out, p[1]);
+               bufferWriteU8(out, p[0]);
+       }
+
+       return len + 5;
+}
+
+int bufferWriteDouble(Buffer out, double d)
+{
+       int len = 0;
+       unsigned char *p = (unsigned char *)&d;
+
+       if(out->pushloc == NULL || SWF_versionNum < 5)
+       {
+               len = 3;
+               bufferWritePushOp(out);
+               bufferWriteS16(out, 9);
+       }
+       else
+               bufferPatchPushLength(out, 5);
+
+       bufferWriteU8(out, PUSH_DOUBLE);
+
+       if(byteorder == SWF_LITTLE_ENDIAN)
+       {
+               bufferWriteU8(out, p[4]);
+               bufferWriteU8(out, p[5]);
+               bufferWriteU8(out, p[6]);
+               bufferWriteU8(out, p[7]);
+               bufferWriteU8(out, p[0]);
+               bufferWriteU8(out, p[1]);
+               bufferWriteU8(out, p[2]);
+               bufferWriteU8(out, p[3]);
+       }
+       else
+       {
+               bufferWriteU8(out, p[3]);
+               bufferWriteU8(out, p[2]);
+               bufferWriteU8(out, p[1]);
+               bufferWriteU8(out, p[0]);
+               bufferWriteU8(out, p[7]);
+               bufferWriteU8(out, p[6]);
+               bufferWriteU8(out, p[5]);
+               bufferWriteU8(out, p[4]);
+       }
+
+       return len + 9;
+}
+
+int bufferWriteNull(Buffer out)
+{
+       int len = 0;
+
+       if(out->pushloc == NULL || SWF_versionNum < 5)
+       {
+               len = 3;
+               bufferWritePushOp(out);
+               bufferWriteS16(out, 1);
+       }
+       else
+               bufferPatchPushLength(out, 1);
+
+       bufferWriteU8(out, PUSH_NULL);
+
+       return len + 1;
+}
+
+int bufferWriteBoolean(Buffer out, int val)
+{
+       int len = 0;
+
+       if(out->pushloc == NULL || SWF_versionNum < 5)
+       {
+               len = 3;
+               bufferWritePushOp(out);
+               bufferWriteS16(out, 2);
+       }
+       else
+               bufferPatchPushLength(out, 2);
+
+       bufferWriteU8(out, PUSH_BOOLEAN);
+       bufferWriteU8(out, val ? 1 : 0);
+
+       return len + 2;
+}
+
+int bufferWriteRegister(Buffer out, int num)
+{
+       int len = 0;
+
+       if(out->pushloc == NULL || SWF_versionNum < 5)
+       {
+               len = 3;
+               bufferWritePushOp(out);
+               bufferWriteS16(out, 2);
+       }
+       else
+               bufferPatchPushLength(out, 2);
+
+       bufferWriteU8(out, PUSH_REGISTER);
+       bufferWriteU8(out, num);
+
+       return len + 2;
+}
+
+int bufferWriteSetRegister(Buffer out, int num)
+{
+       bufferWriteU8(out, SWFACTION_SETREGISTER);
+       bufferWriteS16(out, 1);
+       bufferWriteU8(out, num);
+       return 4;
+}
+
+void lower(char *s)
+{
+       while(*s)
+       {
+               *s = tolower(*s);
+               ++s;
+       }
+}
+
+/* this code will eventually help to pop extra values off the
+ stack and make sure that continue and break address the proper
+ context
+ */
+static enum ctx *ctx_stack = {0};
+static int ctx_count = {0}, ctx_len = {0};
+void addctx(enum ctx val)
+{      if(ctx_count >= ctx_len)
+               ctx_stack = (enum ctx*) realloc(ctx_stack, (ctx_len += 10) * sizeof(enum ctx));
+       ctx_stack[ctx_count++] = val;
+}
+void delctx(enum ctx val)
+{      if(ctx_count <= 0 || ctx_stack[--ctx_count] != val)
+               SWF_error("consistency check in delctx");
+}
+
+int chkctx(enum ctx val)
+{      int n, ret = 0;
+       switch(val)
+       {       case CTX_FUNCTION:
+                       for(n = ctx_count ; --n >= 0 ; )
+                               switch(ctx_stack[n])
+                               {       case CTX_SWITCH:
+                                       case CTX_FOR_IN:
+                                               ret++;
+                                               break;
+                                       case CTX_FUNCTION:
+                                               return ret;
+                                       default: ; /* computers are stupid */
+                               }
+                       return -1;
+               case CTX_BREAK:
+                       for(n = ctx_count ; --n >= 0 ; )
+                               switch(ctx_stack[n])
+                               {       case CTX_SWITCH:
+                                       case CTX_LOOP:
+                                               return 0;
+                                       case CTX_FOR_IN:
+                                               return 1;
+                                       case CTX_FUNCTION:
+                                               return -1;
+                                       default: ; /* computers are stupid */
+                               }
+               case CTX_CONTINUE:
+                       for(n = ctx_count ; --n >= 0 ; )
+                               switch(ctx_stack[n])
+                               {       case CTX_LOOP:
+                                       case CTX_FOR_IN:
+                                               return 0;
+                                       case CTX_FUNCTION:
+                                               return -1;
+                                       default: ; /* computers are stupid */
+                               }
+               default: ; /* computers are stupid */
+       }
+       return 0;
+}
+
+/* replace MAGIC_CONTINUE_NUMBER and MAGIC_BREAK_NUMBER with jumps to
+        head or tail, respectively */
+/* jump offset is relative to end of jump instruction */
+/* I can't believe this actually worked */
+
+void bufferResolveJumps(Buffer out)
+{
+       byte *p = out->buffer;
+       int l, target;
+
+       while(p < out->pos)
+       {
+               if(*p & 0x80) /* then it's a multibyte instruction */
+               {
+                       if(*p == SWFACTION_BRANCHALWAYS)
+                       {
+       p += 3; /* plus instruction plus two-byte length */
+
+       if(*p == MAGIC_CONTINUE_NUMBER_LO &&
+                *(p+1) == MAGIC_CONTINUE_NUMBER_HI)
+       {
+               target = out->buffer - (p+2);
+               *p = target & 0xff;
+               *(p+1) = (target>>8) & 0xff;
+       }
+       else if(*p == MAGIC_BREAK_NUMBER_LO &&
+               *(p+1) == MAGIC_BREAK_NUMBER_HI)
+       {
+               target = out->pos - (p+2);
+               *p = target & 0xff;
+               *(p+1) = (target>>8) & 0xff;
+       }
+
+       p += 2;
+                       }
+                       else
+                       {
+       ++p;
+       l = *p;
+       ++p;
+       l += *p<<8;
+       ++p;
+
+       p += l;
+                       }
+               }
+               else
+                       ++p;
+       }
+}
+
+// handle SWITCH statement
+
+void bufferResolveSwitch(Buffer buffer, struct switchcases *slp)
+{      struct switchcase *scp;
+       int n, len;
+       unsigned char *output;
+                       
+       len = bufferLength(buffer);
+       for(n = 0, scp = slp->list ; n < slp->count ; n++, scp++)
+       {       scp->actlen = bufferLength(scp->action);
+               if((n < slp->count-1))
+                       scp->actlen += 5;
+               if(scp->cond)
+               {       scp->condlen = bufferLength(scp->cond) + 8;
+                       bufferWriteOp(buffer, SWFACTION_DUP);
+                       bufferConcat(buffer, scp->cond);
+                       bufferWriteOp(buffer, SWFACTION_NEWEQUALS);
+                       bufferWriteOp(buffer, SWFACTION_LOGICALNOT);
+                       bufferWriteOp(buffer, SWFACTION_BRANCHIFTRUE);
+                       bufferWriteS16(buffer, 2);
+                       bufferWriteS16(buffer, scp->actlen);
+               }
+               else
+                       scp->condlen = 0;
+               bufferConcat(buffer, scp->action);
+               bufferWriteOp(buffer, SWFACTION_BRANCHALWAYS);
+               bufferWriteS16(buffer, 2);
+               bufferWriteS16(buffer, scp->isbreak ? MAGIC_BREAK_NUMBER : 0);
+               if(!scp->cond)
+               {       slp->count = n+1;
+                       break;
+               }
+       }
+       for(n = 0, scp = slp->list ; n < slp->count ; n++, scp++)
+       {       len += scp->condlen;
+               output = buffer->buffer + len;
+               if((n < slp->count-1) && !scp->isbreak)
+               {       output[scp->actlen-2] = (scp+1)->condlen & 0xff;
+                       output[scp->actlen-1] = (scp+1)->condlen >> 8;
+               }
+               len += scp->actlen;
+       }
+}
+       
+int lookupSetProperty(char *string)
+{
+       lower(string);
+
+       if(strcmp(string,"x")==0)               return 0x0000;
+       if(strcmp(string,"y")==0)               return 0x3f80;
+       if(strcmp(string,"xscale")==0)  return 0x4000;
+       if(strcmp(string,"yscale")==0)  return 0x4040;
+       if(strcmp(string,"alpha")==0)           return 0x40c0;
+       if(strcmp(string,"visible")==0) return 0x40e0;
+       if(strcmp(string,"rotation")==0)        return 0x4120;
+       if(strcmp(string,"name")==0)            return 0x4140;
+       if(strcmp(string,"quality")==0) return 0x4180;
+       if(strcmp(string,"focusrect")==0)       return 0x4188;
+       if(strcmp(string,"soundbuftime")==0)    return 0x4190;
+
+       SWF_error("No such property: %s\n", string);
+       return -1;
+}
+
+int bufferWriteSetProperty(Buffer out, char *string)
+{
+       int property = lookupSetProperty(string);
+
+       bufferWriteU8(out, SWFACTION_PUSHDATA);
+       bufferWriteS16(out, 5);
+       bufferWriteU8(out, PUSH_PROPERTY);
+       bufferWriteS16(out, 0);
+       bufferWriteS16(out, property);
+
+       return 8;
+}
+
+int bufferWriteWTHITProperty(Buffer out)
+{
+       bufferWriteU8(out, SWFACTION_PUSHDATA);
+       bufferWriteS16(out, 5);
+       bufferWriteU8(out, PUSH_PROPERTY);
+       bufferWriteS16(out, 0);
+       bufferWriteS16(out, 0x4680);
+
+       return 8;
+}
+
+const char *lookupGetProperty(char *string)
+{
+       lower(string);
+
+       if(strcmp(string,"x")==0)               return "0";
+       if(strcmp(string,"y")==0)               return "1";
+       if(strcmp(string,"xscale")==0)  return "2";
+       if(strcmp(string,"yscale")==0)  return "3";
+       if(strcmp(string,"currentframe")==0)    return "4";
+       if(strcmp(string,"totalframes")==0)     return "5";
+       if(strcmp(string,"alpha")==0)           return "6";
+       if(strcmp(string,"visible")==0) return "7";
+       if(strcmp(string,"width")==0)           return "8";
+       if(strcmp(string,"height")==0)  return "9";
+       if(strcmp(string,"rotation")==0)        return "10";
+       if(strcmp(string,"target")==0)  return "11";
+       if(strcmp(string,"framesloaded")==0)    return "12";
+       if(strcmp(string,"name")==0)            return "13";
+       if(strcmp(string,"droptarget")==0)      return "14";
+       if(strcmp(string,"url")==0)             return "15";
+       if(strcmp(string,"quality")==0) return "16";
+       if(strcmp(string,"focusrect")==0)       return "17";
+       if(strcmp(string,"soundbuftime")==0)    return "18";
+
+       SWF_error("No such property: %s\n", string);
+       return "";
+}
+
+int bufferWriteGetProperty(Buffer out, char *string)
+{
+       const char *property = lookupGetProperty(string);
+
+       bufferWriteU8(out, SWFACTION_PUSHDATA);
+       bufferWriteS16(out, strlen(property)+2);
+       bufferWriteU8(out, PUSH_STRING);
+
+       return 4 + bufferWriteData(out, (byte*) property, strlen(property)+1);
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 2
+ * c-basic-offset: 2
+ * End:
+ */