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