/*

 Name: LOAD_M15.C

 Description:
 15 instrument MOD loader

 Portability:
 All systems - all compilers (hopefully)

 If this module is found to not be portable to any particular platform,
 please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
 more information on contacting the author).

*/

#include <string.h>
#include "mikmod.h"

#ifdef __BEOS__
extern bool rawloader;
#endif

/*************************************************************************
*************************************************************************/


typedef struct MSAMPINFO       // sample header as it appears in a module
{   CHAR  samplename[22];
    UWORD length;
    UBYTE finetune;
    UBYTE volume;
    UWORD reppos;
    UWORD replen;
} MSAMPINFO;


typedef struct MODULEHEADER          // verbatim module header
{   CHAR       songname[20];         // the songname..
    MSAMPINFO  samples[15];          // all sampleinfo
    UBYTE      songlength;           // number of patterns used
    UBYTE      magic1;               // should be 127
    UBYTE      positions[128];       // which pattern to play at pos
} MODULEHEADER;


typedef struct MODNOTE
{   UBYTE a,b,c,d;
} MODNOTE;


/*************************************************************************
*************************************************************************/

static MODULEHEADER *mh = NULL;       // raw as-is module header
static MODNOTE *patbuf = NULL;


static BOOL LoadModuleHeader(MODULEHEADER *mh)
{
    int t;

    _mm_read_string(mh->songname,20,modfp);

    for(t=0; t<15; t++)
    {   MSAMPINFO *s= &mh->samples[t];
        _mm_read_string(s->samplename,22,modfp);
        s->length   =_mm_read_M_UWORD(modfp);
        s->finetune =_mm_read_UBYTE(modfp);
        s->volume   =_mm_read_UBYTE(modfp);
        s->reppos   =_mm_read_M_UWORD(modfp);
        s->replen   =_mm_read_M_UWORD(modfp);
    }

    mh->songlength  =_mm_read_UBYTE(modfp);
    mh->magic1      =_mm_read_UBYTE(modfp);               // should be 127
    _mm_read_UBYTES(mh->positions,128,modfp);

    return(!feof(modfp));
}



BOOL M15_Test(void)
{
    int t;
    MODULEHEADER mh;

#ifdef __BEOS__
	extern char *glblfn;	
	if(
		strncasecmp("mod.",glblfn,4)!=0 &&
		strncasecmp(".mod",&glblfn[strlen(glblfn)-4],4)!=0
	) return 0;
#endif

    if(!LoadModuleHeader(&mh)) return 0;

    for(t=0; t<15; t++)
    {   // all finetunes should be zero
        if(mh.samples[t].finetune!=0) return 0;
        // all volumes should be <=64
        if(mh.samples[t].volume>64) return 0;
    }
    if(mh.magic1>127) return 0;    // and magic1 should be <128

    return 1;
}


BOOL M15_Init(void)
{
    if(!(mh=(MODULEHEADER *)_mm_calloc(1,sizeof(MODULEHEADER)))) return 0;
    return 1;
}


void M15_Cleanup(void)
{
    if(mh!=NULL) free(mh);
    if(patbuf!=NULL) free(patbuf);

    mh = NULL;
    patbuf = NULL;
}


/*
Old (amiga) noteinfo:

 _____byte 1_____   byte2_    _____byte 3_____   byte4_
/                \ /      \  /                \ /      \
0000          0000-00000000  0000          0000-00000000

Upper four    12 bits for    Lower four    Effect command.
bits of sam-  note period.   bits of sam-
ple number.                  ple number.

*/


void M15_ConvertNote(MODNOTE *n)
{
    UBYTE instrument,effect,effdat,note;
    UWORD period;

    // extract the various information from the 4 bytes that
    // make up a single note

    instrument = (n->a&0x10)|(n->c>>4);
    period     = (((UWORD)n->a&0xf)<<8)+n->b;
    effect     = n->c&0xf;
    effdat     = n->d;

    // Convert the period to a note number

    note=0;
    if(period != 0)
    {   for(note=0; note<60; note++)
            if(period>=npertab[note]) break;
        note++;
        if(note==61) note=0;
    }

    if(instrument!=0) UniInstrument(instrument-1);
    if(note!=0) UniNote(note+23);

    // Convert pattern jump from Dec to Hex
    if(effect == 0xd)
        effdat = (((effdat&0xf0)>>4)*10)+(effdat&0xf);

    UniPTEffect(effect,effdat);
}



