Mercurial > hg > dmlib
view jloadjss.c @ 510:43ea59887c69
Start work on making C64 formats encoding possible by changing DMDecodeOps
to DMEncDecOps and adding fields and op enums for custom encode functions, renaming,
etc. Split generic op sanity checking into a separate function in
preparation for its use in generic encoding function.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 19 Nov 2012 15:06:01 +0200 |
parents | cd57ba1130eb |
children |
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_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_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 #ifdef 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 #ifdef 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 #ifdef 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 #ifdef 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 #ifdef JM_SUP_PATMODE_5 #undef JSGETBYTE #define JSGETBYTE(XV) if (!jsGetBufByte(&buf, bufLeft, XV)) return DMERR_OUT_OF_DATA #define JSFOREACHNOTE1 \ for (channel = 0; channel < pattern->nchannels; channel++) \ for (row = 0; row < pattern->nrows; row++) { \ JSSNote *note = pattern->data + (pattern->nchannels * row) + channel; #define JSFOREACHNOTE2 } static int jssGetPatternRawVertElem(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern) { int row, channel; Uint8 tmp; assert(buf != NULL); assert(pattern != NULL); JSFOREACHNOTE1 JSGETBYTE(&tmp); if (tmp == 0) note->note = jsetNotSet; else if (tmp == 127) note->note = jsetNoteOff; else note->note = tmp - 1; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp; JSFOREACHNOTE2 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; }