Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_ult.c
1 /*      MikMod sound library\r
2         (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file\r
3         AUTHORS 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: load_ult.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   Ultratracker (ULT) module loader\r
26 \r
27 ==============================================================================*/\r
28 \r
29 #ifdef HAVE_CONFIG_H\r
30 #include "config.h"\r
31 #endif\r
32 \r
33 #ifdef HAVE_UNISTD_H\r
34 #include <unistd.h>\r
35 #endif\r
36 \r
37 #include <stdio.h>\r
38 #ifdef HAVE_MEMORY_H\r
39 #include <memory.h>\r
40 #endif\r
41 #include <string.h>\r
42 \r
43 #include "mikmod_internals.h"\r
44 \r
45 #ifdef SUNOS\r
46 extern int fprintf(FILE *, const char *, ...);\r
47 #endif\r
48 \r
49 /*========== Module structure */\r
50 \r
51 /* header */\r
52 typedef struct ULTHEADER {\r
53         CHAR  id[16];\r
54         CHAR  songtitle[32];\r
55         UBYTE reserved;\r
56 } ULTHEADER;\r
57 \r
58 /* sample information */\r
59 typedef struct ULTSAMPLE {\r
60         CHAR  samplename[32];\r
61         CHAR  dosname[12];\r
62         SLONG loopstart;\r
63         SLONG loopend;\r
64         SLONG sizestart;\r
65         SLONG sizeend;\r
66         UBYTE volume;\r
67         UBYTE flags;\r
68         UWORD speed;\r
69         SWORD finetune;\r
70 } ULTSAMPLE;\r
71 \r
72 typedef struct ULTEVENT {\r
73         UBYTE note,sample,eff,dat1,dat2;\r
74 } ULTEVENT;\r
75 \r
76 /*========== Loader variables */\r
77 \r
78 #define ULTS_16BITS     4\r
79 #define ULTS_LOOP       8\r
80 #define ULTS_REVERSE    16\r
81 \r
82 #define ULT_VERSION_LEN 18\r
83 static  CHAR ULT_Version[ULT_VERSION_LEN]="Ultra Tracker v1.x";\r
84 \r
85 static  ULTEVENT ev;\r
86 \r
87 /*========== Loader code */\r
88 \r
89 BOOL ULT_Test(void)\r
90 {\r
91         CHAR id[16];\r
92 \r
93         if(!_mm_read_string(id,15,modreader)) return 0;\r
94         if(strncmp(id,"MAS_UTrack_V00",14)) return 0;\r
95         if((id[14]<'1')||(id[14]>'4')) return 0;\r
96         return 1;\r
97 }\r
98 \r
99 BOOL ULT_Init(void)\r
100 {\r
101         return 1;\r
102 }\r
103 \r
104 void ULT_Cleanup(void)\r
105 {\r
106 }\r
107 \r
108 static UBYTE ReadUltEvent(ULTEVENT* event)\r
109 {\r
110         UBYTE flag,rep=1;\r
111 \r
112         flag = _mm_read_UBYTE(modreader);\r
113         if(flag==0xfc) {\r
114                 rep = _mm_read_UBYTE(modreader);\r
115                 event->note =_mm_read_UBYTE(modreader);\r
116         } else\r
117                 event->note = flag;\r
118 \r
119         event->sample   =_mm_read_UBYTE(modreader);\r
120         event->eff      =_mm_read_UBYTE(modreader);\r
121         event->dat1     =_mm_read_UBYTE(modreader);\r
122         event->dat2     =_mm_read_UBYTE(modreader);\r
123 \r
124         return rep;\r
125 }\r
126 \r
127 BOOL ULT_Load(BOOL curious)\r
128 {\r
129         int t,u,tracks=0;\r
130         SAMPLE *q;\r
131         ULTSAMPLE s;\r
132         ULTHEADER mh;\r
133         UBYTE nos,noc,nopa;\r
134         (void)curious;\r
135 \r
136         /* try to read module header */\r
137         _mm_read_string(mh.id,15,modreader);\r
138         _mm_read_string(mh.songtitle,32,modreader);\r
139         mh.reserved=_mm_read_UBYTE(modreader);\r
140 \r
141         if(_mm_eof(modreader)) {\r
142                 _mm_errno = MMERR_LOADING_HEADER;\r
143                 return 0;\r
144         }\r
145 \r
146         ULT_Version[ULT_VERSION_LEN-1]='3'+(mh.id[14]-'1');\r
147         of.modtype   = DupStr(ULT_Version,ULT_VERSION_LEN,1);\r
148         of.initspeed = 6;\r
149         of.inittempo = 125;\r
150         of.reppos    = 0;\r
151 \r
152         /* read songtext */\r
153         if ((mh.id[14]>'1')&&(mh.reserved))\r
154                 if(!ReadLinedComment(mh.reserved * 32, 32)) return 0;\r
155 \r
156         nos=_mm_read_UBYTE(modreader);\r
157         if(_mm_eof(modreader)) {\r
158                 _mm_errno = MMERR_LOADING_HEADER;\r
159                 return 0;\r
160         }\r
161 \r
162         of.songname=DupStr(mh.songtitle,32,1);\r
163         of.numins=of.numsmp=nos;\r
164 \r
165         if(!AllocSamples()) return 0;\r
166         q = of.samples;\r
167         for(t=0;t<nos;t++) {\r
168                 /* try to read sample info */\r
169                 _mm_read_string(s.samplename,32,modreader);\r
170                 _mm_read_string(s.dosname,12,modreader);\r
171                 s.loopstart     =_mm_read_I_ULONG(modreader);\r
172                 s.loopend       =_mm_read_I_ULONG(modreader);\r
173                 s.sizestart     =_mm_read_I_ULONG(modreader);\r
174                 s.sizeend       =_mm_read_I_ULONG(modreader);\r
175                 s.volume        =_mm_read_UBYTE(modreader);\r
176                 s.flags         =_mm_read_UBYTE(modreader);\r
177                 s.speed         =(mh.id[14]>='4')?_mm_read_I_UWORD(modreader):8363;\r
178                 s.finetune      =_mm_read_I_SWORD(modreader);\r
179 \r
180                 if(_mm_eof(modreader)) {\r
181                         _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
182                         return 0;\r
183                 }\r
184 \r
185                 q->samplename=DupStr(s.samplename,32,1);\r
186                 /* The correct formula for the coefficient would be\r
187                    pow(2,(double)s.finetume/OCTAVE/32768), but to avoid floating point\r
188                    here, we'll use a first order approximation here.\r
189                    1/567290 == Ln(2)/OCTAVE/32768 */\r
190                 q->speed=s.speed+s.speed*(((SLONG)s.speed*(SLONG)s.finetune)/567290);\r
191                 q->length    = s.sizeend-s.sizestart;\r
192                 q->volume    = s.volume>>2;\r
193                 q->loopstart = s.loopstart;\r
194                 q->loopend   = s.loopend;\r
195                 q->flags = SF_SIGNED;\r
196                 if(s.flags&ULTS_LOOP) q->flags|=SF_LOOP;\r
197                 if(s.flags&ULTS_16BITS) {\r
198                         s.sizeend+=(s.sizeend-s.sizestart);\r
199                         s.sizestart<<=1;\r
200                         q->flags|=SF_16BITS;\r
201                         q->loopstart>>=1;\r
202                         q->loopend>>=1;\r
203                 }\r
204                 q++;\r
205         }\r
206 \r
207         if(!AllocPositions(256)) return 0;\r
208         for(t=0;t<256;t++)\r
209                 of.positions[t]=_mm_read_UBYTE(modreader);\r
210         for(t=0;t<256;t++)\r
211                 if(of.positions[t]==255) {\r
212                         of.positions[t]=LAST_PATTERN;\r
213                         break;\r
214                 }\r
215         of.numpos=t;\r
216 \r
217         noc=_mm_read_UBYTE(modreader);\r
218         nopa=_mm_read_UBYTE(modreader);\r
219 \r
220         of.numchn=++noc;\r
221         of.numpat=++nopa;\r
222         of.numtrk=of.numchn*of.numpat;\r
223         if(!AllocTracks()) return 0;\r
224         if(!AllocPatterns()) return 0;\r
225         for(u=0;u<of.numchn;u++)\r
226                 for(t=0;t<of.numpat;t++)\r
227                         of.patterns[(t*of.numchn)+u]=tracks++;\r
228 \r
229         /* read pan position table for v1.5 and higher */\r
230         if(mh.id[14]>='3') {\r
231                 for(t=0;t<of.numchn;t++) of.panning[t]=_mm_read_UBYTE(modreader)<<4;\r
232                 of.flags |= UF_PANNING;\r
233         }\r
234 \r
235         for(t=0;t<of.numtrk;t++) {\r
236                 int rep,row=0;\r
237 \r
238                 UniReset();\r
239                 while(row<64) {\r
240                         rep=ReadUltEvent(&ev);\r
241 \r
242                         if(_mm_eof(modreader)) {\r
243                                 _mm_errno = MMERR_LOADING_TRACK;\r
244                                 return 0;\r
245                         }\r
246 \r
247                         while(rep--) {\r
248                                 UBYTE eff;\r
249                                 int offset;\r
250 \r
251                                 if(ev.sample) UniInstrument(ev.sample-1);\r
252                                 if(ev.note)   UniNote(ev.note+2*OCTAVE-1);\r
253 \r
254                                 /* first effect - various fixes by Alexander Kerkhove and\r
255                                                   Thomas Neumann */\r
256                                 eff = ev.eff>>4;\r
257                                 switch(eff) {\r
258                                         case 0x3: /* tone portamento */\r
259                                                 UniEffect(UNI_ITEFFECTG,ev.dat2);\r
260                                                 break;\r
261                                         case 0x5:\r
262                                                 break;\r
263                                         case 0x9: /* sample offset */\r
264                                                 offset=(ev.dat2<<8)|((ev.eff&0xf)==9?ev.dat1:0);\r
265                                                 UniEffect(UNI_ULTEFFECT9,offset);\r
266                                                 break;\r
267                                         case 0xb: /* panning */\r
268                                                 UniPTEffect(8,ev.dat2*0xf);\r
269                                                 of.flags |= UF_PANNING;\r
270                                                 break;\r
271                                         case 0xc: /* volume */\r
272                                                 UniPTEffect(eff,ev.dat2>>2);\r
273                                                 break;\r
274                                         default:\r
275                                                 UniPTEffect(eff,ev.dat2);\r
276                                                 break;\r
277                                 }\r
278 \r
279                                 /* second effect */\r
280                                 eff=ev.eff&0xf;\r
281                                 switch(eff) {\r
282                                         case 0x3: /* tone portamento */\r
283                                                 UniEffect(UNI_ITEFFECTG,ev.dat1);\r
284                                                 break;\r
285                                         case 0x5:\r
286                                                 break;\r
287                                         case 0x9: /* sample offset */\r
288                                                 if((ev.eff>>4)!=9)\r
289                                                         UniEffect(UNI_ULTEFFECT9,((UWORD)ev.dat1)<<8);\r
290                                                 break;\r
291                                         case 0xb: /* panning */\r
292                                                 UniPTEffect(8,ev.dat1*0xf);\r
293                                                 of.flags |= UF_PANNING;\r
294                                                 break;\r
295                                         case 0xc: /* volume */\r
296                                                 UniPTEffect(eff,ev.dat1>>2);\r
297                                                 break;\r
298                                         default:\r
299                                                 UniPTEffect(eff,ev.dat1);\r
300                                                 break;\r
301                                 }\r
302 \r
303                                 UniNewline();\r
304                                 row++;\r
305                         }\r
306                 }\r
307                 if(!(of.tracks[t]=UniDup())) return 0;\r
308         }\r
309         return 1;\r
310 }\r
311 \r
312 CHAR *ULT_LoadTitle(void)\r
313 {\r
314         CHAR s[32];\r
315 \r
316         _mm_fseek(modreader,15,SEEK_SET);\r
317         if(!_mm_read_UBYTES(s,32,modreader)) return NULL;\r
318 \r
319         return(DupStr(s,32,1));\r
320 }\r
321 \r
322 /*========== Loader information */\r
323 \r
324 MIKMODAPI MLOADER load_ult={\r
325         NULL,\r
326         "ULT",\r
327         "ULT (UltraTracker)",\r
328         ULT_Init,\r
329         ULT_Test,\r
330         ULT_Load,\r
331         ULT_Cleanup,\r
332         ULT_LoadTitle\r
333 };\r
334 \r
335 \r
336 /* ex:set ts=4: */\r