1 /* MikMod sound library
2 (c) 2004, Raphael Assenat 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 ASYLUM Music Format v1.0 (.amf) loader
26 adapted from load_mod.c by Raphael Assenat <raph@raphnet.net>,
27 with the help of the AMF2MOD utility sourcecode,
28 written to convert crusader's amf files into 8
29 channels mod file in 1995 by Mr. P / Powersource
30 mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca
31
32
33 ==============================================================================*/
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #include <string.h>
44
45 #include "mikmod_internals.h"
46
47 /*========== Module structure */
48
49 typedef struct MSAMPINFO {
50 CHAR samplename[24];
51 UBYTE finetune;
52 UBYTE volume;
53 ULONG length;
54 ULONG reppos;
55 ULONG replen;
56 } MSAMPINFO;
57
58 typedef struct MODULEHEADER {
59 CHAR songname[21];
60 UBYTE num_patterns; /* number of patterns used */
61 UBYTE num_orders;
62 UBYTE positions[256]; /* which pattern to play at pos */
63 MSAMPINFO samples[64]; /* all sampleinfo */
64 } MODULEHEADER;
65
66 typedef struct MODTYPE {
67 CHAR id[5];
68 UBYTE channels;
69 CHAR *name;
70 } MODTYPE;
71
72 typedef struct MODNOTE {
73 UBYTE a, b, c, d;
74 } MODNOTE;
75
76 /* This table is taken from AMF2MOD.C
77 * written in 1995 by Mr. P / Powersource
78 * mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca */
79 static const UWORD periodtable[] = {
80 6848,6464,6096,5760,5424,5120,4832,4560,4304,
81 4064,3840,3628,3424,3232,3048,2880,2712,2560,
82 2416,2280,2152,2032,1920,1814,1712,1616,1524,
83 1440,1356,1280,1208,1140,1076,1016, 960, 907,
84 856, 808, 762, 720, 678, 640, 604, 570, 538,
85 508, 480, 453, 428, 404, 381, 360, 339, 320,
86 302, 285, 269, 254, 240, 226, 214, 202, 190,
87 180, 170, 160, 151, 143, 135, 127, 120, 113,
88 107, 101, 95, 90, 85, 80, 75, 71, 67,
89 63, 60, 56, 53, 50, 47, 45, 42, 40,
90 37, 35, 33, 31, 30, 28};
91
92 /*========== Loader variables */
93
94 static CHAR asylum[] = "Asylum 1.0";
95
96 static MODULEHEADER *mh = NULL;
97 static MODNOTE *patbuf = NULL;
98 static int modtype = 0;
99
100 /*========== Loader code */
101
ASY_CheckType(UBYTE * id,UBYTE * numchn,CHAR ** descr)102 static BOOL ASY_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
103 {
104 if (!memcmp(id, "ASYLUM Music Format V1.0", 24))
105 {
106 *descr = asylum;
107 *numchn = 8;
108 modtype = 1;
109 return 1;
110 }
111
112 return 0;
113 }
114
ASY_Test(void)115 static BOOL ASY_Test(void)
116 {
117 UBYTE namestring[24], numchn;
118 CHAR *descr;
119
120 /* Read the magic string */
121 _mm_fseek(modreader, 0, SEEK_SET);
122 if (!_mm_read_UBYTES(namestring, 24, modreader))
123 return 0;
124
125 /* Test if the string is what we expect */
126 if (ASY_CheckType(namestring, &numchn, &descr))
127 return 1;
128
129 return 0;
130 }
131
ASY_Init(void)132 static BOOL ASY_Init(void)
133 {
134 if (!(mh = (MODULEHEADER *)MikMod_malloc(sizeof(MODULEHEADER))))
135 return 0;
136 return 1;
137 }
138
ASY_Cleanup(void)139 static void ASY_Cleanup(void)
140 {
141 MikMod_free(mh);
142 MikMod_free(patbuf);
143 mh = NULL;
144 patbuf = NULL;
145 }
146
ConvertNote(MODNOTE * n)147 static void ConvertNote(MODNOTE *n)
148 {
149 UBYTE instrument, effect, effdat, note;
150 UWORD period;
151 UBYTE lastnote = 0;
152
153 instrument = n->b&0x1f;
154 effect = n->c;
155 effdat = n->d;
156
157 /* convert amf note to mod period */
158 if (n->a) {
159 period = periodtable[n->a];
160 } else {
161 period = 0;
162 }
163
164 /* Convert the period to a note number */
165 note = 0;
166 if (period)
167 {
168 for (note = 0; note < 7 * OCTAVE; note++)
169 if (period >= npertab[note])
170 break;
171 if (note == 7 * OCTAVE)
172 note = 0;
173 else
174 note++;
175 }
176
177 if (instrument) {
178 /* if instrument does not exist, note cut */
179 if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {
180 UniPTEffect(0xc, 0);
181 if (effect == 0xc)
182 effect = effdat = 0;
183 } else {
184 /* Protracker handling */
185 if (!modtype) {
186 /* if we had a note, then change instrument...*/
187 if (note)
188 UniInstrument(instrument - 1);
189 /* ...otherwise, only adjust volume... */
190 else {
191 /* ...unless an effect was specified,
192 * which forces a new note to be
193 * played */
194 if (effect || effdat) {
195 UniInstrument(instrument - 1);
196 note = lastnote;
197 } else
198 UniPTEffect(0xc,
199 mh->samples[instrument -
200 1].volume & 0x7f);
201 }
202 } else {
203 /* Fasttracker handling */
204 UniInstrument(instrument - 1);
205 if (!note)
206 note = lastnote;
207 }
208 }
209 }
210 if (note) {
211 UniNote(note + 2 * OCTAVE - 1);
212 lastnote = note;
213 }
214
215 /* Convert pattern jump from Dec to Hex */
216 if (effect == 0xd)
217 effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);
218
219 /* Volume slide, up has priority */
220 if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))
221 effdat &= 0xf0;
222
223 UniPTEffect(effect, effdat);
224 }
225
ConvertTrack(MODNOTE * n)226 static UBYTE *ConvertTrack(MODNOTE *n)
227 {
228 int t;
229
230 UniReset();
231 for (t = 0; t < 64; t++) {
232 ConvertNote(n);
233 UniNewline();
234 n += of.numchn;
235 }
236 return UniDup();
237 }
238
239 /* Loads all patterns of a modfile and converts them into the 3 byte format. */
ML_LoadPatterns(void)240 static BOOL ML_LoadPatterns(void)
241 {
242 int t, s, tracks = 0;
243
244 if (!AllocPatterns()) {
245 return 0;
246 }
247 if (!AllocTracks()) {
248 return 0;
249 }
250
251 /* Allocate temporary buffer for loading and converting the patterns */
252 if (!(patbuf = (MODNOTE *)MikMod_calloc(64U * of.numchn, sizeof(MODNOTE))))
253 return 0;
254
255
256 /* patterns start here */
257 _mm_fseek(modreader, 0xA66, SEEK_SET);
258 for (t = 0; t < of.numpat; t++) {
259 /* Load the pattern into the temp buffer and convert it */
260 for (s = 0; s < (64U * of.numchn); s++) {
261 patbuf[s].a = _mm_read_UBYTE(modreader);
262 patbuf[s].b = _mm_read_UBYTE(modreader);
263 patbuf[s].c = _mm_read_UBYTE(modreader);
264 patbuf[s].d = _mm_read_UBYTE(modreader);
265 }
266 for (s = 0; s < of.numchn; s++) {
267 if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s))) {
268 return 0;
269 }
270 }
271 }
272 return 1;
273 }
274
ASY_Load(BOOL curious)275 static BOOL ASY_Load(BOOL curious)
276 {
277 int t;
278 SAMPLE *q;
279 MSAMPINFO *s;
280 CHAR *descr=asylum;
281 ULONG seekpos;
282
283 /* no title in asylum amf files :( */
284 mh->songname[0] = '\0';
285
286 _mm_fseek(modreader, 0x23, SEEK_SET);
287 mh->num_patterns = _mm_read_UBYTE(modreader);
288 mh->num_orders = _mm_read_UBYTE(modreader);
289
290 /* skip unknown byte */
291 _mm_skip_BYTE(modreader);
292 _mm_read_UBYTES(mh->positions, 256, modreader);
293
294 /* read samples headers*/
295 for (t = 0; t < 64; t++) {
296 s = &mh->samples[t];
297
298 _mm_fseek(modreader, 0x126 + (t*37), SEEK_SET);
299
300 _mm_read_string(s->samplename, 22, modreader);
301 s->samplename[21] = 0; /* just in case */
302
303 s->finetune = _mm_read_UBYTE(modreader);
304 s->volume = _mm_read_UBYTE(modreader);
305 _mm_skip_BYTE(modreader);/* skip unknown byte */
306 s->length = _mm_read_I_ULONG(modreader);
307 s->reppos = _mm_read_I_ULONG(modreader);
308 s->replen = _mm_read_I_ULONG(modreader);
309 }
310
311 if (_mm_eof(modreader)) {
312 _mm_errno = MMERR_LOADING_HEADER;
313 return 0;
314 }
315
316 /* set module variables */
317 of.initspeed = 6;
318 of.inittempo = 125;
319 of.numchn = 8;
320 modtype = 0;
321 of.songname = DupStr(mh->songname, 21, 1);
322 of.numpos = mh->num_orders;
323 of.reppos = 0;
324 of.numpat = mh->num_patterns;
325 of.numtrk = of.numpat * of.numchn;
326
327 /* Copy positions (orders) */
328 if (!AllocPositions(of.numpos))
329 return 0;
330 for (t = 0; t < of.numpos; t++) {
331 of.positions[t] = mh->positions[t];
332 }
333
334 /* Finally, init the sampleinfo structures */
335 of.numins = 31;
336 of.numsmp = 31;
337 if (!AllocSamples())
338 return 0;
339 s = mh->samples;
340 q = of.samples;
341 seekpos = 2662+(2048*(of.numpat));
342 for (t = 0; t < of.numins; t++) {
343 /* convert the samplename */
344 q->samplename = DupStr(s->samplename, 23, 1);
345
346 /* init the sampleinfo variables */
347 q->speed = finetune[s->finetune & 0xf];
348 q->volume = s->volume & 0x7f;
349
350 q->loopstart = (ULONG)s->reppos;
351 q->loopend = (ULONG)q->loopstart + (s->replen);
352 q->length = (ULONG)s->length;
353
354 q->flags = SF_SIGNED;
355
356 q->seekpos = seekpos;
357 seekpos += q->length;
358
359 if ((s->replen) > 2) {
360 q->flags |= SF_LOOP;
361 }
362
363 /* fix replen if repend > length */
364 if (q->loopend > q->length)
365 q->loopend = q->length;
366
367 s++;
368 q++;
369 }
370
371 of.modtype = MikMod_strdup(descr);
372
373 if (!ML_LoadPatterns())
374 return 0;
375
376 return 1;
377 }
378
ASY_LoadTitle(void)379 static CHAR *ASY_LoadTitle(void)
380 {
381 return MikMod_strdup("");
382 }
383
384 /*========== Loader information */
385
386 MLOADER load_asy = {
387 NULL,
388 "AMF",
389 "AMF (ASYLUM Music Format V1.0)",
390 ASY_Init,
391 ASY_Test,
392 ASY_Load,
393 ASY_Cleanup,
394 ASY_LoadTitle
395 };
396
397 /* ex:set ts=4: */
398