Mercurial > ~darius > hgwebdir.cgi > mikmod
view playercode/munitrk.c @ 9:990c9dadb348
Initial revision
author | darius |
---|---|
date | Fri, 23 Jan 1998 16:05:10 +0000 |
parents | d14fd386d182 |
children |
line wrap: on
line source
/* 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; }