Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / playercode / munitrk.c
1 /*      MikMod sound library\r
2         (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS\r
3         for complete list.\r
4 \r
5         This library is free software; you can redistribute it and/or modify\r
6         it under the terms of the GNU Library General Public License as\r
7         published by the Free Software Foundation; either version 2 of\r
8         the License, or (at your option) any later version.\r
9  \r
10         This program is distributed in the hope that it will be useful,\r
11         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13         GNU Library General Public License for more details.\r
14  \r
15         You should have received a copy of the GNU Library General Public\r
16         License along with this library; if not, write to the Free Software\r
17         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
18         02111-1307, USA.\r
19 */\r
20 \r
21 /*==============================================================================\r
22 \r
23   $Id: munitrk.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   All routines dealing with the manipulation of UNITRK streams\r
26 \r
27 ==============================================================================*/\r
28 \r
29 #ifdef HAVE_CONFIG_H\r
30 #include "config.h"\r
31 #endif\r
32 \r
33 #include <string.h>\r
34 \r
35 #include "mikmod_internals.h"\r
36 \r
37 /* Unibuffer chunk size */\r
38 #define BUFPAGE  128\r
39 \r
40 UWORD unioperands[UNI_LAST]={\r
41         0, /* not used */\r
42         1, /* UNI_NOTE */\r
43         1, /* UNI_INSTRUMENT */\r
44         1, /* UNI_PTEFFECT0 */\r
45         1, /* UNI_PTEFFECT1 */\r
46         1, /* UNI_PTEFFECT2 */\r
47         1, /* UNI_PTEFFECT3 */\r
48         1, /* UNI_PTEFFECT4 */\r
49         1, /* UNI_PTEFFECT5 */\r
50         1, /* UNI_PTEFFECT6 */\r
51         1, /* UNI_PTEFFECT7 */\r
52         1, /* UNI_PTEFFECT8 */\r
53         1, /* UNI_PTEFFECT9 */\r
54         1, /* UNI_PTEFFECTA */\r
55         1, /* UNI_PTEFFECTB */\r
56         1, /* UNI_PTEFFECTC */\r
57         1, /* UNI_PTEFFECTD */\r
58         1, /* UNI_PTEFFECTE */\r
59         1, /* UNI_PTEFFECTF */\r
60         1, /* UNI_S3MEFFECTA */\r
61         1, /* UNI_S3MEFFECTD */\r
62         1, /* UNI_S3MEFFECTE */\r
63         1, /* UNI_S3MEFFECTF */\r
64         1, /* UNI_S3MEFFECTI */\r
65         1, /* UNI_S3MEFFECTQ */\r
66         1, /* UNI_S3MEFFECTR */\r
67         1, /* UNI_S3MEFFECTT */\r
68         1, /* UNI_S3MEFFECTU */\r
69         0, /* UNI_KEYOFF */\r
70         1, /* UNI_KEYFADE */\r
71         2, /* UNI_VOLEFFECTS */\r
72         1, /* UNI_XMEFFECT4 */\r
73         1, /* UNI_XMEFFECT6 */\r
74         1, /* UNI_XMEFFECTA */\r
75         1, /* UNI_XMEFFECTE1 */\r
76         1, /* UNI_XMEFFECTE2 */\r
77         1, /* UNI_XMEFFECTEA */\r
78         1, /* UNI_XMEFFECTEB */\r
79         1, /* UNI_XMEFFECTG */\r
80         1, /* UNI_XMEFFECTH */\r
81         1, /* UNI_XMEFFECTL */\r
82         1, /* UNI_XMEFFECTP */\r
83         1, /* UNI_XMEFFECTX1 */\r
84         1, /* UNI_XMEFFECTX2 */\r
85         1, /* UNI_ITEFFECTG */\r
86         1, /* UNI_ITEFFECTH */\r
87         1, /* UNI_ITEFFECTI */\r
88         1, /* UNI_ITEFFECTM */\r
89         1, /* UNI_ITEFFECTN */\r
90         1, /* UNI_ITEFFECTP */\r
91         1, /* UNI_ITEFFECTT */\r
92         1, /* UNI_ITEFFECTU */\r
93         1, /* UNI_ITEFFECTW */\r
94         1, /* UNI_ITEFFECTY */\r
95         2, /* UNI_ITEFFECTZ */\r
96         1, /* UNI_ITEFFECTS0 */\r
97         2, /* UNI_ULTEFFECT9 */\r
98         2, /* UNI_MEDSPEED */\r
99         0, /* UNI_MEDEFFECTF1 */\r
100         0, /* UNI_MEDEFFECTF2 */\r
101         0, /* UNI_MEDEFFECTF3 */\r
102         2, /* UNI_OKTARP */\r
103 };\r
104 \r
105 /* Sparse description of the internal module format\r
106    ------------------------------------------------\r
107 \r
108   A UNITRK stream is an array of bytes representing a single track of a pattern.\r
109 It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly\r
110 language):\r
111 \r
112 rrrlllll\r
113 [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..\r
114 ^                                         ^ ^\r
115 |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...\r
116 \r
117   The rep/len byte contains the number of bytes in the current row, _including_\r
118 the length byte itself (So the LENGTH byte of row 0 in the previous example\r
119 would have a value of 5). This makes it easy to search through a stream for a\r
120 particular row. A track is concluded by a 0-value length byte.\r
121 \r
122   The upper 3 bits of the rep/len byte contain the number of times -1 this row\r
123 is repeated for this track. (so a value of 7 means this row is repeated 8 times)\r
124 \r
125   Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being\r
126 used. Each opcode can have a different number of operands. You can find the\r
127 number of operands to a particular opcode by using the opcode as an index into\r
128 the 'unioperands' table.\r
129 \r
130 */\r
131 \r
132 /*========== Reading routines */\r
133 \r
134 static  UBYTE *rowstart; /* startadress of a row */\r
135 static  UBYTE *rowend;   /* endaddress of a row (exclusive) */\r
136 static  UBYTE *rowpc;    /* current unimod(tm) programcounter */\r
137 \r
138 static  UBYTE lastbyte;  /* for UniSkipOpcode() */\r
139 \r
140 void UniSetRow(UBYTE* t)\r
141 {\r
142         rowstart = t;\r
143         rowpc    = rowstart;\r
144         rowend   = t?rowstart+(*(rowpc++)&0x1f):t;\r
145 }\r
146 \r
147 UBYTE UniGetByte(void)\r
148 {\r
149         return lastbyte = (rowpc<rowend)?*(rowpc++):0;\r
150 }\r
151 \r
152 UWORD UniGetWord(void)\r
153 {\r
154         return ((UWORD)UniGetByte()<<8)|UniGetByte();\r
155 }\r
156 \r
157 void UniSkipOpcode(void)\r
158 {\r
159         if (lastbyte < UNI_LAST) {\r
160                 UWORD t = unioperands[lastbyte];\r
161 \r
162                 while (t--)\r
163                         UniGetByte();\r
164         }\r
165 }\r
166 \r
167 /* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns\r
168    NULL if the row can't be found. */\r
169 UBYTE *UniFindRow(UBYTE* t,UWORD row)\r
170 {\r
171         UBYTE c,l;\r
172 \r
173         if(t)\r
174                 while(1) {\r
175                         c = *t;             /* get rep/len byte */\r
176                         if(!c) return NULL; /* zero ? -> end of track.. */\r
177                         l = (c>>5)+1;       /* extract repeat value */\r
178                         if(l>row) break;    /* reached wanted row? -> return pointer */\r
179                         row -= l;           /* haven't reached row yet.. update row */\r
180                         t += c&0x1f;        /* point t to the next row */\r
181                 }\r
182         return t;\r
183 }\r
184 \r
185 /*========== Writing routines */\r
186 \r
187 static  UBYTE *unibuf; /* pointer to the temporary unitrk buffer */\r
188 static  UWORD unimax;  /* buffer size */\r
189 \r
190 static  UWORD unipc;   /* buffer cursor */\r
191 static  UWORD unitt;   /* current row index */\r
192 static  UWORD lastp;   /* previous row index */\r
193 \r
194 /* Resets index-pointers to create a new track. */\r
195 void UniReset(void)\r
196 {\r
197         unitt     = 0;   /* reset index to rep/len byte */\r
198         unipc     = 1;   /* first opcode will be written to index 1 */\r
199         lastp     = 0;   /* no previous row yet */\r
200         unibuf[0] = 0;   /* clear rep/len byte */\r
201 }\r
202 \r
203 /* Expands the buffer */\r
204 static BOOL UniExpand(int wanted)\r
205 {\r
206         if ((unipc+wanted)>=unimax) {\r
207                 UBYTE *newbuf;\r
208 \r
209                 /* Expand the buffer by BUFPAGE bytes */\r
210                 newbuf=(UBYTE*)realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE));\r
211 \r
212                 /* Check if realloc succeeded */\r
213                 if(newbuf) {\r
214                         unibuf = newbuf;\r
215                         unimax+=BUFPAGE;\r
216                         return 1;\r
217                 } else \r
218                         return 0;\r
219         }\r
220         return 1;\r
221 }\r
222 \r
223 /* Appends one byte of data to the current row of a track. */\r
224 void UniWriteByte(UBYTE data)\r
225 {\r
226         if (UniExpand(1))\r
227                 /* write byte to current position and update */\r
228                 unibuf[unipc++]=data;\r
229 }\r
230 \r
231 void UniWriteWord(UWORD data)\r
232 {\r
233         if (UniExpand(2)) {\r
234                 unibuf[unipc++]=data>>8;\r
235                 unibuf[unipc++]=data&0xff;\r
236         }\r
237 }\r
238 \r
239 static BOOL MyCmp(UBYTE* a,UBYTE* b,UWORD l)\r
240 {\r
241         UWORD t;\r
242 \r
243         for(t=0;t<l;t++)\r
244                 if(*(a++)!=*(b++)) return 0;\r
245         return 1;\r
246 }\r
247 \r
248 /* Closes the current row of a unitrk stream (updates the rep/len byte) and sets\r
249    pointers to start a new row. */\r
250 void UniNewline(void)\r
251 {\r
252         UWORD n,l,len;\r
253 \r
254         n = (unibuf[lastp]>>5)+1;     /* repeat of previous row */\r
255         l = (unibuf[lastp]&0x1f);     /* length of previous row */\r
256 \r
257         len = unipc-unitt;            /* length of current row */\r
258 \r
259         /* Now, check if the previous and the current row are identical.. when they\r
260            are, just increase the repeat field of the previous row */\r
261         if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) {\r
262                 unibuf[lastp]+=0x20;\r
263                 unipc = unitt+1;\r
264         } else {\r
265                 if (UniExpand(unitt-unipc)) {\r
266                         /* current and previous row aren't equal... update the pointers */\r
267                         unibuf[unitt] = len;\r
268                         lastp = unitt;\r
269                         unitt = unipc++;\r
270                 }\r
271         }\r
272 }\r
273 \r
274 /* Terminates the current unitrk stream and returns a pointer to a copy of the\r
275    stream. */\r
276 UBYTE* UniDup(void)\r
277 {\r
278         UBYTE *d;\r
279 \r
280         if (!UniExpand(unitt-unipc)) return NULL;\r
281         unibuf[unitt] = 0;\r
282 \r
283         if(!(d=(UBYTE *)_mm_malloc(unipc))) return NULL;\r
284         memcpy(d,unibuf,unipc);\r
285 \r
286         return d;\r
287 }\r
288 \r
289 BOOL UniInit(void)\r
290 {\r
291         unimax = BUFPAGE;\r
292 \r
293         if(!(unibuf=(UBYTE*)_mm_malloc(unimax*sizeof(UBYTE)))) return 0;\r
294         return 1;\r
295 }\r
296 \r
297 void UniCleanup(void)\r
298 {\r
299         if(unibuf) free(unibuf);\r
300         unibuf = NULL;\r
301 }\r
302 \r
303 /* ex:set ts=4: */\r