1 /*	MikMod sound library
2 	(c) 1999, 2000 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   Oktalyzer (OKT) module loader
26 
27 ==============================================================================*/
28 
29 /*
30 	Written by UFO <ufo303@poczta.onet.pl>
31 	based on the file description compiled by Harald Zappe
32 	                                                      <zappe@gaea.sietec.de>
33 
34 */
35 
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 
44 #include <stdio.h>
45 #include <string.h>
46 
47 #include "unimod_priv.h"
48 
49 /*========== Module blocks */
50 
51 /* sample information */
52 typedef struct OKTSAMPLE {
53 	CHAR sampname[20];
54 	ULONG len;
55 	UWORD loopbeg;
56 	UWORD looplen;
57 	UBYTE volume;
58 } OKTSAMPLE;
59 
60 typedef struct OKTNOTE {
61 	UBYTE note, ins, eff, dat;
62 } OKTNOTE;
63 
64 /*========== Loader variables */
65 
66 static OKTNOTE *okttrk = NULL;
67 
68 /*========== Loader code */
69 
70 static BOOL
OKT_Test(void)71 OKT_Test(void)
72 {
73 	CHAR id[8];
74 
75 	if (!_mm_read_UBYTES(id, 8, modreader))
76 		return 0;
77 	if (!memcmp(id, "OKTASONG", 8))
78 		return 1;
79 
80 	return 0;
81 }
82 
83 /*	Pattern analysis routine.
84 	Effects not implemented (yet) : (in decimal)
85 	11 Arpeggio 4: Change note every 50Hz tick between N,H,N,L
86 	12 Arpeggio 5: Change note every 50Hz tick between H,H,N
87                    N = normal note being played in this channel (1-36)
88                    L = normal note number minus upper four bits of 'data'.
89                    H = normal note number plus  lower four bits of 'data'.
90     13 Decrease note number by 'data' once per tick.
91     17 Increase note number by 'data' once per tick.
92     21 Decrease note number by 'data' once per line.
93     30 Increase note number by 'data' once per line.
94 */
OKT_ConvertTrack(UBYTE patrows)95 static UBYTE *OKT_ConvertTrack(UBYTE patrows)
96 {
97 	int t;
98 	UBYTE ins, note, eff, dat;
99 
100 	UniReset();
101 	for (t = 0; t < patrows; t++) {
102 		note = okttrk[t].note;
103 		ins = okttrk[t].ins;
104 		eff = okttrk[t].eff;
105 		dat = okttrk[t].dat;
106 
107 		if (note) {
108 			UniNote(note + 3*OCTAVE - 1);
109 			UniInstrument(ins);
110 		}
111 
112 		if (eff)
113 			switch (eff) {
114 			  case 1:			/* Porta Up */
115 				UniPTEffect(0x1, dat);
116 				break;
117 			  case 2:			/* Portamento Down */
118 				UniPTEffect(0x2, dat);
119 				break;
120 			  case 10:			/* Arpeggio 3 supported */
121 				UniPTEffect(0x0, dat);
122 				break;
123 			  case 15:	/* Amiga filter toggle, ignored */
124 				break;
125 			  case 25:			/* Pattern Jump */
126 				UniPTEffect(0xb, dat);
127 				break;
128 			  case 27:			/* Release - similar to Keyoff */
129 				UniWriteByte(UNI_KEYOFF);
130 				break;
131 			  case 28:			/* Set Tempo */
132 				UniPTEffect(0xf, dat);
133 				break;
134 			  case 31:			/* volume Control */
135 				if (dat <= 0x40)
136 					UniPTEffect(0xc, dat);
137 				else if (dat <= 0x50)
138 					UniEffect(UNI_XMEFFECTA, (dat - 0x40));	/* fast fade out */
139 				else if (dat <= 0x60)
140 					UniEffect(UNI_XMEFFECTA, (dat - 0x50) << 4);	/* fast fade in */
141 				else if (dat <= 0x70)
142 					UniEffect(UNI_XMEFFECTEB, (dat - 0x60));	/* slow fade out */
143 				else if (dat <= 0x80)
144 					UniEffect(UNI_XMEFFECTEA, (dat - 0x70));	/* slow fade in */
145 				break;
146 #ifdef MIKMOD_DEBUG
147 			  default:
148 				fprintf(stderr, "\rUnimplemented effect (%02d,%02x)\n",
149 						eff, dat);
150 #endif
151 			}
152 
153 		UniNewline();
154 	}
155 	return UniDup();
156 }
157 
158 /* Read "channel modes" i.e. channel number and panning information */
OKT_doCMOD(void)159 static void OKT_doCMOD(void)
160 {
161 	/* amiga channel panning table */
162 	UBYTE amigapan[4] = { 0x00, 0xff, 0xff, 0x00 };
163 	int t;
164 
165 	of.numchn = 0;
166 
167 	for (t = 0; t < 4; t++)
168 		if (_mm_read_M_UWORD(modreader)) {
169 			/* two channels tied to the same Amiga hardware voice */
170 			of.panning[of.numchn++] = amigapan[t];
171 			of.panning[of.numchn++] = amigapan[t];
172 		} else
173 			/* one channel tied to the Amiga hardware voice */
174 			of.panning[of.numchn++] = amigapan[t];
175 }
176 
177 /* Read sample information */
OKT_doSAMP(int len)178 static BOOL OKT_doSAMP(int len)
179 {
180 	int t;
181 	SAMPLE *q;
182 	OKTSAMPLE s;
183 
184 	of.numins = of.numsmp = (len / 0x20);
185 	if (!AllocSamples())
186 		return 0;
187 
188 	for (t = 0, q = of.samples; t < of.numins; t++, q++) {
189 		_mm_read_UBYTES(s.sampname, 20, modreader);
190 		s.len = _mm_read_M_ULONG(modreader);
191 		s.loopbeg = _mm_read_M_UWORD(modreader);
192 		s.looplen = _mm_read_M_UWORD(modreader);
193 		_mm_read_UBYTE(modreader);
194 		s.volume = _mm_read_UBYTE(modreader);
195 		_mm_read_M_UWORD(modreader);
196 
197 		if (_mm_eof(modreader)) {
198 			_mm_errno = MMERR_LOADING_SAMPLEINFO;
199 			return 0;
200 		}
201 
202 		if (!s.len)
203 			q->seekpos = q->length = q->loopstart = q->loopend = q->flags = 0;
204 		else {
205 			s.len--;
206 			/* sanity checks */
207 			if (s.loopbeg > s.len)
208 				s.loopbeg = s.len;
209 			if (s.loopbeg + s.looplen > s.len)
210 				s.looplen = s.len - s.loopbeg;
211 			if (s.looplen < 2)
212 				s.looplen = 0;
213 
214 			q->length = s.len;
215 			q->loopstart = s.loopbeg;
216 			q->loopend = s.looplen + q->loopstart;
217 			q->volume = s.volume;
218 			q->flags = SF_SIGNED;
219 
220 			if (s.looplen)
221 				q->flags |= SF_LOOP;
222 		}
223 		q->samplename = DupStr(s.sampname, 20, 1);
224 		q->speed = 8363;
225 	}
226 	return 1;
227 }
228 
229 /* Read speed information */
OKT_doSPEE(void)230 static void OKT_doSPEE(void)
231 {
232 	int tempo = _mm_read_M_UWORD(modreader);
233 
234 	of.initspeed = tempo;
235 }
236 
237 /* Read song length information */
OKT_doSLEN(void)238 static void OKT_doSLEN(void)
239 {
240 	of.numpat = _mm_read_M_UWORD(modreader);
241 }
242 
243 /* Read pattern length information */
OKT_doPLEN(void)244 static void OKT_doPLEN(void)
245 {
246 	of.numpos = _mm_read_M_UWORD(modreader);
247 }
248 
249 /* Read order table */
OKT_doPATT(void)250 static BOOL OKT_doPATT(void)
251 {
252 	int t;
253 
254 	if (!of.numpos || !AllocPositions(of.numpos))
255 		return 0;
256 
257 	for (t = 0; t < 128; t++)
258 		if (t < of.numpos)
259 			of.positions[t] = (UWORD)_mm_read_UBYTE(modreader);
260 		else
261 			break;
262 
263 	return 1;
264 }
265 
OKT_doPBOD(int patnum)266 static BOOL OKT_doPBOD(int patnum)
267 {
268 	char *patbuf;
269 	int rows, i;
270 	int u;
271 
272 	if (!patnum) {
273 		of.numtrk = of.numpat * of.numchn;
274 
275 		if (!AllocTracks() || !AllocPatterns())
276 			return 0;
277 	}
278 
279 	/* Read pattern */
280 	of.pattrows[patnum] = rows = _mm_read_M_UWORD(modreader);
281 
282 	if (!(okttrk = (OKTNOTE *) _mm_calloc(rows, sizeof(OKTNOTE))) ||
283 	    !(patbuf = (char *)_mm_calloc(rows * of.numchn, sizeof(OKTNOTE))))
284 		return 0;
285 	_mm_read_UBYTES(patbuf, rows * of.numchn * sizeof(OKTNOTE), modreader);
286 	if (_mm_eof(modreader)) {
287 		_mm_errno = MMERR_LOADING_PATTERN;
288 		return 0;
289 	}
290 
291 	for (i = 0; i < of.numchn; i++) {
292 		for (u = 0; u < rows; u++) {
293 			okttrk[u].note = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE)];
294 			okttrk[u].ins = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 1];
295 			okttrk[u].eff = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 2];
296 			okttrk[u].dat = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 3];
297 		}
298 
299 		if (!(of.tracks[patnum * of.numchn + i] = OKT_ConvertTrack(rows)))
300 			return 0;
301 	}
302 	_mm_free(patbuf);
303 	_mm_free(okttrk);
304 	return 1;
305 }
306 
OKT_doSBOD(int insnum)307 static void OKT_doSBOD(int insnum)
308 {
309 	of.samples[insnum].seekpos = _mm_ftell(modreader);
310 }
311 
312 static BOOL
OKT_Load(BOOL curious)313 OKT_Load(BOOL curious)
314 {
315 	UBYTE id[4];
316 	ULONG len;
317 	ULONG fp;
318 	BOOL seen_cmod = 0, seen_samp = 0, seen_slen = 0, seen_plen = 0, seen_patt
319 			= 0, seen_spee = 0;
320 	int patnum = 0, insnum = 0;
321 
322 	/* skip OKTALYZER header */
323 	_mm_fseek(modreader, 8, SEEK_SET);
324 	of.songname = strdup("");
325 
326 	of.modtype = strdup("Amiga Oktalyzer");
327 	of.numpos = of.reppos = 0;
328 
329 	/* default values */
330 	of.initspeed = 6;
331 	of.inittempo = 125;
332 
333 	while (1) {
334 		/* read block header */
335 		_mm_read_UBYTES(id, 4, modreader);
336 		len = _mm_read_M_ULONG(modreader);
337 
338 		if (_mm_eof(modreader))
339 			break;
340 		fp = _mm_ftell(modreader);
341 
342 		if (!memcmp(id, "CMOD", 4)) {
343 			if (!seen_cmod) {
344 				OKT_doCMOD();
345 				seen_cmod = 1;
346 			} else {
347 				_mm_errno = MMERR_LOADING_HEADER;
348 				return 0;
349 			}
350 		} else if (!memcmp(id, "SAMP", 4)) {
351 			if (!seen_samp && OKT_doSAMP(len))
352 				seen_samp = 1;
353 			else {
354 				_mm_errno = MMERR_LOADING_HEADER;
355 				return 0;
356 			}
357 		} else if (!memcmp(id, "SPEE", 4)) {
358 			if (!seen_spee) {
359 				OKT_doSPEE();
360 				seen_spee = 1;
361 			} else {
362 				_mm_errno = MMERR_LOADING_HEADER;
363 				return 0;
364 			}
365 		} else if (!memcmp(id, "SLEN", 4)) {
366 			if (!seen_slen) {
367 				OKT_doSLEN();
368 				seen_slen = 1;
369 			} else {
370 				_mm_errno = MMERR_LOADING_HEADER;
371 				return 0;
372 			}
373 		} else if (!memcmp(id, "PLEN", 4)) {
374 			if (!seen_plen) {
375 				OKT_doPLEN();
376 				seen_plen = 1;
377 			} else {
378 				_mm_errno = MMERR_LOADING_HEADER;
379 				return 0;
380 			}
381 		} else if (!memcmp(id, "PATT", 4)) {
382 			if (!seen_plen) {
383 				_mm_errno = MMERR_LOADING_HEADER;
384 				return 0;
385 			}
386 			if (!seen_patt && OKT_doPATT())
387 				seen_patt = 1;
388 			else {
389 				_mm_errno = MMERR_LOADING_HEADER;
390 				return 0;
391 			}
392 		} else if (!memcmp(id,"PBOD", 4)) {
393 			/* need to know numpat and numchn */
394 			if (!seen_slen || !seen_cmod || (patnum >= of.numpat)) {
395 				_mm_errno = MMERR_LOADING_HEADER;
396 				return 0;
397 			}
398 			if (!OKT_doPBOD(patnum++)) {
399 				_mm_errno = MMERR_LOADING_PATTERN;
400 				return 0;
401 			}
402 		} else if (!memcmp(id,"SBOD",4)) {
403 			/* need to know numsmp */
404 			if (!seen_samp) {
405 				_mm_errno = MMERR_LOADING_HEADER;
406 				return 0;
407 			}
408 			while ((insnum < of.numins) && !of.samples[insnum].length)
409 				insnum++;
410 			if (insnum >= of.numins) {
411 				_mm_errno = MMERR_LOADING_HEADER;
412 				return 0;
413 			}
414 			OKT_doSBOD(insnum++);
415 		}
416 
417 		/* goto next block start position */
418 		_mm_fseek(modreader, fp + len, SEEK_SET);
419 	}
420 
421 	if (!seen_cmod || !seen_samp || !seen_patt ||
422 		!seen_slen || !seen_plen || (patnum != of.numpat)) {
423 		_mm_errno = MMERR_LOADING_HEADER;
424 		return 0;
425 	}
426 
427 	return 1;
428 }
429 
OKT_LoadTitle(void)430 static CHAR *OKT_LoadTitle(void)
431 {
432 	return strdup("");
433 }
434 
435 /*========== Loader information */
436 
437 MLOADER load_okt = {
438 	NULL,
439 	"OKT",
440 	"OKT (Amiga Oktalyzer)",
441 	NULL,
442 	OKT_Test,
443 	OKT_Load,
444 	NULL,
445 	OKT_LoadTitle
446 };
447 
448 /* ex:set ts=4: */
449