1 /* MikMod sound library
\r
2 (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
\r
5 This library is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU Library General Public License as
\r
7 published by the Free Software Foundation; either version 2 of
\r
8 the License, or (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU Library General Public License for more details.
\r
15 You should have received a copy of the GNU Library General Public
\r
16 License along with this library; if not, write to the Free Software
\r
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
\r
21 /*==============================================================================
\r
23 $Id: munitrk.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $
\r
25 All routines dealing with the manipulation of UNITRK streams
\r
27 ==============================================================================*/
\r
29 #ifdef HAVE_CONFIG_H
\r
35 #include "mikmod_internals.h"
\r
37 /* Unibuffer chunk size */
\r
40 UWORD unioperands[UNI_LAST]={
\r
43 1, /* UNI_INSTRUMENT */
\r
44 1, /* UNI_PTEFFECT0 */
\r
45 1, /* UNI_PTEFFECT1 */
\r
46 1, /* UNI_PTEFFECT2 */
\r
47 1, /* UNI_PTEFFECT3 */
\r
48 1, /* UNI_PTEFFECT4 */
\r
49 1, /* UNI_PTEFFECT5 */
\r
50 1, /* UNI_PTEFFECT6 */
\r
51 1, /* UNI_PTEFFECT7 */
\r
52 1, /* UNI_PTEFFECT8 */
\r
53 1, /* UNI_PTEFFECT9 */
\r
54 1, /* UNI_PTEFFECTA */
\r
55 1, /* UNI_PTEFFECTB */
\r
56 1, /* UNI_PTEFFECTC */
\r
57 1, /* UNI_PTEFFECTD */
\r
58 1, /* UNI_PTEFFECTE */
\r
59 1, /* UNI_PTEFFECTF */
\r
60 1, /* UNI_S3MEFFECTA */
\r
61 1, /* UNI_S3MEFFECTD */
\r
62 1, /* UNI_S3MEFFECTE */
\r
63 1, /* UNI_S3MEFFECTF */
\r
64 1, /* UNI_S3MEFFECTI */
\r
65 1, /* UNI_S3MEFFECTQ */
\r
66 1, /* UNI_S3MEFFECTR */
\r
67 1, /* UNI_S3MEFFECTT */
\r
68 1, /* UNI_S3MEFFECTU */
\r
70 1, /* UNI_KEYFADE */
\r
71 2, /* UNI_VOLEFFECTS */
\r
72 1, /* UNI_XMEFFECT4 */
\r
73 1, /* UNI_XMEFFECT6 */
\r
74 1, /* UNI_XMEFFECTA */
\r
75 1, /* UNI_XMEFFECTE1 */
\r
76 1, /* UNI_XMEFFECTE2 */
\r
77 1, /* UNI_XMEFFECTEA */
\r
78 1, /* UNI_XMEFFECTEB */
\r
79 1, /* UNI_XMEFFECTG */
\r
80 1, /* UNI_XMEFFECTH */
\r
81 1, /* UNI_XMEFFECTL */
\r
82 1, /* UNI_XMEFFECTP */
\r
83 1, /* UNI_XMEFFECTX1 */
\r
84 1, /* UNI_XMEFFECTX2 */
\r
85 1, /* UNI_ITEFFECTG */
\r
86 1, /* UNI_ITEFFECTH */
\r
87 1, /* UNI_ITEFFECTI */
\r
88 1, /* UNI_ITEFFECTM */
\r
89 1, /* UNI_ITEFFECTN */
\r
90 1, /* UNI_ITEFFECTP */
\r
91 1, /* UNI_ITEFFECTT */
\r
92 1, /* UNI_ITEFFECTU */
\r
93 1, /* UNI_ITEFFECTW */
\r
94 1, /* UNI_ITEFFECTY */
\r
95 2, /* UNI_ITEFFECTZ */
\r
96 1, /* UNI_ITEFFECTS0 */
\r
97 2, /* UNI_ULTEFFECT9 */
\r
98 2, /* UNI_MEDSPEED */
\r
99 0, /* UNI_MEDEFFECTF1 */
\r
100 0, /* UNI_MEDEFFECTF2 */
\r
101 0, /* UNI_MEDEFFECTF3 */
\r
102 2, /* UNI_OKTARP */
\r
105 /* Sparse description of the internal module format
\r
106 ------------------------------------------------
\r
108 A UNITRK stream is an array of bytes representing a single track of a pattern.
\r
109 It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly
\r
113 [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
\r
115 |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
\r
117 The rep/len byte contains the number of bytes in the current row, _including_
\r
118 the length byte itself (So the LENGTH byte of row 0 in the previous example
\r
119 would have a value of 5). This makes it easy to search through a stream for a
\r
120 particular row. A track is concluded by a 0-value length byte.
\r
122 The upper 3 bits of the rep/len byte contain the number of times -1 this row
\r
123 is repeated for this track. (so a value of 7 means this row is repeated 8 times)
\r
125 Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being
\r
126 used. Each opcode can have a different number of operands. You can find the
\r
127 number of operands to a particular opcode by using the opcode as an index into
\r
128 the 'unioperands' table.
\r
132 /*========== Reading routines */
\r
134 static UBYTE *rowstart; /* startadress of a row */
\r
135 static UBYTE *rowend; /* endaddress of a row (exclusive) */
\r
136 static UBYTE *rowpc; /* current unimod(tm) programcounter */
\r
138 static UBYTE lastbyte; /* for UniSkipOpcode() */
\r
140 void UniSetRow(UBYTE* t)
\r
144 rowend = t?rowstart+(*(rowpc++)&0x1f):t;
\r
147 UBYTE UniGetByte(void)
\r
149 return lastbyte = (rowpc<rowend)?*(rowpc++):0;
\r
152 UWORD UniGetWord(void)
\r
154 return ((UWORD)UniGetByte()<<8)|UniGetByte();
\r
157 void UniSkipOpcode(void)
\r
159 if (lastbyte < UNI_LAST) {
\r
160 UWORD t = unioperands[lastbyte];
\r
167 /* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns
\r
168 NULL if the row can't be found. */
\r
169 UBYTE *UniFindRow(UBYTE* t,UWORD row)
\r
175 c = *t; /* get rep/len byte */
\r
176 if(!c) return NULL; /* zero ? -> end of track.. */
\r
177 l = (c>>5)+1; /* extract repeat value */
\r
178 if(l>row) break; /* reached wanted row? -> return pointer */
\r
179 row -= l; /* haven't reached row yet.. update row */
\r
180 t += c&0x1f; /* point t to the next row */
\r
185 /*========== Writing routines */
\r
187 static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */
\r
188 static UWORD unimax; /* buffer size */
\r
190 static UWORD unipc; /* buffer cursor */
\r
191 static UWORD unitt; /* current row index */
\r
192 static UWORD lastp; /* previous row index */
\r
194 /* Resets index-pointers to create a new track. */
\r
195 void UniReset(void)
\r
197 unitt = 0; /* reset index to rep/len byte */
\r
198 unipc = 1; /* first opcode will be written to index 1 */
\r
199 lastp = 0; /* no previous row yet */
\r
200 unibuf[0] = 0; /* clear rep/len byte */
\r
203 /* Expands the buffer */
\r
204 static BOOL UniExpand(int wanted)
\r
206 if ((unipc+wanted)>=unimax) {
\r
209 /* Expand the buffer by BUFPAGE bytes */
\r
210 newbuf=(UBYTE*)realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE));
\r
212 /* Check if realloc succeeded */
\r
223 /* Appends one byte of data to the current row of a track. */
\r
224 void UniWriteByte(UBYTE data)
\r
227 /* write byte to current position and update */
\r
228 unibuf[unipc++]=data;
\r
231 void UniWriteWord(UWORD data)
\r
233 if (UniExpand(2)) {
\r
234 unibuf[unipc++]=data>>8;
\r
235 unibuf[unipc++]=data&0xff;
\r
239 static BOOL MyCmp(UBYTE* a,UBYTE* b,UWORD l)
\r
244 if(*(a++)!=*(b++)) return 0;
\r
248 /* Closes the current row of a unitrk stream (updates the rep/len byte) and sets
\r
249 pointers to start a new row. */
\r
250 void UniNewline(void)
\r
254 n = (unibuf[lastp]>>5)+1; /* repeat of previous row */
\r
255 l = (unibuf[lastp]&0x1f); /* length of previous row */
\r
257 len = unipc-unitt; /* length of current row */
\r
259 /* Now, check if the previous and the current row are identical.. when they
\r
260 are, just increase the repeat field of the previous row */
\r
261 if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) {
\r
262 unibuf[lastp]+=0x20;
\r
265 if (UniExpand(unitt-unipc)) {
\r
266 /* current and previous row aren't equal... update the pointers */
\r
267 unibuf[unitt] = len;
\r
274 /* Terminates the current unitrk stream and returns a pointer to a copy of the
\r
276 UBYTE* UniDup(void)
\r
280 if (!UniExpand(unitt-unipc)) return NULL;
\r
283 if(!(d=(UBYTE *)_mm_malloc(unipc))) return NULL;
\r
284 memcpy(d,unibuf,unipc);
\r
293 if(!(unibuf=(UBYTE*)_mm_malloc(unimax*sizeof(UBYTE)))) return 0;
\r
297 void UniCleanup(void)
\r
299 if(unibuf) free(unibuf);
\r