comparison playercode/munitrk.c @ 6:d14fd386d182

Initial entry of mikmod into the CVS tree.
author darius
date Fri, 23 Jan 1998 16:05:09 +0000
parents
children
comparison
equal deleted inserted replaced
5:42e11dc15457 6:d14fd386d182
1 /*
2
3 Name:
4 MUNITRK.C
5
6 Description:
7 All routines dealing with the manipulation of UNITRK(tm) streams
8
9 Portability:
10 All systems - all compilers
11
12 */
13
14 #include <string.h>
15 #include "mikmod.h"
16
17 #define BUFPAGE 128 // smallest unibuffer size
18 #define TRESHOLD 16
19
20 UWORD unioperands[256] =
21 { 0, // not used
22 1, // UNI_NOTE
23 1, // UNI_INSTRUMENT
24 1, // UNI_PTEFFECT0
25 1, // UNI_PTEFFECT1
26 1, // UNI_PTEFFECT2
27 1, // UNI_PTEFFECT3
28 1, // UNI_PTEFFECT4
29 1, // UNI_PTEFFECT5
30 1, // UNI_PTEFFECT6
31 1, // UNI_PTEFFECT7
32 1, // UNI_PTEFFECT8
33 1, // UNI_PTEFFECT9
34 1, // UNI_PTEFFECTA
35 1, // UNI_PTEFFECTB
36 1, // UNI_PTEFFECTC
37 1, // UNI_PTEFFECTD
38 1, // UNI_PTEFFECTE
39 1, // UNI_PTEFFECTF
40 1, // UNI_S3MEFFECTA
41 1, // UNI_S3MEFFECTD
42 1, // UNI_S3MEFFECTE
43 1, // UNI_S3MEFFECTF
44 1, // UNI_S3MEFFECTI
45 1, // UNI_S3MEFFECTQ
46 1, // UNI_S3MEFFECTR
47 1, // UNI_S3MEFFECTT
48 1, // UNI_S3MEFFECTU
49 0, // UNI_KEYOFF
50 1, // UNI_KEYFADE
51 2, // UNI_VOLEFFECTS
52 1, // UNI_XMEFFECT4
53 1, // UNI_XMEFFECTA
54 1, // UNI_XMEFFECTE1
55 1, // UNI_XMEFFECTE2
56 1, // UNI_XMEFFECTEA
57 1, // UNI_XMEFFECTEB
58 1, // UNI_XMEFFECTG
59 1, // UNI_XMEFFECTH
60 1, // UNI_XMEFFECTL
61 1, // UNI_XMEFFECTP
62 1, // UNI_XMEFFECTX1
63 1, // UNI_XMEFFECTX2
64 1, // UNI_ITEFFECTG
65 1, // UNI_ITEFFECTH
66 1, // UNI_ITEFFECTI
67 1, // UNI_ITEFFECTM
68 1, // UNI_ITEFFECTN
69 1, // UNI_ITEFFECTP
70 1, // UNI_ITEFFECTU
71 1, // UNI_ITEFFECTW
72 1, // UNI_ITEFFECTY
73 1 // UNI_ITEFFECTS0
74 };
75
76
77 // unibuffer is increased by BUFPAGE
78 // bytes when unipc reaches unimax-TRESHOLD
79
80
81 /*
82 Ok.. I'll try to explain the new internal module format.. so here it goes:
83
84
85 The UNITRK(tm) Format:
86 ======================
87
88 A UNITRK stream is an array of bytes representing a single track
89 of a pattern. It's made up of 'repeat/length' bytes, opcodes and
90 operands (sort of a assembly language):
91
92 rrrlllll
93 [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
94 ^ ^ ^
95 |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
96
97
98 The rep/len byte contains the number of bytes in the current row,
99 _including_ the length byte itself (So the LENGTH byte of row 0 in the
100 previous example would have a value of 5). This makes it easy to search
101 through a stream for a particular row. A track is concluded by a 0-value
102 length byte.
103
104 The upper 3 bits of the rep/len byte contain the number of times -1 this
105 row is repeated for this track. (so a value of 7 means this row is repeated
106 8 times)
107
108 Opcodes can range from 1 to 255 but currently only opcodes 1 to 45 are
109 being used. Each opcode can have a different number of operands. You can
110 find the number of operands to a particular opcode by using the opcode
111 as an index into the 'unioperands' table.
112
113 */
114
115
116 /***************************************************************************
117 >>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<<
118 ***************************************************************************/
119
120
121 static UBYTE *rowstart; // startadress of a row
122 static UBYTE *rowend; // endaddress of a row (exclusive)
123 static UBYTE *rowpc; // current unimod(tm) programcounter
124
125
126 void UniSetRow(UBYTE *t)
127 {
128 rowstart = t;
129 rowpc = rowstart;
130 rowend = rowstart+(*(rowpc++)&0x1f);
131 }
132
133
134 UBYTE UniGetByte(void)
135 {
136 return (rowpc<rowend) ? *(rowpc++) : 0;
137 }
138
139
140 void UniSkipOpcode(UBYTE op)
141 {
142 UWORD t = unioperands[op];
143 while(t--) UniGetByte();
144 }
145
146
147 UBYTE *UniFindRow(UBYTE *t, UWORD row)
148 // Finds the address of row number 'row' in the UniMod(tm) stream 't'
149 // returns NULL if the row can't be found.
150 {
151 UBYTE c,l;
152
153 while(1)
154 { c = *t; // get rep/len byte
155 if(!c) return NULL; // zero ? -> end of track..
156 l = (c>>5)+1; // extract repeat value
157 if(l>row) break; // reached wanted row? -> return pointer
158 row -= l; // haven't reached row yet.. update row
159 t += c&0x1f; // point t to the next row
160 }
161
162 return t;
163 }
164
165
166
167 /***************************************************************************
168 >>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<<
169 ***************************************************************************/
170
171
172 static UBYTE *unibuf; // pointer to the temporary unitrk buffer
173 static UWORD unimax; // maximum number of bytes to be written to this buffer
174
175 static UWORD unipc; // index in the buffer where next opcode will be written
176 static UWORD unitt; // holds index of the rep/len byte of a row
177 static UWORD lastp; // holds index to the previous row (needed for compressing)
178
179
180 void UniReset(void)
181 // Resets index-pointers to create a new track.
182 {
183 unitt = 0; // reset index to rep/len byte
184 unipc = 1; // first opcode will be written to index 1
185 lastp = 0; // no previous row yet
186 unibuf[0] = 0; // clear rep/len byte
187 }
188
189
190 void UniWrite(UBYTE data)
191 // Appends one byte of data to the current row of a track.
192 {
193 // write byte to current position and update
194
195 unibuf[unipc++] = data;
196
197 // Check if we've reached the end of the buffer
198
199 if(unipc > (unimax-TRESHOLD))
200 { UBYTE *newbuf;
201
202 // We've reached the end of the buffer, so expand
203 // the buffer by BUFPAGE bytes
204
205 newbuf = (UBYTE *)realloc(unibuf, unimax+BUFPAGE);
206
207 // Check if realloc succeeded
208
209 if(newbuf!=NULL)
210 { unibuf = newbuf;
211 unimax+=BUFPAGE;
212 } else
213 { // realloc failed, so decrease unipc so we won't write beyond
214 // the end of the buffer.. I don't report the out-of-memory
215 // here; the UniDup() will fail anyway so that's where the
216 // loader sees that something went wrong
217
218 unipc--;
219 }
220 }
221 }
222
223
224 BOOL MyCmp(UBYTE *a, UBYTE *b, UWORD l)
225 {
226 UWORD t;
227
228 for(t=0; t<l; t++)
229 if(*(a++) != *(b++)) return 0;
230 return 1;
231 }
232
233
234 void UniNewline(void)
235 // Closes the current row of a unitrk stream (updates the rep/len byte)
236 // and sets pointers to start a new row.
237 {
238 UWORD n,l,len;
239
240 n = (unibuf[lastp]>>5)+1; // repeat of previous row
241 l = (unibuf[lastp]&0x1f); // length of previous row
242
243 len = unipc-unitt; // length of current row
244
245 // Now, check if the previous and the current row are identical..
246 // when they are, just increase the repeat field of the previous row
247
248 if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1))
249 { unibuf[lastp]+=0x20;
250 unipc = unitt+1;
251 } else
252 { // current and previous row aren't equal.. so just update the pointers
253
254 unibuf[unitt] = len;
255 lastp = unitt;
256 unitt = unipc;
257 unipc++;
258 }
259 }
260
261
262 UBYTE *UniDup(void)
263 // Terminates the current unitrk stream and returns a pointer
264 // to a copy of the stream.
265 {
266 UBYTE *d;
267
268 unibuf[unitt] = 0;
269
270 if((d=(UBYTE *)_mm_malloc(unipc))==NULL) return NULL;
271 memcpy(d,unibuf,unipc);
272
273 return d;
274 }
275
276
277 UWORD TrkLen(UBYTE *t)
278 // Determines the length (in rows) of a unitrk stream 't'
279 {
280 UWORD len = 0;
281 UBYTE c;
282
283 while(c = *t & 0x1f)
284 { len += c;
285 t += c;
286 }
287 len++;
288
289 return len;
290 }
291
292
293 BOOL UniInit(void)
294 {
295 unimax = BUFPAGE;
296
297 if(!(unibuf=(UBYTE *)_mm_malloc(unimax))) return 0;
298 return 1;
299 }
300
301
302 void UniCleanup(void)
303 {
304 if(unibuf!=NULL) free(unibuf);
305 unibuf = NULL;
306 }
307