Mercurial > ~darius > hgwebdir.cgi > mikmod
diff playercode/munitrk.c @ 6:d14fd386d182
Initial entry of mikmod into the CVS tree.
author | darius |
---|---|
date | Fri, 23 Jan 1998 16:05:09 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/munitrk.c Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,307 @@ +/* + +Name: +MUNITRK.C + +Description: +All routines dealing with the manipulation of UNITRK(tm) streams + +Portability: +All systems - all compilers + +*/ + +#include <string.h> +#include "mikmod.h" + +#define BUFPAGE 128 // smallest unibuffer size +#define TRESHOLD 16 + +UWORD unioperands[256] = +{ 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_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_ITEFFECTU + 1, // UNI_ITEFFECTW + 1, // UNI_ITEFFECTY + 1 // UNI_ITEFFECTS0 +}; + + +// unibuffer is increased by BUFPAGE +// bytes when unipc reaches unimax-TRESHOLD + + +/* + Ok.. I'll try to explain the new internal module format.. so here it goes: + + + The UNITRK(tm) 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 45 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. + +*/ + + +/*************************************************************************** +>>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<< +***************************************************************************/ + + +static UBYTE *rowstart; // startadress of a row +static UBYTE *rowend; // endaddress of a row (exclusive) +static UBYTE *rowpc; // current unimod(tm) programcounter + + +void UniSetRow(UBYTE *t) +{ + rowstart = t; + rowpc = rowstart; + rowend = rowstart+(*(rowpc++)&0x1f); +} + + +UBYTE UniGetByte(void) +{ + return (rowpc<rowend) ? *(rowpc++) : 0; +} + + +void UniSkipOpcode(UBYTE op) +{ + UWORD t = unioperands[op]; + while(t--) UniGetByte(); +} + + +UBYTE *UniFindRow(UBYTE *t, UWORD row) +// Finds the address of row number 'row' in the UniMod(tm) stream 't' +// returns NULL if the row can't be found. +{ + UBYTE c,l; + + 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; +} + + + +/*************************************************************************** +>>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<< +***************************************************************************/ + + +static UBYTE *unibuf; // pointer to the temporary unitrk buffer +static UWORD unimax; // maximum number of bytes to be written to this buffer + +static UWORD unipc; // index in the buffer where next opcode will be written +static UWORD unitt; // holds index of the rep/len byte of a row +static UWORD lastp; // holds index to the previous row (needed for compressing) + + +void UniReset(void) +// Resets index-pointers to create a new track. +{ + 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 +} + + +void UniWrite(UBYTE data) +// Appends one byte of data to the current row of a track. +{ + // write byte to current position and update + + unibuf[unipc++] = data; + + // Check if we've reached the end of the buffer + + if(unipc > (unimax-TRESHOLD)) + { UBYTE *newbuf; + + // We've reached the end of the buffer, so expand + // the buffer by BUFPAGE bytes + + newbuf = (UBYTE *)realloc(unibuf, unimax+BUFPAGE); + + // Check if realloc succeeded + + if(newbuf!=NULL) + { unibuf = newbuf; + unimax+=BUFPAGE; + } else + { // realloc failed, so decrease unipc so we won't write beyond + // the end of the buffer.. I don't report the out-of-memory + // here; the UniDup() will fail anyway so that's where the + // loader sees that something went wrong + + unipc--; + } + } +} + + +BOOL MyCmp(UBYTE *a, UBYTE *b, UWORD l) +{ + UWORD t; + + for(t=0; t<l; t++) + if(*(a++) != *(b++)) return 0; + return 1; +} + + +void UniNewline(void) +// Closes the current row of a unitrk stream (updates the rep/len byte) +// and sets pointers to start a new row. +{ + 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 + { // current and previous row aren't equal.. so just update the pointers + + unibuf[unitt] = len; + lastp = unitt; + unitt = unipc; + unipc++; + } +} + + +UBYTE *UniDup(void) +// Terminates the current unitrk stream and returns a pointer +// to a copy of the stream. +{ + UBYTE *d; + + unibuf[unitt] = 0; + + if((d=(UBYTE *)_mm_malloc(unipc))==NULL) return NULL; + memcpy(d,unibuf,unipc); + + return d; +} + + +UWORD TrkLen(UBYTE *t) +// Determines the length (in rows) of a unitrk stream 't' +{ + UWORD len = 0; + UBYTE c; + + while(c = *t & 0x1f) + { len += c; + t += c; + } + len++; + + return len; +} + + +BOOL UniInit(void) +{ + unimax = BUFPAGE; + + if(!(unibuf=(UBYTE *)_mm_malloc(unimax))) return 0; + return 1; +} + + +void UniCleanup(void) +{ + if(unibuf!=NULL) free(unibuf); + unibuf = NULL; +} +