1 /*
2  * Schism Tracker - a cross-platform Impulse Tracker clone
3  * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4  * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5  * copyright (c) 2009 Storlek & Mrs. Brisby
6  * copyright (c) 2010-2012 Storlek
7  * URL: http://schismtracker.org/
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #define NEED_BYTESWAP
25 #include "headers.h"
26 #include "slurp.h"
27 #include "fmt.h"
28 #include "log.h"
29 
30 #include "sndfile.h"
31 
32 /* --------------------------------------------------------------------- */
33 
fmt_okt_read_info(dmoz_file_t * file,const uint8_t * data,size_t length)34 int fmt_okt_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
35 {
36 	if (!(length > 16 && memcmp(data, "OKTASONG", 8) == 0))
37 		return 0;
38 
39 	file->description = "Amiga Oktalyzer";
40 	/* okts don't have names? */
41 	file->title = strdup("");
42 	file->type = TYPE_MODULE_MOD;
43 	return 1;
44 }
45 
46 /* --------------------------------------------------------------------------------------------------------- */
47 
48 #define OKT_BLOCK(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
49 #define OKT_BLK_CMOD    OKT_BLOCK('C','M','O','D')
50 #define OKT_BLK_SAMP    OKT_BLOCK('S','A','M','P')
51 #define OKT_BLK_SPEE    OKT_BLOCK('S','P','E','E')
52 #define OKT_BLK_SLEN    OKT_BLOCK('S','L','E','N')
53 #define OKT_BLK_PLEN    OKT_BLOCK('P','L','E','N')
54 #define OKT_BLK_PATT    OKT_BLOCK('P','A','T','T')
55 #define OKT_BLK_PBOD    OKT_BLOCK('P','B','O','D')
56 #define OKT_BLK_SBOD    OKT_BLOCK('S','B','O','D')
57 
58 #pragma pack(push,1)
59 struct okt_sample {
60 	char name[20];
61 	uint32_t length;
62 	uint16_t loop_start;
63 	uint16_t loop_len;
64 	uint16_t volume;
65 	uint16_t mode;
66 };
67 #pragma pack(pop)
68 
69 enum {
70 	OKT_HAS_CMOD = 1 << 0,
71 	OKT_HAS_SAMP = 1 << 1,
72 	OKT_HAS_SPEE = 1 << 2,
73 	OKT_HAS_PLEN = 1 << 3,
74 	OKT_HAS_PATT = 1 << 4,
75 };
76 
77 
78 /* return: number of channels */
okt_read_cmod(song_t * song,slurp_t * fp)79 static int okt_read_cmod(song_t *song, slurp_t *fp)
80 {
81 	song_channel_t *cs = song->channels;
82 	int t, cn = 0;
83 
84 	for (t = 0; t < 4; t++) {
85 		if (slurp_getc(fp) || slurp_getc(fp))
86 			cs[cn++].panning = PROTRACKER_PANNING(t);
87 		cs[cn++].panning = PROTRACKER_PANNING(t);
88 	}
89 	for (t = cn; t < 64; t++)
90 		cs[t].flags |= CHN_MUTE;
91 	return cn;
92 }
93 
94 
okt_read_samp(song_t * song,slurp_t * fp,uint32_t len,uint32_t smpflag[])95 static void okt_read_samp(song_t *song, slurp_t *fp, uint32_t len, uint32_t smpflag[])
96 {
97 	unsigned int n;
98 	struct okt_sample osmp;
99 	song_sample_t *ssmp = song->samples + 1;
100 
101 	if (len % 32)
102 		log_appendf(4, " Warning: Sample data is misaligned");
103 	len /= 32;
104 	if (len >= MAX_SAMPLES) {
105 		log_appendf(4, " Warning: Too many samples in file");
106 		len = MAX_SAMPLES - 1;
107 	}
108 
109 	for (n = 1; n <= len; n++, ssmp++) {
110 		slurp_read(fp, &osmp, sizeof(osmp));
111 		osmp.length = bswapBE32(osmp.length);
112 		osmp.loop_start = bswapBE16(osmp.loop_start);
113 		osmp.loop_len = bswapBE16(osmp.loop_len);
114 		osmp.volume = bswapBE16(osmp.volume);
115 		osmp.mode = bswapBE16(osmp.mode);
116 
117 		strncpy(ssmp->name, osmp.name, 20);
118 		ssmp->name[20] = '\0';
119 		ssmp->length = osmp.length & ~1; // round down
120 		if (osmp.loop_len > 2 && osmp.loop_len + osmp.loop_start < ssmp->length) {
121 			ssmp->sustain_start = osmp.loop_start;
122 			ssmp->sustain_end = osmp.loop_start + osmp.loop_len;
123 			if (ssmp->sustain_start < ssmp->length && ssmp->sustain_end < ssmp->length)
124 				ssmp->flags |= CHN_SUSTAINLOOP;
125 			else
126 				ssmp->sustain_start = 0;
127 		}
128 		ssmp->loop_start *= 2;
129 		ssmp->loop_end *= 2;
130 		ssmp->sustain_start *= 2;
131 		ssmp->sustain_end *= 2;
132 		ssmp->volume = MIN(osmp.volume, 64) * 4; //mphack
133 		smpflag[n] = (osmp.mode == 0 || osmp.mode == 2) ? SF_7 : SF_8;
134 
135 		ssmp->c5speed = 8287;
136 		ssmp->global_volume = 64;
137 	}
138 }
139 
140 
141 /* Octalyzer effects list, straight from the internal help (acquired by running "strings octalyzer1.57") --
142 	- Effects Help Page --------------------------
143 	1 Portamento Down (4) (Period)
144 	2 Portamento Up   (4) (Period)
145 	A Arpeggio 1      (B) (down, orig,   up)
146 	B Arpeggio 2      (B) (orig,   up, orig, down)
147 	C Arpeggio 3      (B) (  up,   up, orig)
148 	D Slide Down      (B) (Notes)
149 	U Slide Up        (B) (Notes)
150 	L Slide Down Once (B) (Notes)
151 	H Slide Up   Once (B) (Notes)
152 	F Set Filter      (B) <>00:ON
153 	P Pos Jump        (B)
154 	S Speed           (B)
155 	V Volume          (B) <=40:DIRECT
156 	O Old Volume      (4)   4x:Vol Down      (VO)
157 				5x:Vol Up        (VO)
158 				6x:Vol Down Once (VO)
159 				7x:Vol Up   Once (VO)
160 Note that 1xx/2xx are apparently inverted from Protracker.
161 I'm not sure what "Old Volume" does -- continue a slide? reset to the sample's volume? */
162 
163 /* return: mask indicating effects that aren't implemented/recognized */
okt_read_pbod(song_t * song,slurp_t * fp,int nchn,int pat)164 static uint32_t okt_read_pbod(song_t *song, slurp_t *fp, int nchn, int pat)
165 {
166 	int row, chn, e;
167 	uint16_t rows;
168 	song_note_t *note;
169 	// bitset for effect warnings: (effwarn & (1 << (okteffect - 1)))
170 	// bit 1 is set if out of range values are encountered (Xxx, Yxx, Zxx, or garbage data)
171 	uint32_t effwarn = 0;
172 
173 	slurp_read(fp, &rows, 2);
174 	rows = bswapBE16(rows);
175 	rows = CLAMP(rows, 1, 200);
176 
177 	song->pattern_alloc_size[pat] = song->pattern_size[pat] = rows;
178 	note = song->patterns[pat] = csf_allocate_pattern(rows);
179 
180 	for (row = 0; row < rows; row++, note += 64 - nchn) {
181 		for (chn = 0; chn < nchn; chn++, note++) {
182 			note->note = slurp_getc(fp);
183 			note->instrument = slurp_getc(fp);
184 			e = slurp_getc(fp);
185 			note->param = slurp_getc(fp);
186 
187 			if (note->note && note->note <= 36) {
188 				note->note += 48;
189 				note->instrument++;
190 			} else {
191 				note->instrument = 0; // ?
192 			}
193 
194 			/* blah -- check for read error */
195 			if (e < 0)
196 				return effwarn;
197 
198 			switch (e) {
199 			case 0: // Nothing
200 				break;
201 
202 			/* 1/2 apparently are backwards from .mod? */
203 			case 1: // 1 Portamento Down (Period)
204 				note->effect = FX_PORTAMENTODOWN;
205 				note->param &= 0xf;
206 				break;
207 			case 2: // 2 Portamento Up (Period)
208 				note->effect = FX_PORTAMENTOUP;
209 				note->param &= 0xf;
210 				break;
211 
212 #if 0
213 			/* these aren't like Jxx: "down" means to *subtract* the offset from the note.
214 			For now I'm going to leave these unimplemented. */
215 			case 10: // A Arpeggio 1 (down, orig, up)
216 			case 11: // B Arpeggio 2 (orig, up, orig, down)
217 				if (note->param)
218 					note->effect = FX_WEIRDOKTARP;
219 				break;
220 #endif
221 
222 			/* This one is close enough to "standard" arpeggio -- I think! */
223 			case 12: // C Arpeggio 3 (up, up, orig)
224 				if (note->param)
225 					note->effect = FX_ARPEGGIO;
226 				break;
227 
228 			case 13: // D Slide Down (Notes)
229 				if (note->param) {
230 					note->effect = FX_NOTESLIDEDOWN;
231 					note->param = 0x10 | MIN(0xf, note->param);
232 				}
233 				break;
234 
235 			case 30: // U Slide Up (Notes)
236 				if (note->param) {
237 					note->effect = FX_NOTESLIDEUP;
238 					note->param = 0x10 | MIN(0xf, note->param);
239 				}
240 				break;
241 
242 			case 21: // L Slide Down Once (Notes)
243 				/* We don't have fine note slide, but this is supposed to happen once
244 				per row. Sliding every 5 (non-note) ticks kind of works (at least at
245 				speed 6), but implementing fine slides would of course be better. */
246 				if (note->param) {
247 					note->effect = FX_NOTESLIDEDOWN;
248 					note->param = 0x50 | MIN(0xf, note->param);
249 				}
250 				break;
251 
252 			case 17: // H Slide Up Once (Notes)
253 				if (note->param) {
254 					note->effect = FX_NOTESLIDEUP;
255 					note->param = 0x50 | MIN(0xf, note->param);
256 				}
257 				break;
258 
259 			case 15: // F Set Filter <>00:ON
260 				// Not implemented, but let's import it anyway...
261 				note->effect = FX_SPECIAL;
262 				note->param = !!note->param;
263 				break;
264 
265 			case 25: // P Pos Jump
266 				note->effect = FX_POSITIONJUMP;
267 				break;
268 
269 			case 27: // R Release sample (apparently not listed in the help!)
270 				note->note = NOTE_OFF;
271 				note->instrument = note->effect = note->param = 0;
272 				break;
273 
274 			case 28: // S Speed
275 				note->effect = FX_SPEED; // or tempo?
276 				break;
277 
278 			case 31: // V Volume
279 				note->effect = FX_VOLUMESLIDE;
280 				switch (note->param >> 4) {
281 				case 4:
282 					if (note->param != 0x40) {
283 						note->param &= 0xf; // D0x
284 						break;
285 					}
286 					// 0x40 is set volume -- fall through
287 				case 0 ... 3:
288 					note->voleffect = VOLFX_VOLUME;
289 					note->volparam = note->param;
290 					note->effect = FX_NONE;
291 					note->param = 0;
292 					break;
293 				case 5:
294 					note->param = (note->param & 0xf) << 4; // Dx0
295 					break;
296 				case 6:
297 					note->param = 0xf0 | MIN(note->param & 0xf, 0xe); // DFx
298 					break;
299 				case 7:
300 					note->param = (MIN(note->param & 0xf, 0xe) << 4) | 0xf; // DxF
301 					break;
302 				default:
303 					// Junk.
304 					note->effect = note->param = 0;
305 					break;
306 				}
307 				break;
308 
309 #if 0
310 			case 24: // O Old Volume
311 				/* ? */
312 				note->effect = FX_VOLUMESLIDE;
313 				note->param = 0;
314 				break;
315 #endif
316 
317 			default:
318 				//log_appendf(2, " Pattern %d, row %d: effect %d %02X",
319 				//        pat, row, e, note->param);
320 				effwarn |= (e > 32) ? 1 : (1 << (e - 1));
321 				note->effect = FX_UNIMPLEMENTED;
322 				break;
323 			}
324 		}
325 	}
326 
327 	return effwarn;
328 }
329 
330 /* --------------------------------------------------------------------------------------------------------- */
331 
fmt_okt_load_song(song_t * song,slurp_t * fp,unsigned int lflags)332 int fmt_okt_load_song(song_t *song, slurp_t *fp, unsigned int lflags)
333 {
334 	uint8_t tag[8];
335 	unsigned int readflags = 0;
336 	uint16_t w; // temp for reading
337 	int plen = 0; // how many positions in the orderlist are valid
338 	int npat = 0; // next pattern to read
339 	int nsmp = 1; // next sample (data, not header)
340 	int pat, sh, sd, e; // iterators (pattern, sample header, sample data, effect warnings
341 	int nchn = 0; // how many channels does this song use?
342 	size_t patseek[MAX_PATTERNS] = {0};
343 	size_t smpseek[MAX_SAMPLES + 1] = {0}; // where the sample's data starts
344 	uint32_t smpsize[MAX_SAMPLES + 2] = {0}; // data size (one element bigger to simplify loop condition)
345 	uint32_t smpflag[MAX_SAMPLES + 1] = {0}; // bit width
346 	uint32_t effwarn = 0; // effect warning mask
347 
348 	slurp_read(fp, tag, 8);
349 	if (memcmp(tag, "OKTASONG", 8) != 0)
350 		return LOAD_UNSUPPORTED;
351 
352 	while (!slurp_eof(fp)) {
353 		uint32_t blklen; // length of this block
354 		size_t nextpos; // ... and start of next one
355 
356 		slurp_read(fp, tag, 4);
357 		slurp_read(fp, &blklen, 4);
358 		blklen = bswapBE32(blklen);
359 		nextpos = slurp_tell(fp) + blklen;
360 
361 		switch (OKT_BLOCK(tag[0], tag[1], tag[2], tag[3])) {
362 		case OKT_BLK_CMOD:
363 			if (!(readflags & OKT_HAS_CMOD)) {
364 				readflags |= OKT_HAS_CMOD;
365 				nchn = okt_read_cmod(song, fp);
366 			}
367 			break;
368 		case OKT_BLK_SAMP:
369 			if (!(readflags & OKT_HAS_SAMP)) {
370 				readflags |= OKT_HAS_SAMP;
371 				okt_read_samp(song, fp, blklen, smpflag);
372 			}
373 			break;
374 		case OKT_BLK_SPEE:
375 			if (!(readflags & OKT_HAS_SPEE)) {
376 				readflags |= OKT_HAS_SPEE;
377 				slurp_read(fp, &w, 2);
378 				w = bswapBE16(w);
379 				song->initial_speed = CLAMP(w, 1, 255);
380 				song->initial_tempo = 125;
381 			}
382 			break;
383 		case OKT_BLK_SLEN:
384 			// Don't care.
385 			break;
386 		case OKT_BLK_PLEN:
387 			if (!(readflags & OKT_HAS_PLEN)) {
388 				readflags |= OKT_HAS_PLEN;
389 				slurp_read(fp, &w, 2);
390 				plen = bswapBE16(w);
391 			}
392 			break;
393 		case OKT_BLK_PATT:
394 			if (!(readflags & OKT_HAS_PATT)) {
395 				readflags |= OKT_HAS_PATT;
396 				slurp_read(fp, song->orderlist, MIN(blklen, MAX_ORDERS));
397 			}
398 			break;
399 		case OKT_BLK_PBOD:
400 			/* Need the channel count (in CMOD) in order to read these */
401 			if (npat < MAX_PATTERNS) {
402 				if (blklen > 0)
403 					patseek[npat] = slurp_tell(fp);
404 				npat++;
405 			}
406 			break;
407 		case OKT_BLK_SBOD:
408 			if (nsmp < MAX_SAMPLES) {
409 				smpseek[nsmp] = slurp_tell(fp);
410 				smpsize[nsmp] = blklen;
411 				if (smpsize[nsmp])
412 					nsmp++;
413 			}
414 			break;
415 
416 		default:
417 			//log_appendf(4, " Warning: Unknown block of type '%c%c%c%c' at 0x%lx",
418 			//        tag[0], tag[1], tag[2], tag[3], fp->pos - 8);
419 			break;
420 		}
421 
422 		if (slurp_seek(fp, nextpos, SEEK_SET) != 0) {
423 			log_appendf(4, " Warning: Failed to seek (file truncated?)");
424 			break;
425 		}
426 	}
427 
428 	if ((readflags & (OKT_HAS_CMOD | OKT_HAS_SPEE)) != (OKT_HAS_CMOD | OKT_HAS_SPEE))
429 		return LOAD_FORMAT_ERROR;
430 
431 	if (!(lflags & LOAD_NOPATTERNS)) {
432 		for (pat = 0; pat < npat; pat++) {
433 			slurp_seek(fp, patseek[pat], SEEK_SET);
434 			effwarn |= okt_read_pbod(song, fp, nchn, pat);
435 		}
436 
437 		if (effwarn) {
438 			if (effwarn & 1)
439 				log_appendf(4, " Warning: Out-of-range effects (junk data?)");
440 			for (e = 2; e <= 32; e++) {
441 				if (effwarn & (1 << (e - 1))) {
442 					log_appendf(4, " Warning: Unimplemented effect %cxx",
443 						e + (e < 10 ? '0' : ('A' - 10)));
444 				}
445 			}
446 		}
447 	}
448 
449 	if (!(lflags & LOAD_NOSAMPLES)) {
450 		for (sh = sd = 1; sh < MAX_SAMPLES && smpsize[sd]; sh++) {
451 			song_sample_t *ssmp = song->samples + sh;
452 			if (!ssmp->length)
453 				continue;
454 
455 			if (ssmp->length != smpsize[sd]) {
456 				log_appendf(4, " Warning: Sample %d: header/data size mismatch (%d/%d)", sh,
457 					ssmp->length, smpsize[sd]);
458 				ssmp->length = MIN(smpsize[sd], ssmp->length);
459 			}
460 
461 			csf_read_sample(ssmp, SF_BE | SF_M | SF_PCMS | smpflag[sd],
462 					fp->data + smpseek[sd], ssmp->length);
463 			sd++;
464 		}
465 		// Make sure there's nothing weird going on
466 		for (; sh < MAX_SAMPLES; sh++) {
467 			if (song->samples[sh].length) {
468 				log_appendf(4, " Warning: Sample %d: file truncated", sh);
469 				song->samples[sh].length = 0;
470 			}
471 		}
472 	}
473 
474 	song->pan_separation = 64;
475 	memset(song->orderlist + plen, ORDER_LAST, MAX(0, MAX_ORDERS - plen));
476 	strcpy(song->tracker_id, "Amiga Oktalyzer");
477 
478 	return LOAD_SUCCESS;
479 }
480 
481