Mercurial > hg > dmlib
view minijss/jloadjss.c @ 1303:be30466fbc47
Cleanups.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 19 Aug 2017 11:54:20 +0300 |
parents | 197c9ccdb33c |
children | dce4730372c7 |
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 JSGETBYTE(XV) \ if (!dmf_read_byte(inFile, XV)) \ return DMERR_OUT_OF_DATA static int jssDoGetConvertedNote(DMResource *inFile, JSSNote *pnote, Uint8 note) { Uint8 tmp; if (note == 127) pnote->note = jsetNoteOff; else if (note == 0) pnote->note = jsetNotSet; else pnote->note = note - 1; JSGETBYTE(&tmp); pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet; JSGETBYTE(&tmp); pnote->volume = (tmp > 0) ? tmp - 1 : jsetNotSet; JSGETBYTE(&tmp); pnote->effect = (tmp > 0) ? tmp - 1 : jsetNotSet; JSGETBYTE(&tmp); pnote->param = (tmp == 0 && pnote->effect == jsetNotSet) ? jsetNotSet : tmp; return DMERR_OK; } static inline int jssGetConvertedNote(DMResource *inFile, JSSNote *pnote) { Uint8 tmp; JSGETBYTE(&tmp); return jssDoGetConvertedNote(inFile, pnote, tmp); } #if defined(JM_SUP_PATMODE_2) || defined(JM_SUP_PATMODE_4) static int jssGetCompressedNote(DMResource *inFile, JSSNote *pnote) { Uint8 packb, tmp; JSGETBYTE(&packb); if (packb & 0x80) { if (packb & JM_COMP_NOTE) { JSGETBYTE(&tmp); if (tmp == 127) pnote->note = jsetNoteOff; else pnote->note = tmp; } if (packb & JM_COMP_INSTRUMENT) { JSGETBYTE(&tmp); pnote->instrument = tmp; } if (packb & JM_COMP_VOLUME) { JSGETBYTE(&tmp); pnote->volume = tmp; } if (packb & JM_COMP_EFFECT) { JSGETBYTE(&tmp); pnote->effect = tmp; pnote->param = 0; } if (packb & JM_COMP_PARAM) { JSGETBYTE(&tmp); pnote->param = tmp; } } else { int ret; if ((ret = jssDoGetConvertedNote(inFile, pnote, packb)) != DMERR_OK) return ret; } 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 *pnote = &pattern->data[(pattern->nchannels * row) + channel]; int res = jssGetCompressedNote(inFile, pnote); 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 *pnote = &pattern->data[(pattern->nchannels * row) + channel]; int res = jssGetCompressedNote(inFile, pnote); 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 *pnote = &pattern->data[(pattern->nchannels * row) + channel]; int res = jssGetConvertedNote(inFile, pnote); 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 *pnote = &pattern->data[(pattern->nchannels * row) + channel]; int res = jssGetConvertedNote(inFile, pnote); 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 *pnote = 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) pnote->note = jsetNotSet; else if (tmp == 127) pnote->note = jsetNoteOff; else pnote->note = tmp - 1; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); pnote->volume = (tmp > 0) ? tmp - 1 : jsetNotSet; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); pnote->effect = (tmp > 0) ? tmp - 1 : jsetNotSet; JSFOREACHNOTE2 JSFOREACHNOTE1 JSGETBYTE(&tmp); pnote->param = (tmp == 0 && pnote->effect == jsetNotSet) ? jsetNotSet : tmp; JSFOREACHNOTE2 return DMERR_OK; } #endif #ifdef JM_SUP_EXT_INSTR static int jssMODLoadEnvelope(DMResource *inFile, JSSEnvelope *env, const char *name, const int ninst) { JSSMODEnvelope jssEnv; int i; // Read envelope data if (!dmf_read_byte(inFile, &jssEnv.flags) || !dmf_read_byte(inFile, &jssEnv.npoints) || !dmf_read_byte(inFile, &jssEnv.sustain) || !dmf_read_byte(inFile, &jssEnv.loopS) || !dmf_read_byte(inFile, &jssEnv.loopE)) JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read %s-envelope data for instrument #%d.\n", name, ninst); // Do some sanity checking if (jssEnv.npoints > jsetMaxEnvPoints || jssEnv.loopS < jssEnv.loopE || jssEnv.loopS > jssEnv.npoints || jssEnv.loopE > jssEnv.npoints) JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Invalid values in %s-envelope for instrument #%d.\n", name, ninst); // Copy data env->flags = jssEnv.flags; env->npoints = jssEnv.npoints; env->sustain = jssEnv.sustain; env->loopS = jssEnv.loopS; env->loopE = jssEnv.loopE; for (i = 0; i < jssEnv.npoints; i++) { Uint16 frame, value; if (!dmf_read_le16(inFile, &frame) || !dmf_read_le16(inFile, &value)) JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read %s-envelope values (%d) for instrument #%d.\n", name, i, ninst); env->points[i].frame = frame; env->points[i].value = value; } return DMERR_OK; } #endif int jssLoadJSSMOD(DMResource *inFile, JSSModule **pmodule, BOOL probe) { JSSModule *module; JSSMODHeader jssH; int index, ret = DMERR_OK; *pmodule = NULL; // Check the JSSMOD header if (!dmf_read_str(inFile, &jssH.idMagic, sizeof(jssH.idMagic)) || !dmf_read_byte(inFile, &jssH.idVersion)) JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read JSSMOD header #1.\n"); 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) { if (probe) return DMERR_VERSION; else JSSERROR(DMERR_VERSION, DMERR_VERSION, "Unsupported version of JSSMOD 0x%2x, this version only supports 0x%2x!\n", jssH.idVersion, JSSMOD_VERSION); } // Read rest of the header if (!dmf_read_le16(inFile, &jssH.defFlags) || !dmf_read_le16(inFile, &jssH.intVersion) || !dmf_read_le16(inFile, &jssH.norders) || !dmf_read_le16(inFile, &jssH.npatterns) || !dmf_read_le16(inFile, &jssH.nextInstruments) || !dmf_read_le16(inFile, &jssH.ninstruments) || !dmf_read_le16(inFile, &jssH.defRestartPos) || !dmf_read_byte(inFile, &jssH.nchannels) || !dmf_read_byte(inFile, &jssH.defSpeed) || !dmf_read_byte(inFile, &jssH.defTempo) || !dmf_read_byte(inFile, &jssH.patMode)) JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read JSSMOD header #2.\n"); if (probe) return DMERR_OK; // Check some of the things for sanity if (jssH.nchannels > jsetMaxChannels || jssH.nchannels == 0 || jssH.norders > jsetMaxOrders || jssH.npatterns > jsetMaxPatterns || jssH.nextInstruments > jsetMaxInstruments || jssH.ninstruments > jsetMaxInstruments) { JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Invalid values in JSSMOD header.\n"); } // Allocate the module if ((*pmodule = module = jssAllocateModule()) == NULL) { JSSERROR(DMERR_MALLOC, DMERR_MALLOC, "Could not allocate memory for module structure.\n"); } // Copy header information module->defFlags = jssH.defFlags; module->intVersion = jssH.intVersion; module->norders = jssH.norders; module->npatterns = jssH.npatterns; module->nchannels = jssH.nchannels; module->nextInstruments = jssH.nextInstruments; module->ninstruments = jssH.ninstruments; module->defRestartPos = jssH.defRestartPos; module->defSpeed = jssH.defSpeed; module->defTempo = jssH.defTempo; // Get the orders list for (index = 0; index < module->norders; index++) { Uint16 tmp; if (!dmf_read_le16(inFile, &tmp)) JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read orders list entry #%d.\n", index); if (tmp != 0xffff && tmp > jssH.npatterns) JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Invalid orders list entry #%d value %d > %d.\n", index, tmp, jssH.npatterns); } // Parse the patterns for (index = 0; index < module->npatterns; index++) { JSSPattern *pattern; JSSMODPattern jssP; // Read pattern header if (!dmf_read_le32(inFile, &jssP.size) || !dmf_read_le16(inFile, &jssP.nrows)) JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read JSSMOD pattern header #%d.\n", index); // Validate if (jssP.nrows == 0 || jssP.nrows > jsetMaxRows) JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Invalid number of rows in pattern #%d: %d.\n", index, jssP.nrows); // Allocate pattern pattern = module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels); if (pattern == 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: ret = jssGetPatternRawHoriz(inFile, pattern); break; #endif #ifdef JM_SUP_PATMODE_2 case PATMODE_COMP_HORIZ: ret = jssGetPatternCompHoriz(inFile, pattern); break; #endif #ifdef JM_SUP_PATMODE_3 case PATMODE_RAW_VERT: ret = jssGetPatternRawVert(inFile, pattern); break; #endif #ifdef JM_SUP_PATMODE_4 case PATMODE_COMP_VERT: ret = jssGetPatternCompVert(inFile, pattern); break; #endif #ifdef JM_SUP_PATMODE_5 case PATMODE_RAW_ELEM: ret = jssGetPatternRawVertElem(inFile, pattern); break; #endif default: JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Unsupported pattern mode %d. Check compilation options.", jssH.patMode); break; } if (ret != DMERR_OK) { JSSERROR(ret, ret, "Error in unpacking pattern #%d data.\n", index); } } #ifdef JM_SUP_EXT_INSTR // Read extended instruments for (index = 0; index < module->nextInstruments; index++) { JSSMODExtInstrument jssE; JSSExtInstrument *einst; int i; // Read header data if (!dmf_read_byte(inFile, &jssE.nsamples) || !dmf_read_byte(inFile, &jssE.vibratoType) || !dmf_read_le16(inFile, &jssE.vibratoSweep) || !dmf_read_le16(inFile, &jssE.vibratoDepth) || !dmf_read_le16(inFile, &jssE.vibratoRate) || !dmf_read_le16(inFile, &jssE.fadeOut)) { JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read ext.instrument #%d header.\n", index); } // Allocate instrument if ((einst = jssAllocateExtInstrument()) == NULL) { JSSERROR(DMERR_MALLOC, DMERR_MALLOC, "Could not allocate extended instrument structure #%d\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; // Read and somewhat validate sNumForNotes for (i = 0; i < jsetNNotes; i++) { int snum; Uint32 tmp; if (!dmf_read_le32(inFile, &tmp)) { JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read ext.instrument #%d sNumForNotes[%d].\n", index, i); } einst->sNumForNotes[i] = snum = (tmp > 0) ? ((int) tmp - 1) : jsetNotSet; if (snum != jsetNotSet && snum > module->ninstruments) { JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Ext.instrument #%d has invalid sNumForNotes[%d] = %d > %d max.\n", index, i, snum, module->ninstruments); } } // Read and validate envelopes if ((ret = jssMODLoadEnvelope(inFile, &einst->volumeEnv, "volume", index)) != DMERR_OK || (ret = jssMODLoadEnvelope(inFile, &einst->panningEnv, "panning", index)) != DMERR_OK) return ret; } #ifdef JM_SUP_INSTR // Read sample instrument headers for (index = 0; index < module->ninstruments; index++) { JSSMODInstrument jssI; JSSInstrument *inst; // Read header data if (!dmf_read_le32(inFile, &jssI.size) || !dmf_read_le32(inFile, &jssI.loopS) || !dmf_read_le32(inFile, &jssI.loopE) || !dmf_read_le16(inFile, &jssI.flags) || !dmf_read_le16(inFile, &jssI.C4BaseSpeed) || !dmf_read_le16(inFile, &jssI.ERelNote) || !dmf_read_le16(inFile, &jssI.EFineTune) || !dmf_read_le16(inFile, &jssI.EPanning) || !dmf_read_byte(inFile, &jssI.volume) || !dmf_read_byte(inFile, &jssI.convFlags)) { JSSERROR(DMERR_FREAD, DMERR_FREAD, "Failed to read sample instrument #%d header.\n", index); } // Validate what we can if (jssI.loopS > jssI.size || jssI.loopE > jssI.size || jssI.loopE > jssI.loopS) { JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, "Invalid or corrupted sample instrument #%d.\n", index); } // Allocate instrument // Copy data inst->size = jssI.size; inst->loopS = jssI.loopS; inst->loopE = jssI.loopE; inst->flags = jssI.flags; inst->C4BaseSpeed = jssI.C4BaseSpeed; inst->ERelNote = jssI.ERelNote; inst->EFineTune = jssI.EFineTune; inst->EPanning = jssI.EPanning; inst->volume = jssI.volume; 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 != NULL && inst->convFlags & jsampHasData) { int ret; size_t bsize = (inst->flags & jsf16bit) ? sizeof(Uint16) : sizeof(Uint8); bsize *= inst->size; // Allocate if ((inst->data = dmMalloc(bsize)) == NULL) { JSSERROR(DMERR_MALLOC, DMERR_MALLOC, "Could not allocate %d bytes of sample data #%d\n", bsize, index); } // Copy data if (!dmf_read_str(inFile, inst->data, bsize)) { JSSERROR(DMERR_FREAD, DMERR_FREAD, "Could not read %d bytes of sample data for #%d\n", bsize, 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; }