Mercurial > ~darius > hgwebdir.cgi > mikmod
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 |