/* S3MLOAD.C * * Scream Tracker 3 Module loader * * Copyright 1995 Petteri Kangaslampi and Jarno Paananen * * This file is part of the MIDAS Sound System, and may only be * used, modified and distributed under the terms of the MIDAS * Sound System license, LICENSE.TXT. By continuing to use, * modify or distribute this file you indicate that you have * read the license and understand and accept it fully. */ #include "lang.h" #include "mtypes.h" #include "errors.h" #include "mglobals.h" #include "mmem.h" #include "file.h" #include "sdevice.h" #include "mplayer.h" #include "s3m.h" #ifndef NOEMS #include "ems.h" #endif #include "vu.h" #include "mutils.h" #ifndef NULL #define NULL 0L #endif /* Size of temporary memory area used for avoiding memory fragmentation if EMS is used or in protected mode */ #define TEMPSIZE 8192 /* Pass error code in variable "error" on, used in s3mLoadModule(). */ #define S3MLOADPASSERROR { s3mLoadError(SD); PASSERROR(ID_s3mLoadModule) } /****************************************************************************\ * Module loader buffers and file pointer. These variables are static * instead of local so that a separate deallocation can be used which * will be called before exiting in error situations \****************************************************************************/ static fileHandle f; static int fileOpened; static mpModule *ms3m; static ushort *instPtrs; static ushort *pattPtrs; static uchar *smpBuf; static void *tempmem; static uchar *panningInfos; /****************************************************************************\ * * Function: int s3mFreeModule(mpModule *module, SoundDevice *SD); * * Description: Deallocates a Scream Tracker 3 module * * Input: mpModule *module module to be deallocated * SoundDevice *SD Sound Device that has stored the * samples * * Returns: MIDAS error code * \****************************************************************************/ int CALLING s3mFreeModule(mpModule *module, SoundDevice *SD) { int i, error; if ( module == NULL ) /* valid module? */ { ERROR(errUndefined, ID_s3mFreeModule); return errUndefined; } /* deallocate pattern orders if allocated: */ if ( module->orders != NULL ) if ( (error = memFree(module->orders)) != OK ) PASSERROR(ID_s3mFreeModule) /* deallocate sample used flags: */ if ( module->instsUsed != NULL ) if ( (error = memFree(module->instsUsed)) != OK ) PASSERROR(ID_s3mFreeModule) if ( module->insts != NULL ) /* instruments? */ { for ( i = 0; i < module->numInsts; i++ ) { /* If the instrument has been added to Sound Device, remove it, otherwise just deallocate the sample if allocated */ if ( (module->insts[i].sdInstHandle != 0) && (SD != NULL) ) { if ( (error = SD->RemInstrument( module->insts[i].sdInstHandle)) != OK ) PASSERROR(ID_s3mFreeModule) } else if ( module->insts[i].sample != NULL ) if ( (error = memFree(module->insts[i].sample)) != OK ) PASSERROR(ID_s3mFreeModule) } /* deallocate instrument structures: */ if ( (error = memFree(module->insts)) != OK ) PASSERROR(ID_s3mFreeModule) } if ( module->patterns != NULL ) { for ( i = 0; i < module->numPatts; i++ ) { /* if the pattern has been allocate, deallocate it - either from conventional memory or from EMS */ if ( module->patterns[i] != NULL ) { #ifndef NOEMS if ( useEMS == 1 ) { if ( (error = emsFree((emsBlock*) module->patterns[i])) != OK ) PASSERROR(ID_s3mFreeModule) } else #endif if ( (error = memFree(module->patterns[i])) != OK ) PASSERROR(ID_s3mFreeModule) } } /* deallocate pattern pointers: */ if ( (error = memFree(module->patterns)) != OK ) PASSERROR(ID_s3mFreeModule) } /* deallocate the module: */ if ( (error = memFree(module)) != OK) PASSERROR(ID_s3mFreeModule) return OK; } /****************************************************************************\ * * Function: void s3mLoadError(SoundDevice *SD) * * Description: Stops loading the module, deallocates all buffers and closes * the file. * * Input: SoundDevice *SD Sound Device that has been used for * loading. * \****************************************************************************/ static void s3mLoadError(SoundDevice *SD) { /* Close file if opened. Do not process errors. */ if ( fileOpened ) if ( fileClose(f) != OK ) return; /* Attempt to deallocate module if allocated. Do not process errors. */ if ( ms3m != NULL ) if ( s3mFreeModule(ms3m, SD) != OK ) return; /* Deallocate buffers if allocated. Do not process errors. */ if ( panningInfos != NULL ) if ( memFree(panningInfos) != OK ) return; if ( smpBuf != NULL ) if ( memFree(smpBuf) != OK ) return; if ( tempmem != NULL ) if ( memFree(tempmem) != OK ) return; if ( instPtrs != NULL ) if ( memFree(instPtrs) != OK ) return; if ( pattPtrs != NULL ) if ( memFree(pattPtrs) != OK ) return; } /****************************************************************************\ * * Function: int s3mLoadModule(char *fileName, SoundDevice *SD, * int (*SaveSampleInfo)(ushort sdInstHandle, uchar *sample, * ushort slength, ushort loopStart, ushort loopEnd), * mpModule **module); * * Description: Loads a Scream Tracker 3 module into memory * * Input: char *fileName name of module file to be loaded * SoundDevice *SD Sound Device which will store the * samples. NULL if the samples should * not be added to a Sound Device, but * should be left in conventional memory * instead. * int (*SaveSampleInfo)() Pointer to sample information saving * function. sdInstHandle = Sound Device * instrument handle, sample = pointer to * sample data, slength = sample length, * loopStart = sample loop start, * loopEnd = sample loop end. The * function must return a MIDAS error * code. NULL if no such function is * used. * mpModule **module pointer to variable which will store * the module pointer. * * Returns: MIDAS error code. * Pointer to module structure is stored in *module. * * Notes: The only practical use at this point for SaveSampleInfo() are * the real VU-meters. To load a module and add the prepare the * VU meter information point SaveSampleInfo to vuPrepare(). * \****************************************************************************/ int CALLING s3mLoadModule(char *fileName, SoundDevice *SD, int CALLING (*SaveSampleInfo)(ushort sdInstHandle, uchar *sample, ushort slength, ushort loopStart, ushort loopEnd), mpModule **module) { s3mHeader s3mh; s3mInstHdr s3mi; int i; mpInstrument *inst; ushort pattSize; mpPattern *pattData; ushort lend; ulong maxSmpLength; int error; unsigned ordersize; void *p; /* point buffers to NULL and set fileOpened to 0 so that s3mLoadError() can be called at any point: */ fileOpened = 0; ms3m = NULL; instPtrs = NULL; pattPtrs = NULL; smpBuf = NULL; tempmem = NULL; panningInfos = NULL; /* Open module file: */ if ( (error = fileOpen(fileName, fileOpenRead, &f)) != OK ) S3MLOADPASSERROR /* Allocate memory for the module structure: */ if ( (error = memAlloc(sizeof(mpModule), (void**) &ms3m)) != OK ) S3MLOADPASSERROR ms3m->orders = NULL; /* clear module structure so that */ ms3m->insts = NULL; /* it can be deallocated with */ ms3m->patterns = NULL; /* s3mFree() at any point */ ms3m->instsUsed = NULL; ms3m->MP = &mpS3M; /* point MP field to module player */ /* Read .S3M file header: */ if ( (error = fileRead(f, &s3mh, sizeof(s3mHeader))) != OK ) S3MLOADPASSERROR /* Check the "SCRM" signature in header: */ if ( !mMemEqual(&s3mh.SCRM[0], "SCRM", 4) ) { ERROR(errInvalidModule, ID_s3mLoadModule); s3mLoadError(SD); return errInvalidModule; } mMemCopy(&ms3m->ID[0], &s3mh.SCRM[0], 4); /* copy ID */ ms3m->IDnum = idS3M; /* S3M module ID */ mMemCopy(&ms3m->songName[0], &s3mh.name[0], 28); /* copy song name */ ms3m->songLength = s3mh.songLength; /* copy song length */ ms3m->numInsts = s3mh.numInsts; /* copy number of instruments */ ms3m->numPatts = s3mh.numPatts; /* copy number of patterns */ mMemCopy(&ms3m->flags, &s3mh.flags, sizeof s3mh.flags);/* copy S3M flags */ ms3m->masterVol = s3mh.masterVol; /* copy master volume */ ms3m->speed = s3mh.speed; /* copy initial speed */ ms3m->tempo = s3mh.tempo; /* copy initial BPM tempo */ ms3m->masterMult = s3mh.masterMult & 15; /* copy master multiplier */ ms3m->stereo = (s3mh.masterMult >> 4) & 1; /* copy stereo flag */ /* Use fast volume slides for Scream Tracker versions below 3.20: */ if ( s3mh.trackerVer < 0x1320 ) ms3m->flags.fastVolSlide = 1; /* Allocate memory for pattern orders: (length of pattern orders must be even) */ ordersize = 2 * ((ms3m->songLength+1) / 2); if ( (error = memAlloc(ordersize, (void**) &ms3m->orders)) != OK ) S3MLOADPASSERROR /* Read pattern orders from file: */ if ( (error = fileRead(f, ms3m->orders, ordersize)) != OK ) S3MLOADPASSERROR /* Calculate real song length: (exclude 0xFF bytes from end) */ for ( i = (ms3m->songLength - 1); ms3m->orders[i] == 0xFF; i-- ); ms3m->songLength = i + 1; /* check that song length is nonzero: */ if ( ms3m->songLength == 0 ) { ERROR(errInvalidModule, ID_s3mLoadModule); s3mLoadError(SD); return errInvalidModule; } /* Allocate memory for instrument structures: */ if ( (error = memAlloc(ms3m->numInsts * sizeof(mpInstrument), (void**) &ms3m->insts)) != OK ) S3MLOADPASSERROR /* Clear all instruments: */ for ( i = 0; i < ms3m->numInsts; i++ ) { ms3m->insts[i].sample = NULL; ms3m->insts[i].sdInstHandle = 0; } /* Allocate memory for instrument paragraph pointers: */ if ( (error = memAlloc(2 * ms3m->numInsts, (void**) &instPtrs)) != OK ) S3MLOADPASSERROR /* Read instrument pointers: */ if ( (error = fileRead(f, instPtrs, 2 * ms3m->numInsts)) != OK ) S3MLOADPASSERROR /* Allocate memory for S3M file pattern pointers: */ if ( (error = memAlloc(2 * ms3m->numPatts, (void**) &pattPtrs)) != OK ) S3MLOADPASSERROR /* Read pattern pointers: */ if ( (error = fileRead(f, pattPtrs, 2 * ms3m->numPatts)) != OK ) S3MLOADPASSERROR /* Allocate memory for pattern pointers: */ if ( (error = memAlloc(ms3m->numPatts * sizeof(mpPattern*), (void**) &ms3m->patterns)) != OK ) S3MLOADPASSERROR if ( s3mh.panningMagic == 0xFC ) { /* Allocate memory for panning infos: */ if ( (error = memAlloc(32 * sizeof(uchar), (void**) &panningInfos)) != OK ) S3MLOADPASSERROR /* Read panning infos: */ if ( ( error = fileRead(f, panningInfos, 32 * sizeof(uchar)) ) != OK ) S3MLOADPASSERROR /* Convert panning values: */ for (i = 0; i < 32; i++) { if ( s3mh.chanSettings[i] > 15 ) { ms3m->chanSettings[i] = 0; } else { if ( panningInfos[i] & 0x20 ) { ms3m->chanSettings[i] = ((panningInfos[i] & 0xf) - 8) * 8; if ( ms3m->chanSettings[i] >= 0 ) ms3m->chanSettings[i] += 8; } else { if (s3mh.chanSettings[i] < 8) ms3m->chanSettings[i] = panLeft; else ms3m->chanSettings[i] = panRight; } } } /* Free panning infos: */ if ( (error = memFree(panningInfos)) != OK ) S3MLOADPASSERROR panningInfos = NULL; } else { /* copy default channel panning settings: */ for (i = 0; i < 32; i++) { if (s3mh.chanSettings[i] > 15) ms3m->chanSettings[i] = 0; else { if (s3mh.chanSettings[i] < 8) ms3m->chanSettings[i] = panLeft; else ms3m->chanSettings[i] = panRight; } } } /* Point all unallocated patterns to NULL for safety: */ for ( i = 0; i < ms3m->numPatts; i++ ) ms3m->patterns[i] = NULL; /* Read all patterns to memory: */ for ( i = 0; i < ms3m->numPatts; i++ ) { if ( pattPtrs[i] != NULL ) { /* Seek to pattern beginning in file: */ if ( (error = fileSeek(f, 16L * pattPtrs[i], fileSeekAbsolute)) != OK ) S3MLOADPASSERROR /* Read pattern length from file: */ if ( (error = fileRead(f, &pattSize, 2)) != OK ) S3MLOADPASSERROR #ifndef NOEMS if ( useEMS == 1 ) { /* Allocate EMS memory for pattern: */ if ( (error = emsAlloc(pattSize+2, (emsBlock**) &p)) != OK ) S3MLOADPASSERROR /* Map EMS block to conventional memory and point pattData to the memory area: */ if ( (error = emsMap((emsBlock*) p, (void**) &pattData)) != OK ) S3MLOADPASSERROR } else { #endif /* No EMS memory used - allocate conventional memory for pattern: */ if ( (error = memAlloc(pattSize+2, (void**) &p)) != OK ) S3MLOADPASSERROR pattData = p; #ifndef NOEMS } #endif ms3m->patterns[i] = p; pattData->length = pattSize; /* save pattern length */ /* Read pattern data from file: */ if ( (error = fileRead(f, &pattData->data[0], pattSize)) != OK ) S3MLOADPASSERROR } } /* deallocate pattern file pointers: */ if ( (error = memFree(pattPtrs)) != OK ) S3MLOADPASSERROR pattPtrs = NULL; /* detect number of channels: */ if ( (error = s3mDetectChannels(ms3m, &ms3m->numChans)) != OK ) S3MLOADPASSERROR /* allocate memory for instrument used flags: */ if ( (error = memAlloc(ms3m->numInsts, (void **) &ms3m->instsUsed)) != OK ) S3MLOADPASSERROR /* find which instruments are used: */ if ( (error = s3mFindUsedInsts(ms3m, ms3m->instsUsed)) != OK ) S3MLOADPASSERROR /* Find maximum sample length: */ maxSmpLength = 0; for ( i = 0; i < ms3m->numInsts; i++ ) { /* Seek to instrument header in file: */ if ( (error = fileSeek(f, 16L * instPtrs[i], fileSeekAbsolute)) != OK ) S3MLOADPASSERROR /* Read instrument header from file: */ if ( (error = fileRead(f, &s3mi, sizeof(s3mInstHdr))) != OK ) S3MLOADPASSERROR if ( maxSmpLength < s3mi.length ) maxSmpLength = s3mi.length; } /* Check that no instrument is too long: */ if ( maxSmpLength > SMPMAX ) { ERROR(errInvalidInst, ID_s3mLoadModule); s3mLoadError(SD); return errInvalidInst; } /* If EMS is used, allocate TEMPSIZE bytes of memory before the sample buffer and deallocate it after allocating the sample buffer to minimize memory fragmentation. This is not necessary if the samples will not be added to a Sound Device. */ #ifndef NOEMS if ( useEMS ) { #endif if ( SD != NULL ) { if ( (error = memAlloc(TEMPSIZE, &tempmem)) != OK ) { S3MLOADPASSERROR } } #ifndef NOEMS } #endif /* allocate memory for sample loading buffer if needed: */ if ( SD != NULL ) { if ( (error = memAlloc(maxSmpLength, (void**) &smpBuf)) != OK ) { S3MLOADPASSERROR } } #ifndef NOEMS if ( useEMS ) { #endif if ( SD != NULL ) { if ( (error = memFree(tempmem)) != OK ) { S3MLOADPASSERROR } } tempmem = NULL; #ifndef NOEMS } #endif /* Process all instruments: */ for ( i = 0; i < ms3m->numInsts; i++ ) { /* point inst to current instrument structure */ inst = &ms3m->insts[i]; /* Seek to instrument header in file: */ if ( (error = fileSeek(f, 16 * instPtrs[i], fileSeekAbsolute)) != OK ) S3MLOADPASSERROR /* Read instrument header from file: */ if ( (error = fileRead(f, &s3mi, sizeof(s3mInstHdr))) != OK ) S3MLOADPASSERROR /* Check if the instrument is valid - not too long, not stereo, 16-bit or packed */ if ( (s3mi.type > 1) || (s3mi.length > SMPMAX) || ((s3mi.flags & 6) != 0) || (s3mi.pack != 0) ) { ERROR(errInvalidInst, ID_s3mLoadModule); s3mLoadError(SD); return errFileRead; } mMemCopy(&inst->fileName[0], &s3mi.dosName[0], 13);/* copy filename */ mMemCopy(&inst->iname[0], &s3mi.iname[0], 28); /* copy inst name */ inst->length = s3mi.length; /* copy sample length */ inst->loopStart = s3mi.loopStart; /* copy sample loop start */ inst->loopEnd = s3mi.loopEnd; /* copy sample loop end */ inst->looping = s3mi.flags & 1; /* copy looping status */ inst->volume = s3mi.volume; /* copy default volume */ inst->c2Rate = s3mi.c2Rate; /* copy C2 playing rate */ /* If instrument is looped, set length to loop end */ if ( inst->looping == 1 ) inst->length = inst->loopEnd; /* Make sure that instrument volume is < 63 */ if ( inst->volume > 63 ) inst->volume = 63; /* Check if instrument is used: */ if ( ms3m->instsUsed[i] == 1 ) { /* Instrument is used - check if there is a sample for this instrument - type = 1, signature "SCRS" and length != 0 */ if ( (s3mi.type == 1) && mMemEqual(&s3mi.SCRS[0], "SCRS", 4) && (inst->length != 0) ) { if ( SD == NULL ) { /* No Sound Device used - allocate memory for the sample and point inst->sample to the memory area: */ if ( (error = memAlloc(inst->length, (void**) &smpBuf)) != OK ) S3MLOADPASSERROR inst->sample = smpBuf; } else { /* The instruments will be added to a Sound Device - load the sample to a buffer (allocated before) and point inst->sample to NULL: */ inst->sample = NULL; } /* Seek to sample position in file: */ if ( (error = fileSeek(f, 16L * s3mi.samplePtr, fileSeekAbsolute)) != OK ) S3MLOADPASSERROR /* Read sample to loading buffer: */ if ( (error = fileRead(f, smpBuf, inst->length)) != OK ) S3MLOADPASSERROR } else inst->sample = NULL; /* Add instrument to Sound Device: */ if ( SD != NULL ) { error = SD->AddInstrument(smpBuf, smp8bit, inst->length, inst->loopStart, inst->loopEnd, inst->volume, inst->looping, 1, &inst->sdInstHandle); if ( error != OK ) S3MLOADPASSERROR } /* Call SaveSampleInfo() if not NULL: */ if ( SaveSampleInfo != NULL ) { if ( inst->looping ) lend = inst->loopEnd; else lend = 0; /* no looping - set loop end to zero */ if ( (error = (*SaveSampleInfo)(inst->sdInstHandle, smpBuf, inst->length, inst->loopStart, lend)) != OK ) S3MLOADPASSERROR } } } /* deallocate instrument pointers: */ if ( (error = memFree(instPtrs)) != OK ) S3MLOADPASSERROR instPtrs = NULL; /* deallocate sample loading buffer: */ if ( SD != NULL ) { if ( (error = memFree(smpBuf)) != OK ) { S3MLOADPASSERROR } } smpBuf = NULL; if ( (error = fileClose(f)) != OK ) S3MLOADPASSERROR fileOpened = 0; *module = ms3m; /* return module pointer in *module */ return OK; }