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