view jloadjss.c @ 0:32250b436bca

Initial re-import.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 28 Sep 2012 01:54:23 +0300
parents
children 6bf5220fa47e
line wrap: on
line source

/*
 * miniJSS - JSSMOD module loader
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2007-2009 Tecnic Software productions (TNSP)
 */
#include "jssmod.h"
#include <string.h>


#ifdef JM_SUP_PATMODE_ALL
#define JM_SUP_PATMODE_1 1
#define JM_SUP_PATMODE_2 1
#define JM_SUP_PATMODE_3 1
#define JM_SUP_PATMODE_4 1
#define JM_SUP_PATMODE_5 1
#endif


static BOOL jsGetBufData(Uint8 **buf, size_t *bufLeft, void *data, const size_t dataSize)
{
    if (*bufLeft >= dataSize)
    {
        memcpy(data, *buf, dataSize);
        *buf += dataSize;
        *bufLeft -= dataSize;
        return TRUE;
    }
    else
        return FALSE;
}


static BOOL jsGetBufByte(Uint8 **buf, size_t *bufLeft, Uint8 *data)
{
    if (*bufLeft > 0)
    {
        *data = **buf;
        (*buf)++;
        (*bufLeft)--;
        return TRUE;
    }
    else
        return FALSE;
}


#define JSGETBUF(XV, XT) if (!jsGetBufData(buf, bufLeft, XV, sizeof(XT))) return DMERR_OUT_OF_DATA
#define JSGETBYTE(XV) if (!jsGetBufByte(buf, bufLeft, XV)) return DMERR_OUT_OF_DATA


#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_1) || defined(JM_SUP_PATMODE_3)
static int jssGetConvertedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
{
    Uint8 tmp;

    JSGETBYTE(&tmp);

    if (tmp == 127)
        note->note = jsetNoteOff;
    else if (tmp == 0)
        note->note = jsetNotSet;
    else
        note->note = tmp - 1;

    JSGETBYTE(&tmp);
    note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;

    JSGETBYTE(&tmp);
    note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;

    JSGETBYTE(&tmp);
    note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;

    JSGETBYTE(&tmp);
    note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;

    return DMERR_OK;
}
#endif


#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_2) || defined(JM_SUP_PATMODE_4)
static int jssGetCompressedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
{
    Uint8 packb, tmp;

    JSGETBYTE(&packb);
    if (packb & 0x80)
    {
        if (packb & COMP_NOTE)
        {
            JSGETBYTE(&tmp);
            if (tmp == 127)
                note->note = jsetNoteOff;
            else
                note->note = tmp;
        }

        if (packb & COMP_INSTRUMENT)
        {
            JSGETBYTE(&tmp);
            note->instrument = tmp;
        }

        if (packb & COMP_VOLUME)
        {
            JSGETBYTE(&tmp);
            note->volume = tmp;
        }

        if (packb & COMP_EFFECT)
        {
            JSGETBYTE(&tmp);
            note->effect = tmp;
            note->param = 0;
        }

        if (packb & COMP_PARAM)
        {
            JSGETBYTE(&tmp);
            note->param = tmp;
        }
    }
    else
    {
        tmp = packb;

        if (tmp == 127)
            note->note = jsetNoteOff;
        else if (tmp == 0)
            note->note = jsetNotSet;
        else
            note->note = tmp - 1;

        JSGETBYTE(&tmp);
        note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;

        JSGETBYTE(&tmp);
        note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;

        JSGETBYTE(&tmp);
        note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;

        JSGETBYTE(&tmp);
        note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
    }

    return DMERR_OK;
}
#endif


#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_2)
static int jssGetPatternCompHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
{
    int row, channel;

    assert(buf != NULL);
    assert(pattern != NULL);

    for (row = 0; row < pattern->nrows; row++)
    for (channel = 0; channel < pattern->nchannels; channel++)
    {
        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
        int res = jssGetCompressedNote(&buf, bufLeft, note);
        if (res != DMERR_OK)
            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
    }

    return DMERR_OK;
}
#endif


#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_4)
static int jssGetPatternCompVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
{
    int row, channel;

    assert(buf != NULL);
    assert(pattern != NULL);

    for (channel = 0; channel < pattern->nchannels; channel++)
    for (row = 0; row < pattern->nrows; row++)
    {
        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
        int res = jssGetCompressedNote(&buf, bufLeft, note);
        if (res != DMERR_OK)
            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
    }

    return DMERR_OK;
}
#endif


#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_1)
static int jssGetPatternRawHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
{
    int row, channel;

    assert(buf != NULL);
    assert(pattern != NULL);

    for (row = 0; row < pattern->nrows; row++)
    for (channel = 0; channel < pattern->nchannels; channel++)
    {
        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
        int res = jssGetConvertedNote(&buf, bufLeft, note);
        if (res != DMERR_OK)
            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
    }

    return DMERR_OK;
}
#endif


