1 /*	MikMod sound library
2 	(c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
3 	for complete list.
4 
5 	This library is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU Library General Public License as
7 	published by the Free Software Foundation; either version 2 of
8 	the License, or (at your option) any later version.
9 
10 	This program is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU Library General Public License for more details.
14 
15 	You should have received a copy of the GNU Library General Public
16 	License along with this library; if not, write to the Free Software
17 	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 	02111-1307, USA.
19 */
20 
21 /*==============================================================================
22 
23   $Id$
24 
25   All routines dealing with the manipulation of UNITRK streams
26 
27 ==============================================================================*/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include "mikmod_internals.h"
34 
35 #include <string.h>
36 
37 /* Unibuffer chunk size */
38 #define BUFPAGE  128
39 
40 const UWORD unioperands[UNI_LAST] = {
41 	0, /* not used */
42 	1, /* UNI_NOTE */
43 	1, /* UNI_INSTRUMENT */
44 	1, /* UNI_PTEFFECT0 */
45 	1, /* UNI_PTEFFECT1 */
46 	1, /* UNI_PTEFFECT2 */
47 	1, /* UNI_PTEFFECT3 */
48 	1, /* UNI_PTEFFECT4 */
49 	1, /* UNI_PTEFFECT5 */
50 	1, /* UNI_PTEFFECT6 */
51 	1, /* UNI_PTEFFECT7 */
52 	1, /* UNI_PTEFFECT8 */
53 	1, /* UNI_PTEFFECT9 */
54 	1, /* UNI_PTEFFECTA */
55 	1, /* UNI_PTEFFECTB */
56 	1, /* UNI_PTEFFECTC */
57 	1, /* UNI_PTEFFECTD */
58 	1, /* UNI_PTEFFECTE */
59 	1, /* UNI_PTEFFECTF */
60 	1, /* UNI_S3MEFFECTA */
61 	1, /* UNI_S3MEFFECTD */
62 	1, /* UNI_S3MEFFECTE */
63 	1, /* UNI_S3MEFFECTF */
64 	1, /* UNI_S3MEFFECTI */
65 	1, /* UNI_S3MEFFECTQ */
66 	1, /* UNI_S3MEFFECTR */
67 	1, /* UNI_S3MEFFECTT */
68 	1, /* UNI_S3MEFFECTU */
69 	0, /* UNI_KEYOFF */
70 	1, /* UNI_KEYFADE */
71 	2, /* UNI_VOLEFFECTS */
72 	1, /* UNI_XMEFFECT4 */
73 	1, /* UNI_XMEFFECT6 */
74 	1, /* UNI_XMEFFECTA */
75 	1, /* UNI_XMEFFECTE1 */
76 	1, /* UNI_XMEFFECTE2 */
77 	1, /* UNI_XMEFFECTEA */
78 	1, /* UNI_XMEFFECTEB */
79 	1, /* UNI_XMEFFECTG */
80 	1, /* UNI_XMEFFECTH */
81 	1, /* UNI_XMEFFECTL */
82 	1, /* UNI_XMEFFECTP */
83 	1, /* UNI_XMEFFECTX1 */
84 	1, /* UNI_XMEFFECTX2 */
85 	1, /* UNI_ITEFFECTG */
86 	1, /* UNI_ITEFFECTH */
87 	1, /* UNI_ITEFFECTI */
88 	1, /* UNI_ITEFFECTM */
89 	1, /* UNI_ITEFFECTN */
90 	1, /* UNI_ITEFFECTP */
91 	1, /* UNI_ITEFFECTT */
92 	1, /* UNI_ITEFFECTU */
93 	1, /* UNI_ITEFFECTW */
94 	1, /* UNI_ITEFFECTY */
95 	2, /* UNI_ITEFFECTZ */
96 	1, /* UNI_ITEFFECTS0 */
97 	2, /* UNI_ULTEFFECT9 */
98 	2, /* UNI_MEDSPEED */
99 	0, /* UNI_MEDEFFECTF1 */
100 	0, /* UNI_MEDEFFECTF2 */
101 	0, /* UNI_MEDEFFECTF3 */
102 	2, /* UNI_OKTARP */
103 };
104 
105 /* Sparse description of the internal module format
106    ------------------------------------------------
107 
108   A UNITRK stream is an array of bytes representing a single track of a pattern.
109 It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly
110 language):
111 
112 rrrlllll
113 [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
114 ^                                         ^ ^
115 |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
116 
117   The rep/len byte contains the number of bytes in the current row, _including_
118 the length byte itself (So the LENGTH byte of row 0 in the previous example
119 would have a value of 5). This makes it easy to search through a stream for a
120 particular row. A track is concluded by a 0-value length byte.
121 
122   The upper 3 bits of the rep/len byte contain the number of times -1 this row
123 is repeated for this track. (so a value of 7 means this row is repeated 8 times)
124 
125   Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being
126 used. Each opcode can have a different number of operands. You can find the
127 number of operands to a particular opcode by using the opcode as an index into
128 the 'unioperands' table.
129 
130 */
131 
132 /*========== Reading routines */
133 
134 static	UBYTE *rowstart; /* startadress of a row */
135 static	UBYTE *rowend;   /* endaddress of a row (exclusive) */
136 static	UBYTE *rowpc;    /* current unimod(tm) programcounter */
137 
138 static	UBYTE lastbyte;  /* for UniSkipOpcode() */
139 
UniSetRow(UBYTE * t)140 void UniSetRow(UBYTE* t)
141 {
142 	rowstart = t;
143 	rowpc    = rowstart;
144 	rowend   = t?rowstart+(*(rowpc++)&0x1f):t;
145 }
146 
UniGetByte(void)147 UBYTE UniGetByte(void)
148 {
149 	return lastbyte = (rowpc<rowend)?*(rowpc++):0;
150 }
151 
UniGetWord(void)152 UWORD UniGetWord(void)
153 {
154 	return ((UWORD)UniGetByte()<<8)|UniGetByte();
155 }
156 
UniSkipOpcode(void)157 void UniSkipOpcode(void)
158 {
159 	if (lastbyte < UNI_LAST) {
160 		UWORD t = unioperands[lastbyte];
161 
162 		while (t--)
163 			UniGetByte();
164 	}
165 }
166 
167 /* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns
168    NULL if the row can't be found. */
UniFindRow(UBYTE * t,UWORD row)169 UBYTE *UniFindRow(UBYTE* t,UWORD row)
170 {
171 	UBYTE c,l;
172 
173 	if(t)
174 		while(1) {
175 			c = *t;             /* get rep/len byte */
176 			if(!c) return NULL; /* zero ? -> end of track.. */
177 			l = (c>>5)+1;       /* extract repeat value */
178 			if(l>row) break;    /* reached wanted row? -> return pointer */
179 			row -= l;           /* haven't reached row yet.. update row */
180 			t += c&0x1f;        /* point t to the next row */
181 		}
182 	return t;
183 }
184 
185 /*========== Writing routines */
186 
187 static	UBYTE *unibuf; /* pointer to the temporary unitrk buffer */
188 static	UWORD unimax;  /* buffer size */
189 
190 static	UWORD unipc;   /* buffer cursor */
191 static	UWORD unitt;   /* current row index */
192 static	UWORD lastp;   /* previous row index */
193 
194 /* Resets index-pointers to create a new track. */
UniReset(void)195 void UniReset(void)
196 {
197 	unitt     = 0;   /* reset index to rep/len byte */
198 	unipc     = 1;   /* first opcode will be written to index 1 */
199 	lastp     = 0;   /* no previous row yet */
200 	unibuf[0] = 0;   /* clear rep/len byte */
201 }
202 
203 /* Expands the buffer */
UniExpand(int wanted)204 static BOOL UniExpand(int wanted)
205 {
206 	if ((unipc+wanted)>=unimax) {
207 		UBYTE *newbuf;
208 
209 		/* Expand the buffer by BUFPAGE bytes */
210 		newbuf=(UBYTE*)MikMod_realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE));
211 
212 		/* Check if MikMod_realloc succeeded */
213 		if(newbuf) {
214 			unibuf = newbuf;
215 			unimax+=BUFPAGE;
216 			return 1;
217 		} else
218 			return 0;
219 	}
220 	return 1;
221 }
222 
223 /* Appends one byte of data to the current row of a track. */
UniWriteByte(UBYTE data)224 void UniWriteByte(UBYTE data)
225 {
226 	if (UniExpand(1))
227 		/* write byte to current position and update */
228 		unibuf[unipc++]=data;
229 }
230 
UniWriteWord(UWORD data)231 void UniWriteWord(UWORD data)
232 {
233 	if (UniExpand(2)) {
234 		unibuf[unipc++]=data>>8;
235 		unibuf[unipc++]=data&0xff;
236 	}
237 }
238 
MyCmp(const UBYTE * a,const UBYTE * b,UWORD l)239 static BOOL MyCmp(const UBYTE* a,const UBYTE* b,UWORD l)
240 {
241 	UWORD t;
242 
243 	for(t=0;t<l;t++)
244 		if(*(a++)!=*(b++)) return 0;
245 	return 1;
246 }
247 
248 /* Closes the current row of a unitrk stream (updates the rep/len byte) and sets
249    pointers to start a new row. */
UniNewline(void)250 void UniNewline(void)
251 {
252 	UWORD n,l,len;
253 
254 	n = (unibuf[lastp]>>5)+1;     /* repeat of previous row */
255 	l = (unibuf[lastp]&0x1f);     /* length of previous row */
256 
257 	len = unipc-unitt;            /* length of current row */
258 
259 	/* Now, check if the previous and the current row are identical.. when they
260 	   are, just increase the repeat field of the previous row */
261 	if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) {
262 		unibuf[lastp]+=0x20;
263 		unipc = unitt+1;
264 	} else {
265 		if (UniExpand(unitt-unipc)) {
266 			/* current and previous row aren't equal... update the pointers */
267 			unibuf[unitt] = len;
268 			lastp = unitt;
269 			unitt = unipc++;
270 		}
271 	}
272 }
273 
274 /* Terminates the current unitrk stream and returns a pointer to a copy of the
275    stream. */
UniDup(void)276 UBYTE* UniDup(void)
277 {
278 	void *d;
279 
280 	if (!UniExpand(unipc-unitt)) return NULL;
281 	unibuf[unitt] = 0;
282 
283 	if(!(d=MikMod_malloc(unipc))) return NULL;
284 	memcpy(d,unibuf,unipc);
285 
286 	return (UBYTE *)d;
287 }
288 
UniInit(void)289 BOOL UniInit(void)
290 {
291 	unimax = BUFPAGE;
292 
293 	if(!(unibuf=(UBYTE*)MikMod_malloc(unimax*sizeof(UBYTE)))) return 0;
294 	return 1;
295 }
296 
UniCleanup(void)297 void UniCleanup(void)
298 {
299 	MikMod_free(unibuf);
300 	unibuf = NULL;
301 }
302 
303 /* ex:set ts=4: */
304