/*
 * Schism Tracker - a cross-platform Impulse Tracker clone
 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
 * copyright (c) 2009 Storlek & Mrs. Brisby
 * copyright (c) 2010-2012 Storlek
 * URL: http://schismtracker.org/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define NEED_BYTESWAP
#include "headers.h"
#include "slurp.h"
#include "fmt.h"
#include "log.h"

#include "sndfile.h"

/* --------------------------------------------------------------------------------------------------------- */

/* None of the sfx files on Modland are of the 31-instrument type that xmp recognizes.
However, there are a number of 31-instrument files with a different tag, under "SoundFX 2". */

static struct sfxfmt {
        size_t tagpos;
        const char tag[4];
        int nsmp;
        int dunno;
        const char *id;
} sfxfmts[] = {
        {124, "SO31", 31, 4, "SoundFX 2"},
        {124, "SONG", 31, 0, "SoundFX 2 (?)"},
        { 60, "SONG", 15, 0, "SoundFX"},
        {  0, ""    ,  0, 0, NULL},
};


int fmt_sfx_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
{
        int n;
        for (n = 0; sfxfmts[n].nsmp; n++) {
                if (length >= sfxfmts[n].tagpos + 4
                    && memcmp(data + sfxfmts[n].tagpos, sfxfmts[n].tag, 4) == 0) {
                        file->description = sfxfmts[n].id;
                        /*file->extension = str_dup("sfx");*/
                        file->title = strdup(""); // whatever
                        file->type = TYPE_MODULE_MOD;
                        return 1;
                }
        }
        return 0;
}

/* --------------------------------------------------------------------------------------------------------- */

/* Loader taken mostly from XMP.

Why did I write a loader for such an obscure format? That is, besides the fact that neither Modplug nor
Mikmod support SFX (and for good reason; it's a particularly dumb format) */

