/* Extended Module Player * Copyright (C) 1996-1999 Claudio Matsuoka and Hipolito Carraro Jr * * This file is part of the Extended Module Player and is distributed * under the terms of the GNU General Public License. See doc/COPYING * for more information. */ /* Reverse engineered from the two SFX files in the Delitracker mods disk * and music from Future Wars, Twinworld and Operation Stealth. Effects * must be verified/implemented. */ /* From the ExoticRipper docs: * [SoundFX 2.0 is] simply the same as SoundFX 1.3, except that it * uses 31 samples [instead of 15]. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "period.h" #include "load.h" struct sfx_ins { uint8 name[22]; /* Instrument name */ uint16 len; /* Sample length in words */ uint8 finetune; /* Finetune */ uint8 volume; /* Volume (0-63) */ uint16 loop_start; /* Sample loop start in bytes */ uint16 loop_length; /* Sample loop length in words */ } PACKED; struct sfx_header { uint8 magic[4]; /* 'SONG' */ uint16 delay; /* Delay value (tempo), default is 0x38e5 */ uint16 unknown[7]; /* ? */ } PACKED; struct sfx_header2 { uint8 len; /* Song length */ uint8 restart; /* Restart pos (?) */ uint8 order[128]; /* Order list */ } PACKED; static int sfx_13_20_load (FILE *, int); int sfx_load (FILE * f) { if (sfx_13_20_load (f, 15) < 0) return sfx_13_20_load (f, 31); return 0; } static int sfx_13_20_load (FILE *f, int nins) { int i, j; struct xxm_event *event; struct sfx_header sfx; struct sfx_header2 sfx2; uint8 ev[4]; int ins_size[31]; struct sfx_ins ins[31]; /* Instruments */ LOAD_INIT (); for (i = 0; i < nins; i++) fread (&ins_size[i], 1, 4, f); fread (&sfx, 1, sizeof (sfx), f); if (strncmp ((char *) sfx.magic, "SONG", 4)) return -1; B_ENDIAN16 (sfx.delay); xxh->ins = nins; xxh->smp = xxh->ins; xxh->bpm = 14565 * 122 / sfx.delay; for (i = 0; i < xxh->ins; i++) { fread (&ins[i], 1, sizeof(struct sfx_ins), f); B_ENDIAN32 (ins_size[i]); B_ENDIAN16 (ins[i].len); B_ENDIAN16 (ins[i].loop_start); B_ENDIAN16 (ins[i].loop_length); } fread (&sfx2, 1, sizeof (sfx2), f); xxh->len = sfx2.len; if (xxh->len > 0x7f) return -1; memcpy (xxo, sfx2.order, xxh->len); for (xxh->pat = i = 0; i < xxh->len; i++) if (xxo[i] > xxh->pat) xxh->pat = xxo[i]; xxh->pat++; xxh->trk = xxh->chn * xxh->pat; strcpy (xmp_ctl->type, xxh->ins == 15 ? "SoundFX 1.3" : "SoundFX 2.0"); MODULE_INFO (); INSTRUMENT_INIT (); if (V (1)) report (" Instrument name Len LBeg LEnd L Vol Fin\n"); for (i = 0; i < xxh->ins; i++) { xxi[i] = calloc (sizeof (struct xxm_instrument), 1); xxih[i].nsm = !!(xxs[i].len = ins_size[i]); xxs[i].lps = ins[i].loop_start; xxs[i].lpe = xxs[i].lps + 2 * ins[i].loop_length; xxs[i].flg = ins[i].loop_length > 1 ? WAVE_LOOPING : 0; xxi[i][0].vol = ins[i].volume; xxi[i][0].fin = (int8)(ins[i].finetune << 4); xxi[i][0].pan = 0x80; xxi[i][0].sid = i; strncpy ((char *) xxih[i].name, ins[i].name, 22); if ((V (1)) && (strlen ((char *) xxih[i].name) || (xxs[i].len > 2))) report ("[%2X] %-22.22s %04x %04x %04x %c %02x %+d\n", i, xxih[i].name, xxs[i].len, xxs[i].lps, xxs[i].lpe, xxs[i].flg & WAVE_LOOPING ? 'L' : ' ', xxi[i][0].vol, (char) xxi[i][0].fin >> 4); } PATTERN_INIT (); if (V (0)) report ("Stored patterns: %d ", xxh->pat); for (i = 0; i < xxh->pat; i++) { PATTERN_ALLOC (i); xxp[i]->rows = 64; TRACK_ALLOC (i); for (j = 0; j < 64 * xxh->chn; j++) { event = &EVENT (i, j % xxh->chn, j / xxh->chn); fread (ev, 1, 4, f); event->note = period_to_note ((LSN (ev[0]) << 8) | ev[1]); event->ins = (MSN (ev[0]) << 4) | MSN (ev[2]); event->fxp = ev[3]; switch (LSN (ev[2])) { case 0x1: /* Arpeggio */ event->fxt = FX_ARPEGGIO; break; case 0x02: /* Pitch bend */ if (event->fxp >> 4) { event->fxt = FX_PORTA_DN; event->fxp >>= 4; } else if (event->fxp & 0x0f) { event->fxt = FX_PORTA_UP; event->fxp &= 0x0f; } break; case 0x5: /* Volume up */ event->fxt = FX_VOLSLIDE_DN; break; case 0x6: /* Set volume (attenuation) */ event->fxt = FX_VOLSET; event->fxp = 0x40 - ev[3]; break; case 0x3: /* LED on */ case 0x4: /* LED off */ case 0x7: /* Set step up */ case 0x8: /* Set step down */ default: event->fxt = event->fxp = 0; break; } } if (V (0)) report ("."); } xxh->flg |= XXM_FLG_MODRNG; /* Read samples */ if (V (0)) report ("\nStored samples : %d ", xxh->smp); for (i = 0; i < xxh->ins; i++) { if (xxs[i].len <= 2) continue; xmp_drv_loadpatch (f, i, xmp_ctl->c4rate, 0, &xxs[i], NULL); if (V (0)) report ("."); } if (V (0)) report ("\n"); return 0; }