Mercurial > hg > dmlib
view jloadjss.c @ 59:c560703e85ed
Add resource writing functions (only work for stdio backend)
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 01 Oct 2012 07:51:08 +0300 |
parents | 32250b436bca |
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; }