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