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