6
|
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
|