added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / munitrk.c
diff --git a/libs/oldmik/src/munitrk.c b/libs/oldmik/src/munitrk.c
new file mode 100644 (file)
index 0000000..e976e79
--- /dev/null
@@ -0,0 +1,346 @@
+/*\r
+\r
+Name:\r
+MUNITRK.C\r
+\r
+Description:\r
+All routines dealing with the manipulation of UNITRK(tm) streams\r
+\r
+Portability:\r
+All systems - all compilers\r
+\r
+*/\r
+#include <malloc.h>\r
+#include <string.h>\r
+#include "mikmod.h"\r
+\r
+#define BUFPAGE  128            /* smallest unibuffer size */\r
+#define TRESHOLD 16\r
+\r
+/* unibuffer is increased by BUFPAGE\r
+  bytes when unipc reaches unimax-TRESHOLD */\r
+\r
+\r
+\r
+/*\r
+       Ok.. I'll try to explain the new internal module format.. so here it goes:\r
+\r
+\r
+       The UNITRK(tm) Format:\r
+       ======================\r
+\r
+       A UNITRK stream is an array of bytes representing a single track\r
+       of a pattern. It's made up of 'repeat/length' bytes, opcodes and\r
+       operands (sort of a assembly 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
+\r
+       The rep/len byte contains the number of bytes in the current row,\r
+       _including_ the length byte itself (So the LENGTH byte of row 0 in the\r
+       previous example would have a value of 5). This makes it easy to search\r
+       through a stream for a particular row. A track is concluded by a 0-value\r
+       length byte.\r
+\r
+       The upper 3 bits of the rep/len byte contain the number of times -1 this\r
+       row is repeated for this track. (so a value of 7 means this row is repeated\r
+       8 times)\r
+\r
+       Opcodes can range from 1 to 255 but currently only opcodes 1 to 19 are\r
+       being used. Each opcode can have a different number of operands. You can\r
+       find the number of operands to a particular opcode by using the opcode\r
+       as an index into the 'unioperands' table.\r
+\r
+*/\r
+\r
+\r
+\r
+UWORD unioperands[256]={\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_S3MEFFECTT */\r
+       1,                              /* UNI_XMEFFECTA */\r
+       1,                              /* UNI_XMEFFECTG */\r
+       1,                              /* UNI_XMEFFECTH */\r
+       1                               /* UNI_XMEFFECTP */\r
+};\r
+\r
+\r
+/***************************************************************************\r
+>>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<<\r
+***************************************************************************/\r
+\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
+\r
+void UniSetRow(UBYTE *t)\r
+{\r
+       rowstart=t;\r
+       rowpc=rowstart;\r
+       rowend=rowstart+(*(rowpc++)&0x1f);\r
+}\r
+\r
+\r
+UBYTE UniGetByte(void)\r
+{\r
+       return (rowpc<rowend) ? *(rowpc++) : 0;\r
+}\r
+\r
+\r
+void UniSkipOpcode(UBYTE op)\r
+{\r
+       UWORD t=unioperands[op];\r
+       while(t--) UniGetByte();\r
+}\r
+\r
+\r
+UBYTE *UniFindRow(UBYTE *t,UWORD row)\r
+/*\r
+       Finds the address of row number 'row' in the UniMod(tm) stream 't'\r
+\r
+       returns NULL if the row can't be found.\r
+*/\r
+{\r
+       UBYTE c,l;\r
+\r
+       while(1){\r
+\r
+               c=*t;                                   /* get rep/len byte */\r
+\r
+               if(!c) return NULL;             /* zero ? -> end of track.. */\r
+\r
+               l=(c>>5)+1;                             /* extract repeat value */\r
+\r
+               if(l>row) break;                /* reached wanted row? -> return pointer */\r
+\r
+               row-=l;                                 /* havn't reached row yet.. update row */\r
+               t+=c&0x1f;                              /* point t to the next row */\r
+       }\r
+\r
+       return t;\r
+}\r
+\r
+\r
+\r
+/***************************************************************************\r
+>>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<<\r
+***************************************************************************/\r
+\r
+\r
+static UBYTE *unibuf;          /* pointer to the temporary unitrk buffer */\r
+static UWORD unimax;           /* maximum number of bytes to be written to this buffer */\r
+\r
+static UWORD unipc;                    /* index in the buffer where next opcode will be written */\r
+static UWORD unitt;            /* holds index of the rep/len byte of a row */\r
+static UWORD lastp;                    /* holds index to the previous row (needed for compressing) */\r
+\r
+\r
+void UniReset(void)\r
+/*\r
+       Resets index-pointers to create a new track.\r
+*/\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
+\r
+void UniWrite(UBYTE data)\r
+/*\r
+       Appends one byte of data to the current row of a track.\r
+*/\r
+{\r
+       /* write byte to current position and update */\r
+\r
+       unibuf[unipc++]=data;\r
+\r
+       /* Check if we've reached the end of the buffer */\r
+\r
+       if(unipc>(unimax-TRESHOLD)){\r
+\r
+               UBYTE *newbuf;\r
+\r
+               /* We've reached the end of the buffer, so expand\r
+                  the buffer by BUFPAGE bytes */\r
+\r
+               newbuf=(UBYTE *)realloc(unibuf,unimax+BUFPAGE);\r
+\r
+               /* Check if realloc succeeded */\r
+\r
+               if(newbuf!=NULL){\r
+                       unibuf=newbuf;\r
+                       unimax+=BUFPAGE;\r
+               }\r
+               else{\r
+                       /* realloc failed, so decrease unipc so we won't write beyond\r
+                          the end of the buffer.. I don't report the out-of-memory\r
+                          here; the UniDup() will fail anyway so that's where the\r
+                          loader sees that something went wrong */\r
+\r
+                       unipc--;\r
+               }\r
+       }\r
+}\r
+\r
+\r
+void UniInstrument(UBYTE ins)\r
+/*\r
+       Appends UNI_INSTRUMENT opcode to the unitrk stream.\r
+*/\r
+{\r
+       UniWrite(UNI_INSTRUMENT);\r
+       UniWrite(ins);\r
+}\r
+\r
+\r
+void UniNote(UBYTE note)\r
+/*\r
+       Appends UNI_NOTE opcode to the unitrk stream.\r
+*/\r
+{\r
+       UniWrite(UNI_NOTE);\r
+       UniWrite(note);\r
+}\r
+\r
+\r
+void UniPTEffect(UBYTE eff,UBYTE dat)\r
+/*\r
+       Appends UNI_PTEFFECTX opcode to the unitrk stream.\r
+*/\r
+{\r
+       if(eff!=0 || dat!=0){                           /* don't write empty effect */\r
+               UniWrite(UNI_PTEFFECT0+eff);\r
+               UniWrite(dat);\r
+       }\r
+}\r
+\r
+\r
+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
+       }\r
+       return 1;\r
+}\r
+\r
+\r
+void UniNewline(void)\r
+/*\r
+       Closes the current row of a unitrk stream (updates the rep/len byte)\r
+       and sets pointers to start a new row.\r
+*/\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..\r
+          when they are, just increase the repeat field of the previous row */\r
+\r
+       if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)){\r
+               unibuf[lastp]+=0x20;\r
+               unipc=unitt+1;\r
+       }\r
+       else{\r
+               /* current and previous row aren't equal.. so just update the pointers */\r
+\r
+               unibuf[unitt]=len;\r
+               lastp=unitt;\r
+               unitt=unipc;\r
+               unipc++;\r
+       }\r
+}\r
+\r
+\r
+UBYTE *UniDup(void)\r
+/*\r
+       Terminates the current unitrk stream and returns a pointer\r
+       to a copy of the stream.\r
+*/\r
+{\r
+       UBYTE *d;\r
+\r
+       unibuf[unitt]=0;\r
+\r
+       if((d=(UBYTE *)malloc(unipc))==NULL){\r
+               myerr=ERROR_ALLOC_STRUCT;\r
+               return NULL;\r
+       }\r
+       memcpy(d,unibuf,unipc);\r
+\r
+       return d;\r
+}\r
+\r
+\r
+UWORD TrkLen(UBYTE *t)\r
+/*\r
+       Determines the length (in rows) of a unitrk stream 't'\r
+*/\r
+{\r
+       UWORD len=0;\r
+       UBYTE c;\r
+\r
+       while(c=*t&0x1f){\r
+               len+=c;\r
+               t+=c;\r
+       }\r
+       len++;\r
+\r
+       return len;\r
+}\r
+\r
+\r
+BOOL UniInit(void)\r
+{\r
+       unimax=BUFPAGE;\r
+\r
+       if(!(unibuf=(UBYTE *)malloc(unimax))){\r
+               myerr=ERROR_ALLOC_STRUCT;\r
+               return 0;\r
+       }\r
+       return 1;\r
+}\r
+\r
+\r
+void UniCleanup(void)\r
+{\r
+       if(unibuf!=NULL) free(unibuf);\r
+       unibuf=NULL;\r
+}\r
+\r