view jloadjss.c @ 96:6bf5220fa47e

Urgh .. use memset to silence some bogus GCC warnings about using potentially uninitialized values, while that will not actually be possible. In any case, it is annoying.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 02 Oct 2012 18:52:28 +0300
parents 32250b436bca
children d3a9a3804079
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
    memset(&jssH, 0, sizeof(jssH));
    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
        memset(&jssP, 0, sizeof(jssP));
        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;

        memset(&jssE, 0, sizeof(jssE));
        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;

        memset(&jssI, 0, sizeof(jssI));
        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;
}