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