#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_3)
static int jssGetPatternRawVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
{
    int row, channel;

    assert(buf != NULL);
    assert(pattern != NULL);

    for (channel = 0; channel < pattern->nchannels; channel++)
    for (row = 0; row < pattern->nrows; row++)
    {
        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
        int res = jssGetConvertedNote(&buf, bufLeft, note);
        if (res != DMERR_OK)
            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
    }

    return DMERR_OK;
}
#endif


#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_5)
static int jssGetPatternRawVertElem(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
{
    int row, channel;

    assert(buf != NULL);
    assert(pattern != NULL);

    return DMERR_OK;
}
#endif


#undef JSGETBUF
#undef JSGETBYTE
#define JSGETBUF(XV, XT) do { \
    if (!jsGetBufData(&buf, &bufLeft, XV, sizeof(XT)))  \
        JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,    \
        "Out of data at getting " # XT " (%d bytes)\n", sizeof(XT)); \
} while (0)
#define JSGETBYTE(XV) if (!jsGetBufByte(&buf, &bufLeft, XV)) return DMERR_OUT_OF_DATA


#ifdef JM_SUP_EXT_INSTR
static void jssCopyEnvelope(JSSEnvelope *e, JSSMODEnvelope *je)
{
    int i;

    e->flags   = je->flags;
    e->npoints = je->npoints;
    e->sustain = je->sustain;
    e->loopS   = je->loopS;
    e->loopE   = je->loopE;

    for (i = 0; i < je->npoints; i++)
    {
        e->points[i].frame = je->points[i].frame;
        e->points[i].value = je->points[i].value;
    }
}
#endif


int jssLoadJSSMOD(Uint8 *bufStart, const size_t bufSize, JSSModule **ppModule)
{
    JSSModule *module;
    JSSMODHeader jssH;
    Uint8 *buf = bufStart;
    size_t bufLeft = bufSize;
    int index;

    assert(ppModule != NULL);
    assert(bufStart != NULL);
    *ppModule = NULL;

    // Check the JSSMOD header
    JSGETBUF(&jssH, JSSMODHeader);

    if (memcmp(jssH.idMagic, "JM", 2) != 0)
    {
        JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
        "Not a valid JSSMOD file, header signature missing!\n");
    }

    if (jssH.idVersion != JSSMOD_VERSION)
    {
        JSSERROR(DMERR_VERSION, DMERR_VERSION,
        "Unsupported version of JSSMOD 0x%4x, this version only supports 0x%4x!\n",
        jssH.idVersion, JSSMOD_VERSION);
    }

    // Allocate the module
    module = jssAllocateModule();
    if (module == NULL)
    {
        JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
        "Could not allocate memory for module structure.\n");
    }
    *ppModule = module;

    // Copy header information
    module->norders         = jssH.norders;
    module->npatterns       = jssH.npatterns;
    module->nchannels       = jssH.nchannels;
    module->nextInstruments = jssH.nextInstruments;
    module->ninstruments    = jssH.ninstruments;
    module->defFlags        = jssH.defFlags;
    module->intVersion      = jssH.intVersion;
    module->defRestartPos   = jssH.defRestartPos;
    module->defSpeed        = jssH.defSpeed;
    module->defTempo        = jssH.defTempo;

    // Get the orders list
    for (index = 0; index < module->norders; index++)
    {
        Sint16 order;
        JSGETBUF(&order, Sint16);
        module->orderList[index] = order;
    }

    // Parse the patterns
    for (index = 0; index < module->npatterns; index++)
    {
        JSSMODPattern jssP;
        int result = DMERR_INVALID_DATA;
        size_t bufSize;

        // Get header and check size
        JSGETBUF(&jssP, JSSMODPattern);
        bufSize = jssP.size;
        if (bufLeft < jssP.size)
        {
            JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
            "Out of data for pattern #%d.\n", index);
        }

        // Allocate pattern
        module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels);
        if (module->patterns[index] == NULL)
        {
            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
            "Could not allocate memory for pattern #%d.\n", index);
        }

        // Get pattern data
        switch (jssH.patMode)
        {
#ifdef JM_SUP_PATMODE_1
            case PATMODE_RAW_HORIZ:
                result = jssGetPatternRawHoriz(buf, &bufSize, module->patterns[index]);
                break;
#endif
#ifdef JM_SUP_PATMODE_2
            case PATMODE_COMP_HORIZ:
                result = jssGetPatternCompHoriz(buf, &bufSize, module->patterns[index]);
                break;
#endif
#ifdef JM_SUP_PATMODE_3
            case PATMODE_RAW_VERT:
                result = jssGetPatternRawVert(buf, &bufSize, module->patterns[index]);
                break;
#endif
#ifdef JM_SUP_PATMODE_4
            case PATMODE_COMP_VERT:
                result = jssGetPatternCompVert(buf, &bufSize, module->patterns[index]);
                break;
#endif
#ifdef JM_SUP_PATMODE_5
            case PATMODE_RAW_ELEM:
                result = jssGetPatternRawVertElem(buf, &bufSize, module->patterns[index]);
                break;
#endif
            default:
                JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
                "Unsupported pattern mode %d. Check compilation options.", jssH.patMode);
                break;
        }

        if (bufSize > 0)
        {
            JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA,
            "Unparsed data after pattern (%d bytes), possibly broken file.\n", bufSize);
        }

        if (result != DMERR_OK)
        {
            JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index);
        }

        buf += jssP.size;
        bufLeft -= jssP.size;
    }

