Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / playercode / munitrk.c
diff --git a/apps/plugins/mikmod/playercode/munitrk.c b/apps/plugins/mikmod/playercode/munitrk.c
new file mode 100644 (file)
index 0000000..e8c3a44
--- /dev/null
@@ -0,0 +1,303 @@
+/*     MikMod sound library\r
+       (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS\r
+       for complete list.\r
+\r
+       This library is free software; you can redistribute it and/or modify\r
+       it under the terms of the GNU Library General Public License as\r
+       published by the Free Software Foundation; either version 2 of\r
+       the License, or (at your option) any later version.\r
\r
+       This program is distributed in the hope that it will be useful,\r
+       but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+       GNU Library General Public License for more details.\r
\r
+       You should have received a copy of the GNU Library General Public\r
+       License along with this library; if not, write to the Free Software\r
+       Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
+       02111-1307, USA.\r
+*/\r
+\r
+/*==============================================================================\r
+\r
+  $Id: munitrk.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
+\r
+  All routines dealing with the manipulation of UNITRK streams\r
+\r
+==============================================================================*/\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include <string.h>\r
+\r
+#include "mikmod_internals.h"\r
+\r
+/* Unibuffer chunk size */\r
+#define BUFPAGE  128\r
+\r
+UWORD unioperands[UNI_LAST]={\r
+       0, /* not used */\r
+       1, /* UNI_NOTE */\r
+       1, /* UNI_INSTRUMENT */\r
+       1, /* UNI_PTEFFECT0 */\r
+       1, /* UNI_PTEFFECT1 */\r
+       1, /* UNI_PTEFFECT2 */\r
+       1, /* UNI_PTEFFECT3 */\r
+       1, /* UNI_PTEFFECT4 */\r
+       1, /* UNI_PTEFFECT5 */\r
+       1, /* UNI_PTEFFECT6 */\r
+       1, /* UNI_PTEFFECT7 */\r
+       1, /* UNI_PTEFFECT8 */\r
+       1, /* UNI_PTEFFECT9 */\r
+       1, /* UNI_PTEFFECTA */\r
+       1, /* UNI_PTEFFECTB */\r
+       1, /* UNI_PTEFFECTC */\r
+       1, /* UNI_PTEFFECTD */\r
+       1, /* UNI_PTEFFECTE */\r
+       1, /* UNI_PTEFFECTF */\r
+       1, /* UNI_S3MEFFECTA */\r
+       1, /* UNI_S3MEFFECTD */\r
+       1, /* UNI_S3MEFFECTE */\r
+       1, /* UNI_S3MEFFECTF */\r
+       1, /* UNI_S3MEFFECTI */\r
+       1, /* UNI_S3MEFFECTQ */\r
+       1, /* UNI_S3MEFFECTR */\r
+       1, /* UNI_S3MEFFECTT */\r
+       1, /* UNI_S3MEFFECTU */\r
+       0, /* UNI_KEYOFF */\r
+       1, /* UNI_KEYFADE */\r
+       2, /* UNI_VOLEFFECTS */\r
+       1, /* UNI_XMEFFECT4 */\r
+       1, /* UNI_XMEFFECT6 */\r
+       1, /* UNI_XMEFFECTA */\r
+       1, /* UNI_XMEFFECTE1 */\r
+       1, /* UNI_XMEFFECTE2 */\r
+       1, /* UNI_XMEFFECTEA */\r
+       1, /* UNI_XMEFFECTEB */\r
+       1, /* UNI_XMEFFECTG */\r
+       1, /* UNI_XMEFFECTH */\r
+       1, /* UNI_XMEFFECTL */\r
+       1, /* UNI_XMEFFECTP */\r
+       1, /* UNI_XMEFFECTX1 */\r
+       1, /* UNI_XMEFFECTX2 */\r
+       1, /* UNI_ITEFFECTG */\r
+       1, /* UNI_ITEFFECTH */\r
+       1, /* UNI_ITEFFECTI */\r
+       1, /* UNI_ITEFFECTM */\r
+       1, /* UNI_ITEFFECTN */\r
+       1, /* UNI_ITEFFECTP */\r
+       1, /* UNI_ITEFFECTT */\r
+       1, /* UNI_ITEFFECTU */\r
+       1, /* UNI_ITEFFECTW */\r
+       1, /* UNI_ITEFFECTY */\r
+       2, /* UNI_ITEFFECTZ */\r
+       1, /* UNI_ITEFFECTS0 */\r
+       2, /* UNI_ULTEFFECT9 */\r
+       2, /* UNI_MEDSPEED */\r
+       0, /* UNI_MEDEFFECTF1 */\r
+       0, /* UNI_MEDEFFECTF2 */\r
+       0, /* UNI_MEDEFFECTF3 */\r
+       2, /* UNI_OKTARP */\r
+};\r
+\r
+/* Sparse description of the internal module format\r
+   ------------------------------------------------\r
+\r
+  A UNITRK stream is an array of bytes representing a single track of a pattern.\r
+It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly\r
+language):\r
+\r
+rrrlllll\r
+[REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..\r
+^                                         ^ ^\r
+|-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...\r
+\r
+  The rep/len byte contains the number of bytes in the current row, _including_\r
+the length byte itself (So the LENGTH byte of row 0 in the previous example\r
+would have a value of 5). This makes it easy to search through a stream for a\r
+particular row. A track is concluded by a 0-value length byte.\r
+\r
+  The upper 3 bits of the rep/len byte contain the number of times -1 this row\r
+is repeated for this track. (so a value of 7 means this row is repeated 8 times)\r
+\r
+  Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being\r
+used. Each opcode can have a different number of operands. You can find the\r
+number of operands to a particular opcode by using the opcode as an index into\r
+the 'unioperands' table.\r
+\r
+*/\r
+\r
+/*========== Reading routines */\r
+\r
+static UBYTE *rowstart; /* startadress of a row */\r
+static UBYTE *rowend;   /* endaddress of a row (exclusive) */\r
+static UBYTE *rowpc;    /* current unimod(tm) programcounter */\r
+\r
+static UBYTE lastbyte;  /* for UniSkipOpcode() */\r
+\r
+void UniSetRow(UBYTE* t)\r
+{\r
+       rowstart = t;\r
+       rowpc    = rowstart;\r
+       rowend   = t?rowstart+(*(rowpc++)&0x1f):t;\r
+}\r
+\r
+UBYTE UniGetByte(void)\r
+{\r
+       return lastbyte = (rowpc<rowend)?*(rowpc++):0;\r
+}\r
+\r
+UWORD UniGetWord(void)\r
+{\r
+       return ((UWORD)UniGetByte()<<8)|UniGetByte();\r
+}\r
+\r
+void UniSkipOpcode(void)\r
+{\r
+       if (lastbyte < UNI_LAST) {\r
+               UWORD t = unioperands[lastbyte];\r
+\r
+               while (t--)\r
+                       UniGetByte();\r
+       }\r
+}\r
+\r
+/* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns\r
+   NULL if the row can't be found. */\r
+UBYTE *UniFindRow(UBYTE* t,UWORD row)\r
+{\r
+       UBYTE c,l;\r
+\r
+       if(t)\r
+               while(1) {\r
+                       c = *t;             /* get rep/len byte */\r
+                       if(!c) return NULL; /* zero ? -> end of track.. */\r
+                       l = (c>>5)+1;       /* extract repeat value */\r
+                       if(l>row) break;    /* reached wanted row? -> return pointer */\r
+                       row -= l;           /* haven't reached row yet.. update row */\r
+                       t += c&0x1f;        /* point t to the next row */\r
+               }\r
+       return t;\r
+}\r
+\r
+/*========== Writing routines */\r
+\r
+static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */\r
+static UWORD unimax;  /* buffer size */\r
+\r
+static UWORD unipc;   /* buffer cursor */\r
+static UWORD unitt;   /* current row index */\r
+static UWORD lastp;   /* previous row index */\r
+\r
+/* Resets index-pointers to create a new track. */\r
+void UniReset(void)\r
+{\r
+       unitt     = 0;   /* reset index to rep/len byte */\r
+       unipc     = 1;   /* first opcode will be written to index 1 */\r
+       lastp     = 0;   /* no previous row yet */\r
+       unibuf[0] = 0;   /* clear rep/len byte */\r
+}\r
+\r
+/* Expands the buffer */\r
+static BOOL UniExpand(int wanted)\r
+{\r
+       if ((unipc+wanted)>=unimax) {\r
+               UBYTE *newbuf;\r
+\r
+               /* Expand the buffer by BUFPAGE bytes */\r
+               newbuf=(UBYTE*)realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE));\r
+\r
+               /* Check if realloc succeeded */\r
+               if(newbuf) {\r
+                       unibuf = newbuf;\r
+                       unimax+=BUFPAGE;\r
+                       return 1;\r
+               } else \r
+                       return 0;\r
+       }\r
+       return 1;\r
+}\r
+\r
+/* Appends one byte of data to the current row of a track. */\r
+void UniWriteByte(UBYTE data)\r
+{\r
+       if (UniExpand(1))\r
+               /* write byte to current position and update */\r
+               unibuf[unipc++]=data;\r
+}\r
+\r
+void UniWriteWord(UWORD data)\r
+{\r
+       if (UniExpand(2)) {\r
+               unibuf[unipc++]=data>>8;\r
+               unibuf[unipc++]=data&0xff;\r
+       }\r
+}\r
+\r
+static BOOL MyCmp(UBYTE* a,UBYTE* b,UWORD l)\r
+{\r
+       UWORD t;\r
+\r
+       for(t=0;t<l;t++)\r
+               if(*(a++)!=*(b++)) return 0;\r
+       return 1;\r
+}\r
+\r
+/* Closes the current row of a unitrk stream (updates the rep/len byte) and sets\r
+   pointers to start a new row. */\r
+void UniNewline(void)\r
+{\r
+       UWORD n,l,len;\r
+\r
+       n = (unibuf[lastp]>>5)+1;     /* repeat of previous row */\r
+       l = (unibuf[lastp]&0x1f);     /* length of previous row */\r
+\r
+       len = unipc-unitt;            /* length of current row */\r
+\r
+       /* Now, check if the previous and the current row are identical.. when they\r
+          are, just increase the repeat field of the previous row */\r
+       if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) {\r
+               unibuf[lastp]+=0x20;\r
+               unipc = unitt+1;\r
+       } else {\r
+               if (UniExpand(unitt-unipc)) {\r
+                       /* current and previous row aren't equal... update the pointers */\r
+                       unibuf[unitt] = len;\r
+                       lastp = unitt;\r
+                       unitt = unipc++;\r
+               }\r
+       }\r
+}\r
+\r
+/* Terminates the current unitrk stream and returns a pointer to a copy of the\r
+   stream. */\r
+UBYTE* UniDup(void)\r
+{\r
+       UBYTE *d;\r
+\r
+       if (!UniExpand(unitt-unipc)) return NULL;\r
+       unibuf[unitt] = 0;\r
+\r
+       if(!(d=(UBYTE *)_mm_malloc(unipc))) return NULL;\r
+       memcpy(d,unibuf,unipc);\r
+\r
+       return d;\r
+}\r
+\r
+BOOL UniInit(void)\r
+{\r
+       unimax = BUFPAGE;\r
+\r
+       if(!(unibuf=(UBYTE*)_mm_malloc(unimax*sizeof(UBYTE)))) return 0;\r
+       return 1;\r
+}\r
+\r
+void UniCleanup(void)\r
+{\r
+       if(unibuf) free(unibuf);\r
+       unibuf = NULL;\r
+}\r
+\r
+/* ex:set ts=4: */\r