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