added page flipping/scrolling VBE calls
[dosrtxon] / libs / mikmod / playercode / munitrk.c
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
140 void UniSetRow(UBYTE* t)
141 {
142         rowstart = t;
143         rowpc    = rowstart;
144         rowend   = t?rowstart+(*(rowpc++)&0x1f):t;
145 }
146
147 UBYTE UniGetByte(void)
148 {
149         return lastbyte = (rowpc<rowend)?*(rowpc++):0;
150 }
151
152 UWORD UniGetWord(void)
153 {
154         return ((UWORD)UniGetByte()<<8)|UniGetByte();
155 }
156
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. */
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. */
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 */
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. */
224 void UniWriteByte(UBYTE data)
225 {
226         if (UniExpand(1))
227                 /* write byte to current position and update */
228                 unibuf[unipc++]=data;
229 }
230
231 void UniWriteWord(UWORD data)
232 {
233         if (UniExpand(2)) {
234                 unibuf[unipc++]=data>>8;
235                 unibuf[unipc++]=data&0xff;
236         }
237 }
238
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. */
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. */
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
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
297 void UniCleanup(void)
298 {
299         MikMod_free(unibuf);
300         unibuf = NULL;
301 }
302
303 /* ex:set ts=4: */