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   DSIK internal format (DSM) 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 #define DSM_MAXCHAN (16)
40 #define DSM_MAXORDERS (128)
41 
42 typedef struct DSMSONG
43   {
44     CHAR songname[28];
45     UWORD version;
46     UWORD flags;
47     ULONG reserved2;
48     UWORD numord;
49     UWORD numsmp;
50     UWORD numpat;
51     UWORD numtrk;
52     UBYTE globalvol;
53     UBYTE mastervol;
54     UBYTE speed;
55     UBYTE bpm;
56     UBYTE panpos[DSM_MAXCHAN];
57     UBYTE orders[DSM_MAXORDERS];
58   }
59 DSMSONG;
60 
61 typedef struct DSMINST
62   {
63     CHAR filename[13];
64     UWORD flags;
65     UBYTE volume;
66     ULONG length;
67     ULONG loopstart;
68     ULONG loopend;
69     ULONG reserved1;
70     UWORD c2spd;
71     UWORD period;
72     CHAR samplename[28];
73   }
74 DSMINST;
75 
76 typedef struct DSMNOTE
77   {
78     UBYTE note, ins, vol, cmd, inf;
79   }
80 DSMNOTE;
81 
82 #define DSM_SURROUND (0xa4)
83 
84 /*========== Loader variables */
85 
86 static const CHAR *SONGID = "SONG";
87 static const CHAR *INSTID = "INST";
88 static const CHAR *PATTID = "PATT";
89 
90 static UBYTE blockid[4];
91 static ULONG blockln;
92 static ULONG blocklp;
93 static DSMSONG *mh = NULL;
94 static DSMNOTE *dsmbuf = NULL;
95 
96 static CHAR DSM_Version[] = "DSIK DSM-format";
97 
98 static unsigned char DSMSIG[4 + 4] =
99 {'R', 'I', 'F', 'F', 'D', 'S', 'M', 'F'};
100 
101 /*========== Loader code */
102 
103 static BOOL
DSM_Test(void)104 DSM_Test (void)
105 {
106   UBYTE id[12];
107 
108   if (!_mm_read_UBYTES (id, 12, modreader))
109     return 0;
110   if (!memcmp (id, DSMSIG, 4) && !memcmp (id + 8, DSMSIG + 4, 4))
111     return 1;
112 
113   return 0;
114 }
115 
116 static BOOL
DSM_Init(void)117 DSM_Init (void)
118 {
119   if (!(dsmbuf = (DSMNOTE *) _mm_malloc (DSM_MAXCHAN * 64 * sizeof (DSMNOTE))))
120     return 0;
121   if (!(mh = (DSMSONG *) _mm_calloc (1, sizeof (DSMSONG))))
122     return 0;
123   return 1;
124 }
125 
126 static void
DSM_Cleanup(void)127 DSM_Cleanup (void)
128 {
129   _mm_free (dsmbuf);
130   _mm_free (mh);
131 }
132 
133 static BOOL
GetBlockHeader(void)134 GetBlockHeader (void)
135 {
136   /* make sure we're at the right position for reading the
137      next riff block, no matter how many bytes read */
138   _mm_fseek (modreader, blocklp + blockln, SEEK_SET);
139 
140   while (1)
141     {
142       _mm_read_UBYTES (blockid, 4, modreader);
143       blockln = _mm_read_I_ULONG (modreader);
144       if (_mm_eof (modreader))
145 	{
146 	  _mm_errno = MMERR_LOADING_HEADER;
147 	  return 0;
148 	}
149 
150       if (memcmp (blockid, SONGID, 4) && memcmp (blockid, INSTID, 4) &&
151 	  memcmp (blockid, PATTID, 4))
152 	{
153 #ifdef MIKMOD_DEBUG
154 	  fprintf (stderr, "\rDSM: Skipping unknown block type %4.4s\n", blockid);
155 #endif
156 	  _mm_fseek (modreader, blockln, SEEK_CUR);
157 	}
158       else
159 	break;
160     }
161 
162   blocklp = _mm_ftell (modreader);
163 
164   return 1;
165 }
166 
167 static BOOL
DSM_ReadPattern(void)168 DSM_ReadPattern (void)
169 {
170   int flag, row = 0;
171   SWORD length;
172   DSMNOTE *n;
173 
174   /* clear pattern data */
175   memset (dsmbuf, 255, DSM_MAXCHAN * 64 * sizeof (DSMNOTE));
176   length = _mm_read_I_SWORD (modreader);
177 
178   while (row < 64)
179     {
180       flag = _mm_read_UBYTE (modreader);
181       if ((_mm_eof (modreader)) || (--length < 0))
182 	{
183 	  _mm_errno = MMERR_LOADING_PATTERN;
184 	  return 0;
185 	}
186 
187       if (flag)
188 	{
189 	  n = &dsmbuf[((flag & 0xf) * 64) + row];
190 	  if (flag & 0x80)
191 	    n->note = _mm_read_UBYTE (modreader);
192 	  if (flag & 0x40)
193 	    n->ins = _mm_read_UBYTE (modreader);
194 	  if (flag & 0x20)
195 	    n->vol = _mm_read_UBYTE (modreader);
196 	  if (flag & 0x10)
197 	    {
198 	      n->cmd = _mm_read_UBYTE (modreader);
199 	      n->inf = _mm_read_UBYTE (modreader);
200 	    }
201 	}
202       else
203 	row++;
204     }
205 
206   return 1;
207 }
208 
209 static UBYTE *
DSM_ConvertTrack(DSMNOTE * tr)210 DSM_ConvertTrack (DSMNOTE * tr)
211 {
212   int t;
213   UBYTE note, ins, vol, cmd, inf;
214 
215   UniReset ();
216   for (t = 0; t < 64; t++)
217     {
218       note = tr[t].note;
219       ins = tr[t].ins;
220       vol = tr[t].vol;
221       cmd = tr[t].cmd;
222       inf = tr[t].inf;
223 
224       if (ins != 0 && ins != 255)
225 	UniInstrument (ins - 1);
226       if (note != 255)
227 	UniNote (note - 1);	/* normal note */
228       if (vol < 65)
229 	UniPTEffect (0xc, vol);
230 
231       if (cmd != 255)
232 	{
233 	  if (cmd == 0x8)
234 	    {
235 	      if (inf == DSM_SURROUND)
236 		UniEffect (UNI_ITEFFECTS0, 0x91);
237 	      else if (inf <= 0x80)
238 		{
239 		  inf = (inf < 0x80) ? inf << 1 : 255;
240 		  UniPTEffect (cmd, inf);
241 		}
242 	    }
243 	  else if (cmd == 0xb)
244 	    {
245 	      if (inf <= 0x7f)
246 		UniPTEffect (cmd, inf);
247 	    }
248 	  else
249 	    {
250 	      /* Convert pattern jump from Dec to Hex */
251 	      if (cmd == 0xd)
252 		inf = (((inf & 0xf0) >> 4) * 10) + (inf & 0xf);
253 	      UniPTEffect (cmd, inf);
254 	    }
255 	}
256       UniNewline ();
257     }
258   return UniDup ();
259 }
260 
261 static BOOL
DSM_Load(BOOL curious)262 DSM_Load (BOOL curious)
263 {
264   int t;
265   DSMINST s;
266   SAMPLE *q;
267   int cursmp = 0, curpat = 0, track = 0;
268 
269   blocklp = 0;
270   blockln = 12;
271 
272   if (!GetBlockHeader ())
273     return 0;
274   if (memcmp (blockid, SONGID, 4))
275     {
276       _mm_errno = MMERR_LOADING_HEADER;
277       return 0;
278     }
279 
280   _mm_read_UBYTES (mh->songname, 28, modreader);
281   mh->version = _mm_read_I_UWORD (modreader);
282   mh->flags = _mm_read_I_UWORD (modreader);
283   mh->reserved2 = _mm_read_I_ULONG (modreader);
284   mh->numord = _mm_read_I_UWORD (modreader);
285   mh->numsmp = _mm_read_I_UWORD (modreader);
286   mh->numpat = _mm_read_I_UWORD (modreader);
287   mh->numtrk = _mm_read_I_UWORD (modreader);
288   mh->globalvol = _mm_read_UBYTE (modreader);
289   mh->mastervol = _mm_read_UBYTE (modreader);
290   mh->speed = _mm_read_UBYTE (modreader);
291   mh->bpm = _mm_read_UBYTE (modreader);
292   _mm_read_UBYTES (mh->panpos, DSM_MAXCHAN, modreader);
293   _mm_read_UBYTES (mh->orders, DSM_MAXORDERS, modreader);
294 
295   /* set module variables */
296   of.initspeed = mh->speed;
297   of.inittempo = mh->bpm;
298   of.modtype = strdup (DSM_Version);
299   of.numchn = mh->numtrk;
300   of.numpat = mh->numpat;
301   of.numtrk = of.numchn * of.numpat;
302   of.songname = DupStr (mh->songname, 28, 1);	/* make a cstr of songname */
303   of.reppos = 0;
304 
305   for (t = 0; t < DSM_MAXCHAN; t++)
306     of.panning[t] = mh->panpos[t] == DSM_SURROUND ? PAN_SURROUND :
307       mh->panpos[t] < 0x80 ? (mh->panpos[t] << 1) : 255;
308 
309   if (!AllocPositions (mh->numord))
310     return 0;
311   of.numpos = 0;
312   for (t = 0; t < mh->numord; t++)
313     {
314       of.positions[of.numpos] = mh->orders[t];
315       if (mh->orders[t] < 254)
316 	of.numpos++;
317     }
318 
319   of.numins = of.numsmp = mh->numsmp;
320 
321   if (!AllocSamples ())
322     return 0;
323   if (!AllocTracks ())
324     return 0;
325   if (!AllocPatterns ())
326     return 0;
327 
328   while (cursmp < of.numins || curpat < of.numpat)
329     {
330       if (!GetBlockHeader ())
331 	return 0;
332       if (!memcmp (blockid, INSTID, 4) && cursmp < of.numins)
333 	{
334 	  q = &of.samples[cursmp];
335 
336 	  /* try to read sample info */
337 	  _mm_read_UBYTES (s.filename, 13, modreader);
338 	  s.flags = _mm_read_I_UWORD (modreader);
339 	  s.volume = _mm_read_UBYTE (modreader);
340 	  s.length = _mm_read_I_ULONG (modreader);
341 	  s.loopstart = _mm_read_I_ULONG (modreader);
342 	  s.loopend = _mm_read_I_ULONG (modreader);
343 	  s.reserved1 = _mm_read_I_ULONG (modreader);
344 	  s.c2spd = _mm_read_I_UWORD (modreader);
345 	  s.period = _mm_read_I_UWORD (modreader);
346 	  _mm_read_UBYTES (s.samplename, 28, modreader);
347 
348 	  q->samplename = DupStr (s.samplename, 28, 1);
349 	  q->seekpos = _mm_ftell (modreader);
350 	  q->speed = s.c2spd;
351 	  q->length = s.length;
352 	  q->loopstart = s.loopstart;
353 	  q->loopend = s.loopend;
354 	  q->volume = s.volume;
355 
356 	  if (s.flags & 1)
357 	    q->flags |= SF_LOOP;
358 	  if (s.flags & 2)
359 	    q->flags |= SF_SIGNED;
360 	  /* (s.flags&4) means packed sample,
361 	     but did they really exist in dsm ? */
362 	  cursmp++;
363 	}
364       else if (!memcmp (blockid, PATTID, 4) && curpat < of.numpat)
365 	{
366 	  DSM_ReadPattern ();
367 	  for (t = 0; t < of.numchn; t++)
368 	    if (!(of.tracks[track++] = DSM_ConvertTrack (&dsmbuf[t * 64])))
369 	      return 0;
370 	  curpat++;
371 	}
372     }
373 
374   return 1;
375 }
376 
377 static CHAR *
DSM_LoadTitle(void)378 DSM_LoadTitle (void)
379 {
380   CHAR s[28];
381 
382   _mm_fseek (modreader, 12, SEEK_SET);
383   if (!_mm_read_UBYTES (s, 28, modreader))
384     return NULL;
385 
386   return (DupStr (s, 28, 1));
387 }
388 
389 /*========== Loader information */
390 
391 MLOADER load_dsm =
392 {
393   NULL,
394   "DSM",
395   "DSM (DSIK internal format)",
396   DSM_Init,
397   DSM_Test,
398   DSM_Load,
399   DSM_Cleanup,
400   DSM_LoadTitle
401 };
402 
403 
404 /* ex:set ts=4: */
405