UBYTE *M15_ConvertTrack(MODNOTE *n)
{
    int t;

    UniReset();
    for(t=0;t<64;t++)
    {   M15_ConvertNote(n);
        UniNewline();
        n+=of.numchn;
    }
    return UniDup();
}



BOOL M15_LoadPatterns(void)
//  Loads all patterns of a modfile and converts them into the
//  3 byte format.
{
    int t,s,tracks=0;

    if(!AllocPatterns()) return 0;
    if(!AllocTracks()) return 0;

    // Allocate temporary buffer for loading
    // and converting the patterns

    if(!(patbuf=(MODNOTE *)_mm_calloc(64U*of.numchn,sizeof(MODNOTE)))) return 0;

    for(t=0;t<of.numpat;t++)
    {   // Load the pattern into the temp buffer
        // and convert it

        for(s=0;s<(64U*of.numchn);s++)
        {   patbuf[s].a=_mm_read_UBYTE(modfp);
            patbuf[s].b=_mm_read_UBYTE(modfp);
            patbuf[s].c=_mm_read_UBYTE(modfp);
            patbuf[s].d=_mm_read_UBYTE(modfp);
        }

        for(s=0;s<of.numchn;s++)
            if(!(of.tracks[tracks++]=M15_ConvertTrack(patbuf+s))) return 0;
    }

    return 1;
}



BOOL M15_Load(void)
{
    int t;
    INSTRUMENT *d;          // new sampleinfo structure
    SAMPLE *q;
    MSAMPINFO *s;           // old module sampleinfo

    // try to read module header

    if(!LoadModuleHeader(mh))
    {   _mm_error = MLERR_LOADING_HEADER;
        return 0;
    }

    // set module variables

    of.initspeed = 6;
    of.inittempo = 125;
    of.numchn    = 4;                           // get number of channels
    of.modtype   = strdup("15-instrument");     // get ascii type of mod
    of.songname  = DupStr(mh->songname,20);     // make a cstr of songname
    of.numpos    = mh->songlength;              // copy the songlength
    memcpy(of.positions,mh->positions,128);     // copy the position array

    // Count the number of patterns

    of.numpat = 0;

    for(t=0; t<128; t++)
    {   if(of.positions[t] > of.numpat)
            of.numpat=of.positions[t];
    }
    of.numpat++;
    of.numtrk=of.numpat*of.numchn;

    // Finally, init the sampleinfo structures

    of.numins=of.numsmp=15;
    if(!AllocInstruments()) return 0;
    if(!AllocSamples()) return 0;

    s = mh->samples;          // init source pointer
    d = of.instruments;       // init dest pointer
    q = of.samples;
    
    for(t=0; t<of.numins; t++)
    {   // convert the samplename
        d->insname    = DupStr(s->samplename,22);
        q->samplename = DupStr(s->samplename,22);

        // init the sampleinfo variables and
        // convert the size pointers to longword format

        q->c2spd     = finetune[s->finetune&0xf];
        q->volume    = s->volume;
        q->loopstart = s->reppos;
        q->loopend   = q->loopstart+(s->replen<<1);
        q->length    = s->length<<1;

        q->flags=SF_SIGNED;
        if(s->replen>1) q->flags|=SF_LOOP;

        // fix replen if repend>length

        if(q->loopend>q->length) q->loopend=q->length;

        s++;    // point to next source sampleinfo
        d++;    // point to next destiny sampleinfo
        q++;
    }

    if(!M15_LoadPatterns()) return 0;
    return 1;
}



MLOADER load_m15 =
{   NULL,
    "15-instrument module",
    "Portable MOD-15 loader v0.1",
    M15_Init,
    M15_Test,
    M15_Load,
    M15_Cleanup
};
