1 /* MikMod sound library
2 (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
3 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 "unimod_priv.h"
34
35 #include <string.h>
36
37 /* Unibuffer chunk size */
38 #define BUFPAGE 128
39
40 UWORD unioperands[UNI_LAST] =
41 {
42 0, /* not used */
43 1, /* UNI_NOTE */
44 1, /* UNI_INSTRUMENT */
45 1, /* UNI_PTEFFECT0 */
46 1, /* UNI_PTEFFECT1 */
47 1, /* UNI_PTEFFECT2 */
48 1, /* UNI_PTEFFECT3 */
49 1, /* UNI_PTEFFECT4 */
50 1, /* UNI_PTEFFECT5 */
51 1, /* UNI_PTEFFECT6 */
52 1, /* UNI_PTEFFECT7 */
53 1, /* UNI_PTEFFECT8 */
54 1, /* UNI_PTEFFECT9 */
55 1, /* UNI_PTEFFECTA */
56 1, /* UNI_PTEFFECTB */
57 1, /* UNI_PTEFFECTC */
58 1, /* UNI_PTEFFECTD */
59 1, /* UNI_PTEFFECTE */
60 1, /* UNI_PTEFFECTF */
61 1, /* UNI_S3MEFFECTA */
62 1, /* UNI_S3MEFFECTD */
63 1, /* UNI_S3MEFFECTE */
64 1, /* UNI_S3MEFFECTF */
65 1, /* UNI_S3MEFFECTI */
66 1, /* UNI_S3MEFFECTQ */
67 1, /* UNI_S3MEFFECTR */
68 1, /* UNI_S3MEFFECTT */
69 1, /* UNI_S3MEFFECTU */
70 0, /* UNI_KEYOFF */
71 1, /* UNI_KEYFADE */
72 2, /* UNI_VOLEFFECTS */
73 1, /* UNI_XMEFFECT4 */
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 };
103
104 /* Sparse description of the internal module format
105 ------------------------------------------------
106
107 A UNITRK stream is an array of bytes representing a single track of a pattern.
108 It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly
109 language):
110
111 rrrlllll
112 [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
113 ^ ^ ^
114 |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
115
116 The rep/len byte contains the number of bytes in the current row, _including_
117 the length byte itself (So the LENGTH byte of row 0 in the previous example
118 would have a value of 5). This makes it easy to search through a stream for a
119 particular row. A track is concluded by a 0-value length byte.
120
121 The upper 3 bits of the rep/len byte contain the number of times -1 this row
122 is repeated for this track. (so a value of 7 means this row is repeated 8 times)
123
124 Opcodes can range from 1 to 255 but currently only opcodes 1 to 52 are being
125 used. Each opcode can have a different number of operands. You can find the
126 number of operands to a particular opcode by using the opcode as an index into
127 the 'unioperands' table.
128
129 */
130
131 /*========== Reading routines */
132
133 static UBYTE *rowstart; /* startadress of a row */
134 static UBYTE *rowend; /* endaddress of a row (exclusive) */
135 static UBYTE *rowpc; /* current unimod(tm) programcounter */
136
137
138 void
UniSetRow(UBYTE * t)139 UniSetRow (UBYTE * t)
140 {
141 rowstart = t;
142 rowpc = rowstart;
143 rowend = t ? rowstart + (*(rowpc++) & 0x1f) : t;
144 }
145
146 UBYTE
UniGetByte(void)147 UniGetByte (void)
148 {
149 return (rowpc < rowend) ? *(rowpc++) : 0;
150 }
151
152 UWORD
UniGetWord(void)153 UniGetWord (void)
154 {
155 return ((UWORD) UniGetByte () << 8) | UniGetByte ();
156 }
157
158 void
UniSkipOpcode(UBYTE op)159 UniSkipOpcode (UBYTE op)
160 {
161 if (op < UNI_LAST)
162 {
163 UWORD t = unioperands[op];
164
165 while (t--)
166 UniGetByte ();
167 }
168 }
169
170 /* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns
171 NULL if the row can't be found. */
172 UBYTE *
UniFindRow(UBYTE * t,UWORD row)173 UniFindRow (UBYTE * t, UWORD row)
174 {
175 UBYTE c, l;
176
177 if (t)
178 while (1)
179 {
180 c = *t; /* get rep/len byte */
181 if (!c)
182 return NULL; /* zero ? -> end of track.. */
183 l = (c >> 5) + 1; /* extract repeat value */
184 if (l > row)
185 break; /* reached wanted row? -> return pointer */
186 row -= l; /* haven't reached row yet.. update row */
187 t += c & 0x1f; /* point t to the next row */
188 }
189 return t;
190 }
191
192 /*========== Writing routines */
193
194 static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */
195 static UWORD unimax; /* buffer size */
196
197 static UWORD unipc; /* buffer cursor */
198 static UWORD unitt; /* current row index */
199 static UWORD lastp; /* previous row index */
200
201 /* Resets index-pointers to create a new track. */
202 void
UniReset(void)203 UniReset (void)
204 {
205 unitt = 0; /* reset index to rep/len byte */
206 unipc = 1; /* first opcode will be written to index 1 */
207 lastp = 0; /* no previous row yet */
208 unibuf[0] = 0; /* clear rep/len byte */
209 }
210
211 /* Expands the buffer */
212 static BOOL
UniExpand(int wanted)213 UniExpand (int wanted)
214 {
215 if ((unipc + wanted) >= unimax)
216 {
217 UBYTE *newbuf;
218
219 /* Expand the buffer by BUFPAGE bytes */
220 newbuf = (UBYTE *) realloc (unibuf, (unimax + BUFPAGE) * sizeof (UBYTE));
221
222 /* Check if realloc succeeded */
223 if (newbuf)
224 {
225 unibuf = newbuf;
226 unimax += BUFPAGE;
227 return 1;
228 }
229 else
230 return 0;
231 }
232 return 1;
233 }
234
235 /* Appends one byte of data to the current row of a track. */
236 void
UniWriteByte(UBYTE data)237 UniWriteByte (UBYTE data)
238 {
239 if (UniExpand (1))
240 /* write byte to current position and update */
241 unibuf[unipc++] = data;
242 }
243
244 void
UniWriteWord(UWORD data)245 UniWriteWord (UWORD data)
246 {
247 if (UniExpand (2))
248 {
249 unibuf[unipc++] = data >> 8;
250 unibuf[unipc++] = data & 0xff;
251 }
252 }
253
254 static BOOL
MyCmp(UBYTE * a,UBYTE * b,UWORD l)255 MyCmp (UBYTE * a, UBYTE * b, UWORD l)
256 {
257 UWORD t;
258
259 for (t = 0; t < l; t++)
260 if (*(a++) != *(b++))
261 return 0;
262 return 1;
263 }
264
265 /* Closes the current row of a unitrk stream (updates the rep/len byte) and sets
266 pointers to start a new row. */
267 void
UniNewline(void)268 UniNewline (void)
269 {
270 UWORD n, l, len;
271
272 n = (unibuf[lastp] >> 5) + 1; /* repeat of previous row */
273 l = (unibuf[lastp] & 0x1f); /* length of previous row */
274
275 len = unipc - unitt; /* length of current row */
276
277 /* Now, check if the previous and the current row are identical.. when they
278 are, just increase the repeat field of the previous row */
279 if (n < 8 && len == l && MyCmp (&unibuf[lastp + 1], &unibuf[unitt + 1], len - 1))
280 {
281 unibuf[lastp] += 0x20;
282 unipc = unitt + 1;
283 }
284 else
285 {
286 if (UniExpand (unitt - unipc))
287 {
288 /* current and previous row aren't equal... update the pointers */
289 unibuf[unitt] = len;
290 lastp = unitt;
291 unitt = unipc++;
292 }
293 }
294 }
295
296 /* Terminates the current unitrk stream and returns a pointer to a copy of the
297 stream. */
298 UBYTE *
UniDup(void)299 UniDup (void)
300 {
301 UBYTE *d;
302
303 if (!UniExpand (unitt - unipc))
304 return NULL;
305 unibuf[unitt] = 0;
306
307 if (!(d = (UBYTE *) _mm_malloc (unipc)))
308 return NULL;
309 memcpy (d, unibuf, unipc);
310
311 return d;
312 }
313
314 BOOL
UniInit(void)315 UniInit (void)
316 {
317 unimax = BUFPAGE;
318
319 if (!(unibuf = (UBYTE *) _mm_malloc (unimax * sizeof (UBYTE))))
320 return 0;
321 return 1;
322 }
323
324 void
UniCleanup(void)325 UniCleanup (void)
326 {
327 if (unibuf)
328 free (unibuf);
329 unibuf = NULL;
330 }
331
332 /* ex:set ts=4: */
333