#ifdef JM_SUP_EXT_INSTR
    // Read extended instruments
    for (index = 0; index < module->nextInstruments; index++)
    {
        JSSMODExtInstrument jssE;
        JSSExtInstrument *einst;
        int i;

        JSGETBUF(&jssE, JSSMODExtInstrument);

        if ((einst = jssAllocateExtInstrument()) == NULL)
        {
            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
            "Could not allocate extended instrument structure #%i\n", index);
        }

        module->extInstruments[index] = einst;

        einst->nsamples     = jssE.nsamples;
        einst->vibratoType  = jssE.vibratoType;
        einst->vibratoSweep = jssE.vibratoSweep;
        einst->vibratoDepth = jssE.vibratoDepth;
        einst->vibratoRate  = jssE.vibratoRate;
        einst->fadeOut      = jssE.fadeOut;

        for (i = 0; i < jsetNNotes; i++)
        {
            int snum = jssE.sNumForNotes[i];
            einst->sNumForNotes[i] = (snum > 0) ? snum : jsetNotSet;
        }

        jssCopyEnvelope(&(einst->volumeEnv), &jssE.volumeEnv);
        jssCopyEnvelope(&(einst->panningEnv), &jssE.panningEnv);
    }

#ifdef JM_SUP_INSTR
    // Read sample instrument headers
    for (index = 0; index < module->ninstruments; index++)
    {
        JSSMODInstrument jssI;
        JSSInstrument *inst;

        JSGETBUF(&jssI, JSSMODInstrument);

        if ((inst = jssAllocateInstrument()) == NULL)
        {
            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
            "Could not allocate instrument structure #%i\n", index);
        }

        module->instruments[index] = inst;

        inst->size          = jssI.size;
        inst->loopS         = jssI.loopS;
        inst->loopE         = jssI.loopE;
        inst->volume        = jssI.volume;
        inst->flags         = jssI.flags;
        inst->C4BaseSpeed   = jssI.C4BaseSpeed;
        inst->ERelNote      = jssI.ERelNote;
        inst->EFineTune     = jssI.EFineTune;
        inst->EPanning      = jssI.EPanning;
        inst->hasData       = jssI.hasData;
        inst->convFlags     = jssI.convFlags;
    }

#ifdef JM_SUP_SAMPLES
    // Read sample data
    for (index = 0; index < module->ninstruments; index++)
    {
        JSSInstrument *inst = module->instruments[index];

        if (inst && inst->hasData)
        {
            size_t sz;

            // Calculate data size
            if (inst->flags & jsf16bit)
                sz = inst->size * sizeof(Uint16);
            else
                sz = inst->size * sizeof(Uint8);

            // Check if we can get as much?
            if (bufLeft < sz)
            {
                JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
                "Out of data for instrument sample #%d (%d < %d)\n",
                index, bufLeft, sz);
            }

            // Allocate
            if ((inst->data = dmMalloc(sz)) == NULL)
            {
                JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
                "Could not allocate sample data #%d\n", index);
            }

            // Copy data
            memcpy(inst->data, buf, sz);
            buf += sz;
            bufLeft -= sz;

            // Convert, if needed
            if (inst->flags & jsf16bit)
                jssDecodeSample16(inst->data, inst->size, inst->convFlags);
            else
                jssDecodeSample8(inst->data, inst->size, inst->convFlags);
        }
    }
#else
#warning Not including JSSMOD sample loading!
#endif  // JM_SUP_SAMPLES
#else
#warning Not including JSSMOD instrument loading!
#endif  // JM_SUP_INSTR
#else
#warning Not including JSSMOD ext.instrument loading!
#endif  // JM_SUP_EXT_INSTR

    return DMERR_OK;
}