diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jloadjss.c	Fri Sep 28 01:54:23 2012 +0300
@@ -0,0 +1,533 @@
+/*
+ * 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;
+}