Mercurial > hg > dmlib
view minijss/jloadjss.c @ 1196:aa6c3af36008
Cosmetics.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 05 Mar 2015 15:58:52 +0200 |
parents | 848a88ce7a57 |
children | 0ee6ba7b3e4a |
line wrap: on
line source
/* * miniJSS - JSSMOD module loader * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2007-2015 Tecnic Software productions (TNSP) */ #include "jssmod.h" // If all pattern modes are required, enable them here #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 // Short helper macros for reading data #define JSGETBUF(XV, XT) \ if (!dmf_read_str(inFile, XV, sizeof(XT))) \ return DMERR_OUT_OF_DATA #define JSGETBYTE(XV) \ if (!dmf_read_byte(inFile, XV)) \ return DMERR_OUT_OF_DATA #if defined(JM_SUP_PATMODE_1) || defined(JM_SUP_PATMODE_3) static int jssGetConvertedNote(DMResource *inFile, 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(DMResource *inFile, 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(DMResource *inFile, 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(inFile, 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(DMResource *inFile, 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(inFile, 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(DMResource *inFile, 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(inFile, 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(DMResource *inFile, 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(inFile, 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 (!dmf_read_byte(inFile, 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(DMResource *inFile, 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 (!dmf_read_str(inFile, 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 (!dmf_read_byte(inFile, 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(DMResource *inFile, JSSModule **ppModule, BOOL probe) { JSSModule *module; JSSMODHeader jssH; int index; *ppModule = NULL; // Check the JSSMOD header dmMemset(&jssH, 0, sizeof(jssH)); JSGETBUF(&jssH, JSSMODHeader); if (memcmp(jssH.idMagic, "JM", 2) != 0) { if (probe) return DMERR_NOT_SUPPORTED; else JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED, "Not a JSSMOD file, header signature mismatch!\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; // Get header and check size dmMemset(&jssP, 0, sizeof(jssP)); JSGETBUF(&jssP, JSSMODPattern); // 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(inFile, module->patterns[index]); break; #endif #ifdef JM_SUP_PATMODE_2 case PATMODE_COMP_HORIZ: result = jssGetPatternCompHoriz(inFile, module->patterns[index]); break; #endif #ifdef JM_SUP_PATMODE_3 case PATMODE_RAW_VERT: result = jssGetPatternRawVert(inFile, module->patterns[index]); break; #endif #ifdef JM_SUP_PATMODE_4 case PATMODE_COMP_VERT: result = jssGetPatternCompVert(inFile, module->patterns[index]); break; #endif #ifdef JM_SUP_PATMODE_5 case PATMODE_RAW_ELEM: result = jssGetPatternRawVertElem(inFile, module->patterns[index]); break; #endif default: JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Unsupported pattern mode %d. Check compilation options.", jssH.patMode); break; } if (result != DMERR_OK) { JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index); } } #ifdef JM_SUP_EXT_INSTR // Read extended instruments for (index = 0; index < module->nextInstruments; index++) { JSSMODExtInstrument jssE; JSSExtInstrument *einst; int i; dmMemset(&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 - 1 : 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; dmMemset(&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) { int ret; size_t sz; // Calculate data size if (inst->flags & jsf16bit) sz = inst->size * sizeof(Uint16); else sz = inst->size * sizeof(Uint8); // Allocate if ((inst->data = dmMalloc(sz)) == NULL) { JSSERROR(DMERR_MALLOC, DMERR_MALLOC, "Could not allocate sample data #%d\n", index); } // Copy data if (!dmf_read_str(inFile, inst->data, sz)) { JSSERROR(DMERR_FREAD, DMERR_FREAD, "Could not read sample data for #%d\n", index); } // Convert, if needed if (inst->flags & jsf16bit) ret = jssDecodeSample16(inst->data, inst->size, inst->convFlags); else ret = jssDecodeSample8(inst->data, inst->size, inst->convFlags); if (ret != DMERR_OK) { JSSERROR(ret, ret, "Failed to decode sample data for #%d\n", index); } } } #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; }