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