Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_far.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_far.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   Farandole (FAR) 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 typedef struct FARHEADER1 {\r
52         UBYTE id[4];                    /* file magic */\r
53         CHAR  songname[40];             /* songname */\r
54         CHAR  blah[3];                  /* 13,10,26 */\r
55         UWORD headerlen;                /* remaining length of header in bytes */\r
56         UBYTE version;\r
57         UBYTE onoff[16];\r
58         UBYTE edit1[9];\r
59         UBYTE speed;\r
60         UBYTE panning[16];\r
61         UBYTE edit2[4];\r
62         UWORD stlen;\r
63 } FARHEADER1;\r
64 \r
65 typedef struct FARHEADER2 {\r
66         UBYTE orders[256];\r
67         UBYTE numpat;\r
68         UBYTE snglen;\r
69         UBYTE loopto;\r
70         UWORD patsiz[256];\r
71 } FARHEADER2;\r
72 \r
73 typedef struct FARSAMPLE {\r
74         CHAR  samplename[32];\r
75         ULONG length;\r
76         UBYTE finetune;\r
77         UBYTE volume;\r
78         ULONG reppos;\r
79         ULONG repend;\r
80         UBYTE type;\r
81         UBYTE loop;\r
82 } FARSAMPLE;\r
83 \r
84 typedef struct FARNOTE {\r
85         UBYTE note,ins,vol,eff;\r
86 } FARNOTE;\r
87 \r
88 /*========== Loader variables */\r
89 \r
90 static  CHAR FAR_Version[] = "Farandole";\r
91 static  FARHEADER1 *mh1 = NULL;\r
92 static  FARHEADER2 *mh2 = NULL;\r
93 static  FARNOTE *pat = NULL;\r
94 \r
95 static  unsigned char FARSIG[4+3]={'F','A','R',0xfe,13,10,26};\r
96 \r
97 /*========== Loader code */\r
98 \r
99 BOOL FAR_Test(void)\r
100 {\r
101         UBYTE id[47];\r
102 \r
103         if(!_mm_read_UBYTES(id,47,modreader)) return 0;\r
104         if((memcmp(id,FARSIG,4))||(memcmp(id+44,FARSIG+4,3))) return 0;\r
105         return 1;\r
106 }\r
107 \r
108 BOOL FAR_Init(void)\r
109 {\r
110         if(!(mh1 = (FARHEADER1*)_mm_malloc(sizeof(FARHEADER1)))) return 0;\r
111         if(!(mh2 = (FARHEADER2*)_mm_malloc(sizeof(FARHEADER2)))) return 0;\r
112         if(!(pat = (FARNOTE*)_mm_malloc(256*16*4*sizeof(FARNOTE)))) return 0;\r
113 \r
114         return 1;\r
115 }\r
116 \r
117 void FAR_Cleanup(void)\r
118 {\r
119         _mm_free(mh1);\r
120         _mm_free(mh2);\r
121         _mm_free(pat);\r
122 }\r
123 \r
124 static UBYTE *FAR_ConvertTrack(FARNOTE* n,int rows)\r
125 {\r
126         int t,vibdepth=1;\r
127 \r
128         UniReset();\r
129         for(t=0;t<rows;t++) {\r
130                 if(n->note) {\r
131                         UniInstrument(n->ins);\r
132                         UniNote(n->note+3*OCTAVE-1);\r
133                 }\r
134                 if (n->vol&0xf) UniPTEffect(0xc,(n->vol&0xf)<<2);\r
135                 if (n->eff)\r
136                         switch(n->eff>>4) {\r
137                                 case 0x3: /* porta to note */\r
138                                         UniPTEffect(0x3,(n->eff&0xf)<<4);\r
139                                         break;\r
140                                 case 0x4: /* retrigger */\r
141                                         UniPTEffect(0x0e, 0x90 | (n->eff & 0x0f));\r
142                                         break;\r
143                                 case 0x5: /* set vibrato depth */\r
144                                         vibdepth=n->eff&0xf;\r
145                                         break;\r
146                                 case 0x6: /* vibrato */\r
147                                         UniPTEffect(0x4,((n->eff&0xf)<<4)|vibdepth);\r
148                                         break;\r
149                                 case 0x7: /* volume slide up */\r
150                                         UniPTEffect(0xa,(n->eff&0xf)<<4);\r
151                                         break;\r
152                                 case 0x8: /* volume slide down */\r
153                                         UniPTEffect(0xa,n->eff&0xf);\r
154                                         break;\r
155                                 case 0xb: /* panning */\r
156                                         UniPTEffect(0xe,0x80|(n->eff&0xf));\r
157                                         break;\r
158                                 case 0xf: /* set speed */\r
159                                         UniPTEffect(0xf,n->eff&0xf);\r
160                                         break;\r
161 \r
162                                 /* others not yet implemented */\r
163                                 default:\r
164 #ifdef MIKMOD_DEBUG\r
165                                         fprintf(stderr,"\rFAR: unsupported effect %02X\n",n->eff);\r
166 #endif\r
167                                         break;\r
168                         }\r
169 \r
170                 UniNewline();\r
171                 n+=16;\r
172         }\r
173         return UniDup();\r
174 }\r
175 \r
176 BOOL FAR_Load(BOOL curious)\r
177 {\r
178         int t,u,tracks=0;\r
179         SAMPLE *q;\r
180         FARSAMPLE s;\r
181         FARNOTE *crow;\r
182         UBYTE smap[8];\r
183         (void)curious;\r
184 \r
185         /* try to read module header (first part) */\r
186         _mm_read_UBYTES(mh1->id,4,modreader);\r
187         _mm_read_SBYTES(mh1->songname,40,modreader);\r
188         _mm_read_SBYTES(mh1->blah,3,modreader);\r
189         mh1->headerlen = _mm_read_I_UWORD (modreader);\r
190         mh1->version   = _mm_read_UBYTE (modreader);\r
191         _mm_read_UBYTES(mh1->onoff,16,modreader);\r
192         _mm_read_UBYTES(mh1->edit1,9,modreader);\r
193         mh1->speed     = _mm_read_UBYTE(modreader);\r
194         _mm_read_UBYTES(mh1->panning,16,modreader);\r
195         _mm_read_UBYTES(mh1->edit2,4,modreader);\r
196         mh1->stlen     = _mm_read_I_UWORD (modreader);\r
197 \r
198         /* init modfile data */\r
199         of.modtype   = strdup(FAR_Version);\r
200         of.songname  = DupStr(mh1->songname,40,1);\r
201         of.numchn    = 16;\r
202         of.initspeed = mh1->speed;\r
203         of.inittempo = 80;\r
204         of.reppos    = 0;\r
205         of.flags    |= UF_PANNING;\r
206         for(t=0;t<16;t++) of.panning[t]=mh1->panning[t]<<4;\r
207 \r
208         /* read songtext into comment field */\r
209         if(mh1->stlen)\r
210                 if (!ReadLinedComment(mh1->stlen, 66)) return 0;\r
211 \r
212         /* try to read module header (second part) */\r
213         _mm_read_UBYTES(mh2->orders,256,modreader);\r
214         mh2->numpat        = _mm_read_UBYTE(modreader);\r
215         mh2->snglen        = _mm_read_UBYTE(modreader);\r
216         mh2->loopto        = _mm_read_UBYTE(modreader);\r
217         _mm_read_I_UWORDS(mh2->patsiz,256,modreader);\r
218 \r
219         of.numpos = mh2->snglen;\r
220         if(!AllocPositions(of.numpos)) return 0;\r
221         for(t=0;t<of.numpos;t++) {\r
222                 if(mh2->orders[t]==0xff) break;\r
223                 of.positions[t] = mh2->orders[t];\r
224         }\r
225 \r
226         /* count number of patterns stored in file */\r
227         of.numpat = 0;\r
228         for(t=0;t<256;t++)\r
229                 if(mh2->patsiz[t])\r
230                         if((t+1)>of.numpat) of.numpat=t+1;\r
231         of.numtrk = of.numpat*of.numchn;\r
232 \r
233         /* seek across eventual new data */\r
234         _mm_fseek(modreader,mh1->headerlen-(869+mh1->stlen),SEEK_CUR);\r
235 \r
236         /* alloc track and pattern structures */\r
237         if(!AllocTracks()) return 0;\r
238         if(!AllocPatterns()) return 0;\r
239 \r
240         for(t=0;t<of.numpat;t++) {\r
241                 UBYTE rows=0,tempo;\r
242 \r
243                 memset(pat,0,256*16*4*sizeof(FARNOTE));\r
244                 if(mh2->patsiz[t]) {\r
245                         rows  = _mm_read_UBYTE(modreader);\r
246                         tempo = _mm_read_UBYTE(modreader);\r
247 \r
248                         crow = pat;\r
249                         /* file often allocates 64 rows even if there are less in pattern */\r
250                         if (mh2->patsiz[t]<2+(rows*16*4)) {\r
251                                 _mm_errno = MMERR_LOADING_PATTERN;\r
252                                 return 0;\r
253                         }\r
254                         for(u=(mh2->patsiz[t]-2)/4;u;u--,crow++) {\r
255                                 crow->note = _mm_read_UBYTE(modreader);\r
256                                 crow->ins  = _mm_read_UBYTE(modreader);\r
257                                 crow->vol  = _mm_read_UBYTE(modreader);\r
258                                 crow->eff  = _mm_read_UBYTE(modreader);\r
259                         }\r
260 \r
261                         if(_mm_eof(modreader)) {\r
262                                 _mm_errno = MMERR_LOADING_PATTERN;\r
263                                 return 0;\r
264                         }\r
265 \r
266                         crow=pat;\r
267                         of.pattrows[t] = rows;\r
268                         for(u=16;u;u--,crow++)\r
269                                 if(!(of.tracks[tracks++]=FAR_ConvertTrack(crow,rows))) {\r
270                                         _mm_errno=MMERR_LOADING_PATTERN;\r
271                                         return 0;\r
272                                 }\r
273                 } else\r
274                         tracks+=16;\r
275         }\r
276 \r
277         /* read sample map */\r
278         if(!_mm_read_UBYTES(smap,8,modreader)) {\r
279                 _mm_errno = MMERR_LOADING_HEADER;\r
280                 return 0;\r
281         }\r
282 \r
283         /* count number of samples used */\r
284         of.numins = 0;\r
285         for(t=0;t<64;t++)\r
286                 if(smap[t>>3]&(1<<(t&7))) of.numins=t+1;\r
287         of.numsmp = of.numins;             \r
288 \r
289         /* alloc sample structs */\r
290         if(!AllocSamples()) return 0;\r
291 \r
292         q = of.samples;\r
293         for(t=0;t<of.numsmp;t++) {\r
294                 q->speed      = 8363;\r
295                 q->flags      = SF_SIGNED;\r
296                 if(smap[t>>3]&(1<<(t&7))) {\r
297                         _mm_read_SBYTES(s.samplename,32,modreader);\r
298                         s.length   = _mm_read_I_ULONG(modreader);\r
299                         s.finetune = _mm_read_UBYTE(modreader);\r
300                         s.volume   = _mm_read_UBYTE(modreader);\r
301                         s.reppos   = _mm_read_I_ULONG(modreader);\r
302                         s.repend   = _mm_read_I_ULONG(modreader);\r
303                         s.type     = _mm_read_UBYTE(modreader);\r
304                         s.loop     = _mm_read_UBYTE(modreader);\r
305 \r
306                         q->samplename = DupStr(s.samplename,32,1);\r
307                         q->length     = s.length;\r
308                         q->loopstart  = s.reppos;\r
309                         q->loopend    = s.repend;\r
310                         q->volume     = s.volume<<2;\r
311 \r
312                         if(s.type&1) q->flags|=SF_16BITS;\r
313                         if(s.loop&8) q->flags|=SF_LOOP;\r
314 \r
315                         q->seekpos    = _mm_ftell(modreader);\r
316                         _mm_fseek(modreader,q->length,SEEK_CUR);\r
317                 } else \r
318                         q->samplename = DupStr(NULL,0,0);\r
319                 q++;\r
320         }\r
321         return 1;\r
322 }\r
323 \r
324 CHAR *FAR_LoadTitle(void)\r
325 {\r
326         CHAR s[40];\r
327 \r
328         _mm_fseek(modreader,4,SEEK_SET);\r
329         if(!_mm_read_UBYTES(s,40,modreader)) return NULL;\r
330    \r
331         return(DupStr(s,40,1));\r
332 }\r
333 \r
334 /*========== Loader information */\r
335 \r
336 MIKMODAPI MLOADER load_far={\r
337         NULL,\r
338         "FAR",\r
339         "FAR (Farandole Composer)",\r
340         FAR_Init,\r
341         FAR_Test,\r
342         FAR_Load,\r
343         FAR_Cleanup,\r
344         FAR_LoadTitle\r
345 };\r
346 \r
347 /* ex:set ts=4: */\r