int fmt_sfx_load_song(song_t *song, slurp_t *fp, unsigned int lflags)
{
        uint8_t tag[4];
        int n, nord, npat, pat, chan, restart, nsmp = 0;
        uint32_t smpsize[31];
        uint16_t tmp;
        song_note_t *note;
        song_sample_t *sample;
        unsigned int effwarn = 0;
        struct sfxfmt *fmt = sfxfmts;

        do {
                slurp_seek(fp, fmt->tagpos, SEEK_SET);
                slurp_read(fp, tag, 4);
                if (memcmp(tag, fmt->tag, 4) == 0) {
                        nsmp = fmt->nsmp;
                        break;
                }
                fmt++;
        } while (fmt->nsmp);

        if (!nsmp)
                return LOAD_UNSUPPORTED;


        slurp_rewind(fp);
        slurp_read(fp, smpsize, 4 * nsmp);
        slurp_seek(fp, 4, SEEK_CUR); /* the tag again */
        slurp_read(fp, &tmp, 2);
        if (!tmp)
                return LOAD_UNSUPPORTED; // erf
        tmp = 14565 * 122 / bswapBE16(tmp);
        song->initial_tempo = CLAMP(tmp, 31, 255);

        slurp_seek(fp, 14, SEEK_CUR); /* unknown bytes (reserved?) - see below */

        if (lflags & LOAD_NOSAMPLES) {
                slurp_seek(fp, 30 * nsmp, SEEK_CUR);
        } else {
                for (n = 0, sample = song->samples + 1; n < nsmp; n++, sample++) {
                        slurp_read(fp, sample->name, 22);
                        sample->name[22] = 0;
                        slurp_read(fp, &tmp, 2); /* seems to be half the sample size, minus two bytes? */
                        tmp = bswapBE16(tmp);
                        sample->length = bswapBE32(smpsize[n]);

                        song->samples[n].c5speed = MOD_FINETUNE(slurp_getc(fp)); // ?
                        sample->volume = slurp_getc(fp);
                        if (sample->volume > 64)
                                sample->volume = 64;
                        sample->volume *= 4; //mphack
                        sample->global_volume = 64;
                        slurp_read(fp, &tmp, 2);
                        sample->loop_start = bswapBE16(tmp);
                        slurp_read(fp, &tmp, 2);
                        tmp = bswapBE16(tmp) * 2; /* loop length */
                        if (tmp > 2) {
                                sample->loop_end = sample->loop_start + tmp;
                                sample->flags |= CHN_LOOP;
                        } else {
                                sample->loop_start = sample->loop_end = 0;
                        }
                }
        }

        /* pattern/order stuff */
        nord = slurp_getc(fp);
        nord = MIN(nord, 127);
        restart = slurp_getc(fp);
        slurp_read(fp, song->orderlist, nord);
        slurp_seek(fp, 128 - nord, SEEK_CUR);
        npat = 0;
        for (n = 0; n < nord; n++) {
                if (song->orderlist[n] > npat)
                        npat = song->orderlist[n];
        }
        npat++;

        /* Not sure what this is about, but skipping a few bytes here seems to make SO31's load right.
        (they all seem to have zero here) */
        slurp_seek(fp, fmt->dunno, SEEK_CUR);

        if (lflags & LOAD_NOPATTERNS) {
                slurp_seek(fp, npat * 1024, SEEK_CUR);
        } else {
                for (pat = 0; pat < npat; pat++) {
                        note = song->patterns[pat] = csf_allocate_pattern(64);
                        song->pattern_size[pat] = song->pattern_alloc_size[pat] = 64;
                        for (n = 0; n < 64; n++, note += 60) {
                                for (chan = 0; chan < 4; chan++, note++) {
                                        uint8_t p[4];
                                        slurp_read(fp, p, 4);
                                        mod_import_note(p, note);
                                        /* Note events starting with FF all seem to be special in some way:
                                                bytes   apparent use    example file on modland
                                                -----   ------------    -----------------------
                                                FF FE   note cut        1st intro.sfx
                                                FF FD   unknown!        another world (intro).sfx
                                                FF FC   pattern break   orbit wanderer.sfx2 */
                                        if (p[0] == 0xff) {
                                                switch (p[1]) {
                                                case 0xfc:
                                                        note->note = NOTE_NONE;
                                                        note->instrument = 0;
                                                        // stuff a C00 in channel 5
                                                        note[4 - chan].effect = FX_PATTERNBREAK;
                                                        break;
                                                case 0xfe:
                                                        note->note = NOTE_CUT;
                                                        note->instrument = 0;
                                                        break;
                                                }
                                        }
                                        switch (note->effect) {
                                        case 0:
                                                break;
                                        case 1: /* arpeggio */
                                                note->effect = FX_ARPEGGIO;
                                                break;
                                        case 2: /* pitch bend */
                                                if (note->param >> 4) {
                                                        note->effect = FX_PORTAMENTODOWN;
                                                        note->param >>= 4;
                                                } else if (note->param & 0xf) {
                                                        note->effect = FX_PORTAMENTOUP;
                                                        note->param &= 0xf;
                                                } else {
                                                        note->effect = 0;
                                                }
                                                break;
                                        case 5: /* volume up */
                                                note->effect = FX_VOLUMESLIDE;
                                                note->param = (note->param & 0xf) << 4;
                                                break;
                                        case 6: /* set volume */
                                                if (note->param > 64)
                                                        note->param = 64;
                                                note->voleffect = VOLFX_VOLUME;
                                                note->volparam = 64 - note->param;
                                                note->effect = 0;
                                                note->param = 0;
                                                break;
                                        case 3: /* LED on (wtf!) */
                                        case 4: /* LED off (ditto) */
                                        case 7: /* set step up */
                                        case 8: /* set step down */
                                        default:
                                                effwarn |= (1 << note->effect);
                                                note->effect = FX_UNIMPLEMENTED;
                                                break;
                                        }
                                }
                        }
                }
                for (n = 0; n < 16; n++) {
                        if (effwarn & (1 << n))
                                log_appendf(4, " Warning: Unimplemented effect %Xxx", n);
                }

                if (restart < npat)
                        csf_insert_restart_pos(song, restart);
        }

        /* sample data */
        if (!(lflags & LOAD_NOSAMPLES)) {
                for (n = 0, sample = song->samples + 1; n < fmt->nsmp; n++, sample++) {
                        uint32_t ssize;

                        if (sample->length <= 2)
                                continue;
                        ssize = csf_read_sample(sample, SF_8 | SF_LE | SF_PCMS | SF_M,
                                fp->data + fp->pos, fp->length - fp->pos);
                        slurp_seek(fp, ssize, SEEK_CUR);
                }
        }

        /* more header info */
        song->flags = SONG_ITOLDEFFECTS | SONG_COMPATGXX;
        for (n = 0; n < 4; n++)
                song->channels[n].panning = PROTRACKER_PANNING(n); /* ??? */
        for (; n < MAX_CHANNELS; n++)
                song->channels[n].flags = CHN_MUTE;

        strcpy(song->tracker_id, fmt->id);
        song->pan_separation = 64;

//      if (ferror(fp)) {
//              return LOAD_FILE_ERROR;
//      }

        /* done! */
        return LOAD_SUCCESS;
}


/*
most of modland's sfx files have all zeroes for those 14 "unknown" bytes, with the following exceptions:

64 00 00 00 00 00 00 00 00 00 00 00 00 00  d............. - unknown/antitrax.sfx
74 63 68 33 00 00 00 00 00 00 00 00 00 00  tch3.......... - unknown/axel f.sfx
61 6c 6b 00 00 00 00 00 00 00 00 00 00 00  alk........... Andreas Hommel/cyberblast-intro.sfx
21 00 00 00 00 00 00 00 00 00 00 00 00 00  !............. - unknown/dugger.sfx
00 00 00 00 00 0d 00 00 00 00 00 00 00 00  .............. Jean Baudlot/future wars - time travellers - dugger (title).sfx
00 00 00 00 00 00 00 00 0d 00 00 00 00 00  .............. Jean Baudlot/future wars - time travellers - escalator.sfx
6d 65 31 34 00 00 00 00 00 00 00 00 00 00  me14.......... - unknown/melodious.sfx
0d 0d 0d 53 46 58 56 31 2e 38 00 00 00 00  ...SFXV1.8.... AM-FM/sunday morning.sfx
61 6c 6b 00 00 00 00 00 00 00 00 00 00 00  alk........... - unknown/sunday morning.sfx
6f 67 00 00 00 00 00 00 00 00 00 00 00 00  og............ Philip Jespersen/supaplex.sfx
6e 74 20 73 6f 6e 67 00 00 00 00 00 00 00  nt song....... - unknown/sweety.sfx
61 6c 6b 00 00 00 00 00 00 00 00 00 00 00  alk........... - unknown/thrust.sfx
*/

