1 /*      MikMod sound library
2    (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
3    complete list.
4 
5    This library is free software; you can redistribute it and/or modify
6    it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of
8    the License, or (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA.
19  */
20 
21 /*==============================================================================
22 
23   $Id$
24 
25   MTM module loader
26 
27 ==============================================================================*/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <string.h>
34 
35 #include "unimod_priv.h"
36 
37 /*========== Module structure */
38 
39 typedef struct MTMHEADER
40   {
41     UBYTE id[3];		/* MTM file marker */
42     UBYTE version;		/* upper major, lower nibble minor version number */
43     CHAR songname[20];		/* ASCIIZ songname */
44     UWORD numtracks;		/* number of tracks saved */
45     UBYTE lastpattern;		/* last pattern number saved */
46     UBYTE lastorder;		/* last order number to play (songlength-1) */
47     UWORD commentsize;		/* length of comment field */
48     UBYTE numsamples;		/* number of samples saved  */
49     UBYTE attribute;		/* attribute byte (unused) */
50     UBYTE beatspertrack;
51     UBYTE numchannels;		/* number of channels used  */
52     UBYTE panpos[32];		/* voice pan positions */
53   }
54 MTMHEADER;
55 
56 typedef struct MTMSAMPLE
57   {
58     CHAR samplename[22];
59     ULONG length;
60     ULONG reppos;
61     ULONG repend;
62     UBYTE finetune;
63     UBYTE volume;
64     UBYTE attribute;
65   }
66 MTMSAMPLE;
67 
68 typedef struct MTMNOTE
69   {
70     UBYTE a, b, c;
71   }
72 MTMNOTE;
73 
74 /*========== Loader variables */
75 
76 static MTMHEADER *mh = NULL;
77 static MTMNOTE *mtmtrk = NULL;
78 static UWORD pat[32];
79 
80 static CHAR MTM_Version[] = "MTM";
81 
82 /*========== Loader code */
83 
84 static BOOL
MTM_Test(void)85 MTM_Test (void)
86 {
87   UBYTE id[3];
88 
89   if (!_mm_read_UBYTES (id, 3, modreader))
90     return 0;
91   if (!memcmp (id, "MTM", 3))
92     return 1;
93   return 0;
94 }
95 
96 static BOOL
MTM_Init(void)97 MTM_Init (void)
98 {
99   if (!(mtmtrk = (MTMNOTE *) _mm_calloc (64, sizeof (MTMNOTE))))
100     return 0;
101   if (!(mh = (MTMHEADER *) _mm_malloc (sizeof (MTMHEADER))))
102     return 0;
103 
104   return 1;
105 }
106 
107 static void
MTM_Cleanup(void)108 MTM_Cleanup (void)
109 {
110   _mm_free (mtmtrk);
111   _mm_free (mh);
112 }
113 
114 static UBYTE *
MTM_Convert(void)115 MTM_Convert (void)
116 {
117   int t;
118   UBYTE a, b, inst, note, eff, dat;
119 
120   UniReset ();
121   for (t = 0; t < 64; t++)
122     {
123       a = mtmtrk[t].a;
124       b = mtmtrk[t].b;
125       inst = ((a & 0x3) << 4) | (b >> 4);
126       note = a >> 2;
127       eff = b & 0xf;
128       dat = mtmtrk[t].c;
129 
130       if (inst)
131 	UniInstrument (inst - 1);
132       if (note)
133 	UniNote (note + 2 * OCTAVE);
134 
135       /* MTM bug workaround : when the effect is volslide, slide-up *always*
136          overrides slide-down. */
137       if (eff == 0xa && (dat & 0xf0))
138 	dat &= 0xf0;
139 
140       /* Convert pattern jump from Dec to Hex */
141       if (eff == 0xd)
142 	dat = (((dat & 0xf0) >> 4) * 10) + (dat & 0xf);
143       UniPTEffect (eff, dat);
144       UniNewline ();
145     }
146   return UniDup ();
147 }
148 
149 static BOOL
MTM_Load(BOOL curious)150 MTM_Load (BOOL curious)
151 {
152   int t, u;
153   MTMSAMPLE s;
154   SAMPLE *q;
155 
156   /* try to read module header  */
157   _mm_read_UBYTES (mh->id, 3, modreader);
158   mh->version = _mm_read_UBYTE (modreader);
159   _mm_read_string (mh->songname, 20, modreader);
160   mh->numtracks = _mm_read_I_UWORD (modreader);
161   mh->lastpattern = _mm_read_UBYTE (modreader);
162   mh->lastorder = _mm_read_UBYTE (modreader);
163   mh->commentsize = _mm_read_I_UWORD (modreader);
164   mh->numsamples = _mm_read_UBYTE (modreader);
165   mh->attribute = _mm_read_UBYTE (modreader);
166   mh->beatspertrack = _mm_read_UBYTE (modreader);
167   mh->numchannels = _mm_read_UBYTE (modreader);
168   _mm_read_UBYTES (mh->panpos, 32, modreader);
169 
170   if (_mm_eof (modreader))
171     {
172       _mm_errno = MMERR_LOADING_HEADER;
173       return 0;
174     }
175 
176   /* set module variables */
177   of.initspeed = 6;
178   of.inittempo = 125;
179   of.modtype = strdup (MTM_Version);
180   of.numchn = mh->numchannels;
181   of.numtrk = mh->numtracks + 1;	/* get number of channels */
182   of.songname = DupStr (mh->songname, 20, 1);	/* make a cstr of songname */
183   of.numpos = mh->lastorder + 1;	/* copy the songlength */
184   of.numpat = mh->lastpattern + 1;
185   of.reppos = 0;
186   for (t = 0; t < 32; t++)
187     of.panning[t] = mh->panpos[t] << 4;
188   of.numins = of.numsmp = mh->numsamples;
189 
190   if (!AllocSamples ())
191     return 0;
192   q = of.samples;
193   for (t = 0; t < of.numins; t++)
194     {
195       /* try to read sample info */
196       _mm_read_string (s.samplename, 22, modreader);
197       s.length = _mm_read_I_ULONG (modreader);
198       s.reppos = _mm_read_I_ULONG (modreader);
199       s.repend = _mm_read_I_ULONG (modreader);
200       s.finetune = _mm_read_UBYTE (modreader);
201       s.volume = _mm_read_UBYTE (modreader);
202       s.attribute = _mm_read_UBYTE (modreader);
203 
204       if (_mm_eof (modreader))
205 	{
206 	  _mm_errno = MMERR_LOADING_SAMPLEINFO;
207 	  return 0;
208 	}
209 
210       q->samplename = DupStr (s.samplename, 22, 1);
211       q->seekpos = 0;
212       q->speed = finetune[s.finetune];
213       q->length = s.length;
214       q->loopstart = s.reppos;
215       q->loopend = s.repend;
216       q->volume = s.volume;
217       if ((s.repend - s.reppos) > 2)
218 	q->flags |= SF_LOOP;
219 
220       if (s.attribute & 1)
221 	{
222 	  /* If the sample is 16-bits, convert the length and replen
223 	     byte-values into sample-values */
224 	  q->flags |= SF_16BITS;
225 	  q->length >>= 1;
226 	  q->loopstart >>= 1;
227 	  q->loopend >>= 1;
228 	}
229 
230       q++;
231     }
232 
233   if (!AllocPositions (of.numpos))
234     return 0;
235   for (t = 0; t < of.numpos; t++)
236     of.positions[t] = _mm_read_UBYTE (modreader);
237   for (; t < 128; t++)
238     _mm_read_UBYTE (modreader);
239   if (_mm_eof (modreader))
240     {
241       _mm_errno = MMERR_LOADING_HEADER;
242       return 0;
243     }
244 
245   if (!AllocTracks ())
246     return 0;
247   if (!AllocPatterns ())
248     return 0;
249 
250   of.tracks[0] = MTM_Convert ();	/* track 0 is empty */
251   for (t = 1; t < of.numtrk; t++)
252     {
253       int s;
254 
255       for (s = 0; s < 64; s++)
256 	{
257 	  mtmtrk[s].a = _mm_read_UBYTE (modreader);
258 	  mtmtrk[s].b = _mm_read_UBYTE (modreader);
259 	  mtmtrk[s].c = _mm_read_UBYTE (modreader);
260 	}
261 
262       if (_mm_eof (modreader))
263 	{
264 	  _mm_errno = MMERR_LOADING_TRACK;
265 	  return 0;
266 	}
267 
268       if (!(of.tracks[t] = MTM_Convert ()))
269 	return 0;
270     }
271 
272   for (t = 0; t < of.numpat; t++)
273     {
274       _mm_read_I_UWORDS (pat, 32, modreader);
275       for (u = 0; u < of.numchn; u++)
276 	of.patterns[((long) t * of.numchn) + u] = pat[u];
277     }
278 
279   /* read comment field */
280   if (mh->commentsize)
281     if (!ReadLinedComment (mh->commentsize / 40, 40))
282       return 0;
283 
284   return 1;
285 }
286 
287 static CHAR *
MTM_LoadTitle(void)288 MTM_LoadTitle (void)
289 {
290   CHAR s[20];
291 
292   _mm_fseek (modreader, 4, SEEK_SET);
293   if (!_mm_read_UBYTES (s, 20, modreader))
294     return NULL;
295 
296   return (DupStr (s, 20, 1));
297 }
298 
299 /*========== Loader information */
300 
301 MLOADER load_mtm =
302 {
303   NULL,
304   "MTM",
305   "MTM (MultiTracker Module editor)",
306   MTM_Init,
307   MTM_Test,
308   MTM_Load,
309   MTM_Cleanup,
310   MTM_LoadTitle
311 };
312 
313 /* ex:set ts=4: */
314