/* loads3m.c * * Generic Module Player Screamtracker 3 Module loader * * $Id: loads3m.c,v 1.15 1997/01/25 15:23:16 pekangas Exp $ * * Copyright 1996,1997 Housemarque Inc. * * 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 "gmplayer.h" #ifndef NOEMS #include "ems.h" #endif #include "mutils.h" /*#define CHECK_THE_HEAP*/ #ifdef CHECK_THE_HEAP #include #endif RCSID(const char *loads3m_rcsid = "$Id: loads3m.c,v 1.15 1997/01/25 15:23:16 pekangas Exp $";) /* Pass error code in variable "error" on, used in gmpLoadS3M(). */ #define PASSERR() { LoadError(); PASSERROR(ID_gmpLoadS3M) } /****************************************************************************\ * * Function: TestHeap(void) * * Description: Test if the heap is corrupted. Watcom C ONLY! Implemented here * because the Watcom _heapchk() often reports the heap is OK * even though it isn't. * * Returns: MIDAS error code * \****************************************************************************/ #if defined(CHECKTHEHEAP) && defined(__WC32__) /* Maximum valid block in heap: */ #define MAXBLK (2048*1024) int TestHeap(void) { static struct _heapinfo hinfo; int hstatus; unsigned len; hinfo._pentry = NULL; while ( 1 ) { hstatus = _heapwalk(&hinfo); if ( hstatus != _HEAPOK ) break; len = hinfo._size; if ( (len == 0) || (len > MAXBLK) || (len & 3) ) return errHeapCorrupted; } if ( (hstatus == _HEAPEND) || (hstatus == _HEAPEMPTY) ) return OK; return errHeapCorrupted; } #define CHECKHEAP \ { \ if ( TestHeap() != OK ) \ { \ puts("HEAP CORRUPTED!"); \ ERROR(errHeapCorrupted, ID_gmpLoadXM); \ LoadError(); \ return errHeapCorrupted; \ } \ } #else #define CHECKHEAP #endif /****************************************************************************\ * struct s3mHeader * ---------------- * Description: Scream Tracker 3 module file header \****************************************************************************/ typedef struct { char name[28]; /* song name */ U8 num1A; /* 0x1A */ U8 type; /* file type */ U16 unused1; U16 songLength; /* number of orders */ U16 numInsts; /* number of instruments */ U16 numPatts; /* number of patterns */ U16 flags; /* struct { int st2Vibrato : 1; int st2Tempo : 1; int ptSlides : 1; int zeroVolOpt : 1; int ptLimits : 1; int filter : 1; int fastVolSlide : 1; int unused : 9; } flags; */ U16 trackerVer; /* tracker version */ U16 formatVer; /* file format version */ char SCRM[4]; /* "SCRM" */ U8 masterVol; /* master volume */ U8 speed; /* initial speed */ U8 tempo; /* initial tempo */ U8 masterMult; /* master multiplier (bits 0-3),stereo (bit 4) */ U8 ultraClicks; /* Ultraclick removal info */ U8 panningMagic; /* 0FCh if panning infos exist */ U8 unused2[8]; U16 special; /* pointer to special custom data */ U8 chanSettings[32]; /* channel settings */ } s3mHeader; /****************************************************************************\ * struct s3mPattern * ---------------- * Description: Screamtracker 3 Module pattern \****************************************************************************/ typedef struct { U16 pattDataSize; /* pattern data size */ U8 data[EMPTYARRAY]; /* packed pattern data */ } s3mPattern; /****************************************************************************\ * struct s3mInstHdr * ----------------- * Description: Scream Tracker 3 module instrument file header \****************************************************************************/ typedef struct { U8 type; /* instrument type */ char dosName[12]; /* DOS filename (8+3) */ char zero; /* 0 */ U16 samplePtr; /* paragraph ptr to sample data */ U32 length; /* sample length */ U32 loopStart; /* sample loop start */ U32 loopEnd; /* sample loop end */ U8 volume; /* volume */ U8 disk; /* instrument disk number */ U8 pack; /* sample packing info (0 = raw,1 = DP30ADPCM1) */ U8 flags; /* bit0 = loop, bit1 = stereo,bit2 = 16-bit */ U32 c2Rate; /* C2 sampling rate */ U32 unused; U16 gusPos; /* position in GUS memory / 32 */ U16 int512; U32 intLastUsed; char iname[28]; /* instrument name */ char SCRS[4]; /* "SCRS" if sample */ } s3mInstHdr; /* Conversion table from Screamtracker command number to GMP ones: */ static uchar s3mCommands[26] = { gmpcSetSpeed, /* A - Set speed */ gmpcPositionJump, /* B - Position jump */ gmpcPatternBreak, /* C - Pattern break */ gmpcVolumeSlide, /* D - Volume slide */ gmpcSlideDown, /* E - Slide down */ gmpcSlideUp, /* F - Slide up */ gmpcTonePortamento, /* G - Tone portamento */ gmpcVibrato, /* H - Vibrato */ gmpcNone, /* I - Tremor */ gmpcArpeggio, /* J - Arpeggio */ gmpcVibVSlide, /* K - Vibrato + volume slide */ gmpcTPortVSlide, /* L - Tone portamento + volume slide */ gmpcNone, /* M - Unused */ gmpcNone, /* N - Unused */ gmpcSampleOffset, /* O - Set sample offset */ gmpcNone, /* P - Unused */ gmpcS3MRetrig, /* Q - ST3 extended retrig */ gmpcTremolo, /* R - Tremolo */ gmpcNone, /* S - S-commands (table below) */ gmpcSetTempo, /* T - Set tempo */ gmpcNone, /* U - Fine vibrato */ gmpcSetMVolume, /* V - Set master volume */ gmpcMusicSync, /* W - Music synchronization (extension) */ gmpcSetPanning, /* X - Set panning (extension) */ gmpcNone, /* Y - Unused */ gmpcSetPanning16 /* Z - Set panning (extension) */ }; /* Conversion table from Screamtracker S-command numbers to GMP commands: */ static uchar s3mSCommands[16] = { gmpcNone, /* S0 - Set filter */ gmpcNone, /* S1 - Set glissando control */ gmpcNone, /* S2 - Set finetune */ gmpcNone, /* S3 - Set vibrato waveform */ gmpcNone, /* S4 - Set tremolo waveform */ gmpcNone, /* S5 - Unused */ gmpcNone, /* S6 - Unused */ gmpcNone, /* S7 - Unused */ gmpcSetPanning16, /* S8 - Set panning */ gmpcNone, /* S9 - Unused */ gmpcNone, /* SA - Stereo control (obsolete) */ gmpcPatternLoop, /* SB - Pattern loop */ gmpcNoteCut, /* SC - Note cut */ gmpcNoteDelay, /* SD - Note delay */ gmpcPatternDelay, /* SE - Pattern delay */ gmpcNone /* SF - Invert loop (unused in ST3) */ }; /****************************************************************************\ * Module loader buffers and file handle. 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; /* file handle for module file */ static int fileOpened; /* 1 if module file has been opened */ static gmpModule *module; /* pointer to GMP module structure */ static s3mHeader *header; /* pointer to S3M module header */ static uchar *instUsed; /* instrument used flags */ static s3mPattern *s3mPatt; /* pointer to S3M pattern data */ static gmpPattern *convPatt; /* pointer to converted pattern data*/ static U16 *instPtrs; /* instrument paragraph pointers */ static U16 *pattPtrs; /* pattern paragraph pointers */ static uchar *smpBuf; /* sample loading buffer */ static unsigned numChans; /* number of channels in module */ static unsigned maxChan; /* maximum enabled channel */ static unsigned maxInstUsed; /* maximum instrument used */ static s3mInstHdr s3mi; /****************************************************************************\ * * Function: int ConvertPattern(s3mPattern *srcPatt, gmpPattern *destPatt, * unsigned *convLen); * * Description: Converts a pattern from S3M to GMP internal format. * Also updates instUsed flags. * * Input: s3mPattern *srcPatt pointer to pattern in S3M format * gmpPattern *destPatt pointer to destination GMP pattern * unsigned *convLen pointer to converted pattern length * * Returns: MIDAS error code. Converted pattern length (in bytes, * including header) is written to *convLen. * \****************************************************************************/ static int ConvertPattern(s3mPattern *srcPatt, gmpPattern *destPatt, unsigned *convLen) { uchar *src = &srcPatt->data[0]; uchar *dest = &destPatt->data[0]; int row; unsigned len; U8 note, inst, command, infobyte, vol, compInfo; U8 s3mComp; /* Packed data consits of following entries: BYTE:what 0=end of row &31=channel &32=follows; BYTE:note, BYTE:instrument &64=follows; BYTE:volume &128=follows; BYTE:command, BYTE:info */ /* Check if the pattern is empty: */ if ( (srcPatt->pattDataSize == 0) || (srcPatt == NULL) ) { /* Write row end marker for each row: */ for ( row = 0; row < 64; row++ ) *(dest++) = 0; /* Write number of rows to pattern header: */ destPatt->rows = 64; /* Write converted pattern length to header and return it in *convLen: */ *convLen = destPatt->length = 64 + sizeof(gmpPattern); return OK; } for ( row = 0; row < 64; row++ ) { while ( *src != 0 ) { /* No data for this channel yet: */ compInfo = 0; note = 0xFF; inst = 0xFF; command = 0; infobyte = 0; vol = 0; /* Get note number/compression info from pattern data: */ s3mComp = *src++; /* Check if there is a new note and instrument: */ if ( s3mComp & 32 ) { /* Check that the note is not key off and convert it to GMP internal format: */ if ( ( *src < 0x7f ) || ( *src == 0xfe ) || ( *src == 0xff ) ) note = *src; src++; /* Get the instrument number: */ if ( *src <= module->numInsts ) inst = (*src) - 1; src++; /* Check if the instrument number if above maximum instrument number used: */ if ( inst <= 100 ) { if ( inst < module->numInsts ) { if ( inst > maxInstUsed ) maxInstUsed = inst; /* Mark instrument used: */ instUsed[inst] = 1; } } /* Mark that there is a new note and/or instrument: */ compInfo |= 32; if ( numChans <= (unsigned) (s3mComp & 31) ) numChans = (s3mComp & 31) + 1; } /* Check if there is a volume column byte: */ if ( s3mComp & 64 ) { /* Get the volume column byte: */ vol = *(src++); if ( vol > 63 ) vol = 63; /* Mark that there is a volume column byte: */ compInfo |= 64; } /* Check if there is a command: */ if ( s3mComp & 128 ) { /* Get the command number: */ command = *(src++); /* Get the infobyte: */ infobyte = *(src++); } /* If there is a command or an infobyte we need to write the command to the destination pattern: */ if ( (command > 0) && (command < 26 ) ) { switch ( command ) { case 19: /* Command S - check actual command number: */ command = s3mSCommands[infobyte >> 4]; infobyte = infobyte & 0x0F; break; default: /* Convert command to GMP command number: */ command = s3mCommands[command - 1]; break; } /* Mark that there is a command: */ if ( command != gmpcNone ) compInfo |= 128; } /* If the compression information is nonzero, there is some data for this channel: */ if ( ( compInfo & 0xE0 ) != 0 ) { /* Set channel number to lower 5 bits of the compression info and write it to destination: */ compInfo |= ( s3mComp & 31 ); if ( (unsigned) (s3mComp & 31) <= maxChan ) { *(dest++) = compInfo; /* Check if there a note+instrument pair: */ if ( compInfo & 32 ) { /* Write note and instrument numbers: */ *(dest++) = note; *(dest++) = inst; } /* Check if there is a volume column byte: */ if ( compInfo & 64 ) { /* Write the volume column byte: */ *(dest++) = vol; } /* Check if there is a command: */ if ( compInfo & 128 ) { /* Write command and command infobyte: */ *(dest++) = command; *(dest++) = infobyte; } } } } /* Write row end marker: */ *(dest++) = 0; src++; } /* Write number of rows to pattern header: */ destPatt->rows = 64; /* Calculate converted pattern length: */ len = (unsigned) (dest - ((uchar*) destPatt)); /* Write converted pattern length to header and return it in *convLen: */ destPatt->length = len; *convLen = len; return OK; } /****************************************************************************\ * * Function: void LoadError(void) * * Description: Stops loading the module, deallocates all buffers and closes * the file. * \****************************************************************************/ #define condFree(x) { if ( x != NULL ) if ( memFree(x) != OK) return; } static void LoadError(void) { /* 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 ( module != NULL ) if ( gmpFreeModule(module) != OK ) return; /* Deallocate structures if allocated: */ condFree(header) condFree(instUsed) condFree(smpBuf) } /****************************************************************************\ * * Function: int gmpLoadS3M(char *fileName, int addSD, mpModule **module) * * Description: Loads a Scream Tracker 3 module to memory in Generic Module * Player module format * * Input: char *fileName module file name * int addSD 1 if module samples should be added to * the current Sound Device, 0 if not * int (*SampleCallback)(...) pointer to callback function that * will be called after sample has been * added to Sound Device, but before * sample data is deallocated * mpModule **module pointer to GMP module pointer * * Returns: MIDAS error code * \****************************************************************************/ int CALLING gmpLoadS3M(char *fileName, int addSD, int (CALLING *SampleCallback)(sdSample *sdsmp, gmpSample *gmpsmp), gmpModule **_module) { int error; /* MIDAS error code */ gmpInstrument *inst; uchar *panningInfos; unsigned i; ulong maxSample; static unsigned convPattLen; unsigned slength; gmpSample *sample; uchar *temp; static sdSample sdSmp; int n; #ifndef NOEMS uchar *patt; #endif int panValue; unsigned numS3MPatterns; unsigned numUsedPatterns; /* point buffers to NULL and set fileOpened to 0 so that LoadError() can be called at any point: */ fileOpened = 0; module = NULL; header = NULL; instUsed = NULL; s3mPatt = NULL; convPatt = NULL; instPtrs = NULL; pattPtrs = NULL; smpBuf = NULL; /* Clear other loader variables: */ maxInstUsed = 0; /* Allocate memory for Screamtracker module header: */ if ( (error = memAlloc(sizeof(s3mHeader), (void**) &header)) != OK ) PASSERR() /* Open module file: */ if ( (error = fileOpen(fileName, fileOpenRead, &f)) != OK ) PASSERR() fileOpened = 1; /* Allocate memory for the module structure: */ if ( (error = memAlloc(sizeof(gmpModule), (void**) &module)) != OK ) PASSERR() /* Clear module structure so that it can safely be deallocated with gmpFreeModule() at any point: */ module->panning = NULL; module->songData = NULL; module->instruments = NULL; module->patterns = NULL; /* read Screamtracker module header: */ if ( (error = fileRead(f, header, sizeof(s3mHeader))) != OK ) PASSERR() numChans = 0; /* Check the module signature: */ if ( !(mMemEqual(&header->SCRM[0], "SCRM", 4) ) ) { ERROR(errInvalidModule, ID_gmpLoadS3M); LoadError(); return errInvalidModule; } /* Copy song name: */ mMemCopy(&module->name[0], &header->name[0], 28); module->name[28] = 0; /* force terminating '\0' */ module->songLength = header->songLength; /* copy song length */ module->numInsts = header->numInsts; /* set number of instruments */ numS3MPatterns = header->numPatts; /* store number of patterns */ module->numPatts = 0; /* not really known yet! */ module->playMode = gmpST3; /* set ST3 playing mode */ module->masterVolume = header->masterVol; /* copy master volume */ module->speed = header->speed; /* initial speed */ module->tempo = header->tempo; /* initial tempo */ /* Copy some flags */ module->playFlags.ptLimits = ( header->flags & 0x10 ) >> 4; module->playFlags.fastVolSlides = ( header->flags & 0x40 ) >> 6; if ( header->trackerVer < 0x1320 ) module->playFlags.fastVolSlides = 1; module->restart = 0; #ifdef DEBUGMESSAGES puts("Header ready"); #endif CHECKHEAP /* Allocate memory for song data: */ if ( (error = memAlloc(sizeof(ushort) * module->songLength, (void**) &module->songData)) != OK ) PASSERR() /* read song data: */ if ( (error = fileRead(f, module->songData, sizeof(uchar) * module->songLength)) != OK ) PASSERR() /* Calculate real song length: (exclude 0xFF bytes from end) */ for ( n = (module->songLength - 1); module->songData[n] == 0xFF; n-- ); module->songLength = n + 1; /* Convert song data and find the highest pattern number used: */ temp = (uchar*) module->songData; numUsedPatterns = 0; for ( n = module->songLength - 1; n >= 0 ; n-- ) { switch ( temp[n] ) { case 0xfe: module->songData[n] = 0xfffe; break; case 0xff: module->songData[n] = 0xffff; break; default: module->songData[n] = temp[n]; if ( temp[n] >= numUsedPatterns ) numUsedPatterns = temp[n] + 1; } } module->numPatts = numUsedPatterns; /* check that song length is nonzero: */ if ( module->songLength == 0 ) { ERROR(errInvalidModule, ID_gmpLoadS3M); LoadError(); return errInvalidModule; } #ifdef DEBUGMESSAGES puts("Song data ready"); printf("Num.patts: %i\n", module->numPatts); #endif CHECKHEAP /* Allocate memory for pattern pointers: */ if ( (error = memAlloc(4 * module->numPatts, (void**) &module->patterns)) != OK ) PASSERR() /* Set all pattern pointers to NULL to mark them unallocated: */ for ( i = 0; i < module->numPatts; i++ ) module->patterns[i] = NULL; /* Allocate memory for instrument pointers: */ if ( (error = memAlloc(sizeof(gmpInstrument*) * module->numInsts, (void**) &module->instruments)) != OK ) PASSERR() /* Set all instrument pointers to NULL to mark them unallocated: */ for ( i = 0; i < module->numInsts; i++ ) module->instruments[i] = NULL; /* Allocate memory for instrument used flags: */ if ( (error = memAlloc(module->numInsts, (void**)&instUsed)) != OK ) PASSERR() /* Mark all instruments unused: */ for ( i = 0; i < module->numInsts; i++ ) instUsed[i] = 0; /* Allocate memory for instrument paragraph pointers: */ if ( (error = memAlloc(sizeof(U16) * module->numInsts, (void**) &instPtrs)) != OK ) PASSERR() /* Read instrument pointers: */ if ( (error = fileRead(f, instPtrs, sizeof(U16) * module->numInsts)) != OK ) PASSERR() /* Allocate memory for S3M file pattern pointers: */ if ( (error = memAlloc(sizeof(U16) * numS3MPatterns, (void**) &pattPtrs)) != OK ) PASSERR() /* Read pattern pointers: */ if ( (error = fileRead(f, pattPtrs, sizeof(U16) * numS3MPatterns)) != OK ) PASSERR() #ifdef DEBUGMESSAGES puts("Misc. pointers ready"); #endif CHECKHEAP /* Allocate memory for channel initial panning positions: */ if ( (error = memAlloc(32 * sizeof(int), (void**) &module->panning)) != OK ) PASSERR() if ( header->panningMagic == 0xFC ) { /* Allocate memory for panning infos: */ if ( (error = memAlloc(32 * sizeof(uchar), (void**) &panningInfos)) != OK ) PASSERR() /* Read panning infos: */ if ( ( error = fileRead(f, panningInfos, 32 * sizeof(uchar)) ) != OK ) PASSERR() /* Convert panning values: */ for (i = 0; i < 32; i++) { if ( header->chanSettings[i] > 15 ) { module->panning[i] = 0x40; } else { maxChan = i; if ( panningInfos[i] & 0x20 ) { panValue = panningInfos[i] & 0xF; if ( panValue < 7 ) { module->panning[i] = 8 * panValue; } else { if ( panValue > 8 ) module->panning[i] = 0x80 - (15 - panValue) * 8; else module->panning[i] = 0x40; } } else { if (header->chanSettings[i] < 8) module->panning[i] = 0x00; else module->panning[i] = 0x80; } } } /* Free panning infos: */ if ( (error = memFree(panningInfos)) != OK ) PASSERR() panningInfos = NULL; } else { /* copy default channel panning settings: */ for (i = 0; i < 32; i++) { if (header->chanSettings[i] > 15) module->panning[i] = 0; else { maxChan = i; if (header->chanSettings[i] < 8) module->panning[i] = 0x00; else module->panning[i] = 0x80; } } } #ifdef DEBUGMESSAGES puts("Panning ready"); #endif CHECKHEAP /* Find maximum sample length: */ maxSample = 0; for ( i = 0; i < module->numInsts; i++ ) { /* Seek to instrument header in file: */ if ( (error = fileSeek(f, 16L * instPtrs[i], fileSeekAbsolute)) != OK ) PASSERR() /* Read instrument header from file: */ if ( (error = fileRead(f, &s3mi, sizeof(s3mInstHdr))) != OK ) PASSERR() if ( maxSample < s3mi.length ) maxSample = s3mi.length; } /* Check that the maximum sample length is below the Sound Device limit:*/ if ( maxSample > SMPMAX ) { ERROR(errInvalidInst, ID_gmpLoadS3M); LoadError(); return errInvalidInst; } #ifdef DEBUGMESSAGES puts("Max. sample ready"); printf("Max. sample: %i\n", maxSample); #endif CHECKHEAP /* Allocate memory for pattern loading buffer: */ if ( (error = memAlloc(32 * 64 * 6 + sizeof(s3mPattern), (void**) &s3mPatt)) != OK ) PASSERR() /* Allocate memory for pattern conversion buffer: (maximum GMP pattern data size is 6 bytes per row per channel plus header) */ if ( (error = memAlloc(32 * 64 * 6 + sizeof(gmpPattern), (void**) &convPatt)) != OK ) PASSERR() #ifdef DEBUGMESSAGES puts("Ready for conversion"); #endif CHECKHEAP /* Load and convert all patterns: */ for ( i = 0; i < module->numPatts; i++ ) { if ( (i < numS3MPatterns) && (pattPtrs[i] != 0) ) { #ifdef DEBUGMESSAGES puts("Seek"); #endif CHECKHEAP /* Seek to pattern beginning in file: */ if ( (error = fileSeek(f, 16L * pattPtrs[i], fileSeekAbsolute)) != OK ) PASSERR() #ifdef DEBUGMESSAGES puts("Read pattern length"); #endif CHECKHEAP /* Read pattern length from file: */ if ( (error = fileRead(f, &s3mPatt->pattDataSize, sizeof(U16))) != OK ) PASSERR() #ifdef DEBUGMESSAGES printf("Patt.len: %i\n", s3mPatt->pattDataSize); puts("Read pattern data"); #endif CHECKHEAP /* Read pattern data from file: */ if ( (error = fileRead(f, &s3mPatt->data[0], s3mPatt->pattDataSize)) != OK ) PASSERR() } else { s3mPatt->pattDataSize = 0; #ifdef DEBUGMESSAGES puts("Empty pattern"); #endif } #ifdef DEBUGMESSAGES puts("Convert pattern"); #endif CHECKHEAP /* Convert the pattern data, checking the instruments used: */ if ( (error = ConvertPattern(s3mPatt, convPatt, &convPattLen)) != OK ) PASSERR() #if 0 #ifndef NOEMS if ( mUseEMS == 1 ) /* is EMS memory used? */ { /* Allocate EMS for converted pattern data for current pattern in module: */ if ( (error = emsAlloc(convPattLen, (emsBlock**) &module->patterns[i])) != OK ) PASSERR() /* Map the allocated EMS block to conventional memory: */ if ( (error = emsMap((emsBlock*) module->patterns[i], (void**) &patt)) != OK) PASSERR() /* Copy the converted pattern data to the EMS block: */ mMemCopy(patt, convPatt, convPattLen); } else #endif #endif { #ifdef DEBUGMESSAGES printf("Alloc mem: %i\n", convPattLen); #endif CHECKHEAP /* Allocate memory for converted pattern data for current pattern in module: */ if ( (error = memAlloc(convPattLen, (void**)&module->patterns[i])) != OK ) PASSERR() #ifdef DEBUGMESSAGES puts("Copy pattern"); #endif CHECKHEAP /* Copy the converted pattern data: */ mMemCopy(module->patterns[i], convPatt, convPattLen); } #ifdef DEBUGMESSAGES printf("Pattern %i, Conv.len: %i\n", i, convPattLen); #endif CHECKHEAP } maxChan++; /* store number of channels */ module->numChannels = ( maxChan < numChans ) ? maxChan : numChans; #ifdef DEBUGMESSAGES printf("Num.channels: %i\n", module->numChannels); #endif CHECKHEAP /* Deallocate pattern paragraph pointers: */ if ( (error = memFree(pattPtrs)) != OK ) PASSERR() pattPtrs = NULL; /* Deallocate pattern conversion buffer: */ if ( (error = memFree(convPatt)) != OK ) PASSERR() convPatt = NULL; /* Deallocate pattern loading buffer: */ if ( (error = memFree(s3mPatt)) != OK ) PASSERR() s3mPatt = NULL; #ifdef DEBUGMESSAGES puts("Pattern conversion ready"); #endif CHECKHEAP /* If samples should be added to Sound Device, allocate memory for sample loading buffer: */ if ( addSD ) { if ( (error = memAlloc(maxSample, (void**) &smpBuf)) != OK ) PASSERR() } if ( maxInstUsed > module->numInsts ) module->numInsts = maxInstUsed; #ifdef DEBUGMESSAGES printf("Max.inst. used: %i\n", maxInstUsed); #endif CHECKHEAP /* Load all samples and convert sample and instrument data: */ for ( i = 0; i < module->numInsts; i++ ) { /* Seek to instrument header in file: */ if ( (error = fileSeek(f, 16L * instPtrs[i], fileSeekAbsolute)) != OK ) PASSERR() /* Read instrument header from file: */ if ( (error = fileRead(f, &s3mi, sizeof(s3mInstHdr))) != OK ) PASSERR() /* Allocate memory for this instrument structure: (add space for one sample if the instrument is used) */ if ( instUsed[i] ) { if ( (error = memAlloc(sizeof(gmpInstrument) + sizeof(gmpSample), (void**) &module->instruments[i])) != OK ) PASSERR() } else { if ( (error = memAlloc(sizeof(gmpInstrument), (void**) &module->instruments[i])) != OK ) PASSERR() } /* Point inst to current instrument structure: */ inst = module->instruments[i]; /* Mark there are no sample for the instrument: */ inst->numSamples = 0; /* Copy instrument name: */ mMemCopy(&inst->name[0], &s3mi.iname[0], 28); inst->name[28] = 0; /* force terminating '\0' */ /* Mark the instrument has no sample number table for keyboard notes: */ inst->noteSamples = NULL; /* Mark the instrument has no volume or panning envelopes: */ inst->volEnvelope = NULL; inst->panEnvelope = NULL; /* Mark the instrument has no FT2 auto-vibrato: */ inst->vibType = inst->vibSweep = inst->vibDepth = inst->vibRate = 0; /* Check if the instrument is used in the module: */ if ( instUsed[i] ) { /* The instrument is used - mark it used and point sample to its sample information: */ inst->used = 1; inst->numSamples = 1; sample = &inst->samples[0]; /* Mark there is no sample data and the sample has not been added to Sound Device: */ sample->sample = NULL; sample->sdHandle = 0; /* Copy sample loop start and calculate loop end: */ sample->loop1Start = s3mi.loopStart; sample->loop1End = s3mi.loopEnd; slength = s3mi.length; if ( ( s3mi.flags & 1 ) && ( s3mi.loopStart != s3mi.loopEnd ) ) { /* Looping sample - set loop types and limit sample length to loop end: */ sample->loopMode = sdLoop1; sample->loop1Type = loopUnidir; if ( slength > sample->loop1End ) slength = sample->loop1End; } else { /* Sample not looping: */ sample->loopMode = sdLoopNone; sample->loop1Type = loopNone; } /* Set sample length: */ sample->sampleLength = slength; /* Copy sample default volume: */ sample->volume = s3mi.volume; /* No finetune used: */ sample->finetune = 0; /* Copy sample base tune: */ sample->baseTune = s3mi.c2Rate; /* Set sample default panning position to middle: */ sample->panning = panMiddle; /* Check if there is sample data for this sample: */ if ( slength != 0 ) { /* If sample data should not be added to Sound Device, allocate memory for the sample data and point smpBuf there: */ if ( !addSD ) { smpBuf = NULL; if ( (error = memAlloc(slength, (void**)&smpBuf)) != OK ) PASSERR() sample->sample = smpBuf; } else { /* Sample is added to the Sound Device - sample data is not available: */ sample->sample = NULL; } /* Seek to instrument in file: */ if ( (error = fileSeek(f, 16L * s3mi.samplePtr, fileSeekAbsolute)) != OK ) PASSERR() /* There is sample data - load sample: */ if ( (error = fileRead(f, smpBuf, slength)) != OK ) PASSERR() /* Set Sound Device sample type: */ sdSmp.sampleType = smp8bit; sample->sampleType = smp8bit; /* Set Sound Device sample position in memory: */ sdSmp.samplePos = sdSmpConv; /* Point Sound Device sample data to sample loading buffer: */ sdSmp.sample = smpBuf; /* Point smpBuf to NULL if the sample is not added to Sound Device to mark it should not be deallocated: */ if ( !addSD ) smpBuf = NULL; } else { /* Mark there is no sample data: */ sdSmp.sampleType = smpNone; sdSmp.samplePos = sdSmpNone; sdSmp.sample = NULL; } sdSmp.sampleLength = slength; sdSmp.loopMode = sample->loopMode; sdSmp.loop1Start = sample->loop1Start; sdSmp.loop1End = sample->loop1End; sdSmp.loop1Type = sample->loop1Type; /* Set up the rest of Sound Device sample structure so that the sample can be added to the Sound Device: */ if ( addSD ) { /* Add the sample to Sound Device and store the Sound Device sample handle in sample->sdHandle: */ if ( (error = gmpSD->AddSample(&sdSmp, 1, &sample->sdHandle)) != OK) PASSERR() /* Call sample callback if used: */ if ( SampleCallback != NULL ) { if ( (error = SampleCallback(&sdSmp, sample)) != OK ) PASSERR() } } else { /* Sample data has not been added to Sound Device: */ sample->sdHandle = 0; } } else { /* Sample is not used - there is no sample data: */ inst->used = 0; inst->numSamples = 0; } #ifdef DEBUGMESSAGES printf("Inst: %i, Len: %i\n", i, slength); #endif CHECKHEAP } /* Deallocate sample loading buffer if allocated: */ if ( addSD ) { if ( (error = memFree(smpBuf)) != OK ) PASSERR() } smpBuf = NULL; /* Deallocate instrument paragraph pointers: */ if ( (error = memFree(instPtrs)) != OK ) PASSERR() instPtrs = NULL; /* Deallocate instrument use flags: */ if ( (error = memFree(instUsed)) != OK ) PASSERR() instUsed = NULL; #ifdef DEBUGMESSAGES puts("Samples ready"); #endif CHECKHEAP /* Now we are finished loading. Close module file: */ if ( (error = fileClose(f)) != OK) PASSERR() fileOpened = 0; /* Deallocate Screamtracker module header: */ if ( (error = memFree(header)) != OK ) PASSERR(); /* Return module pointer in *module: */ *_module = module; #ifdef DEBUGMESSAGES puts("Load Ok"); #endif CHECKHEAP return OK; } /* * $Log: loads3m.c,v $ * Revision 1.15 1997/01/25 15:23:16 pekangas * The file is now closed properly if loading terminates to an error * * Revision 1.14 1997/01/16 18:41:59 pekangas * Changed copyright messages to Housemarque * * Revision 1.13 1996/12/30 23:38:08 jpaana * Cleaned up LoadError * * Revision 1.12 1996/09/08 21:00:42 pekangas * Fixed Set Tempo -command * * Revision 1.11 1996/09/05 06:28:28 pekangas * Changed S8 command to SetPanning16 (was SetPanning) (thanks Zapper) * * Revision 1.10 1996/09/01 15:42:31 pekangas * Changed to use new style slides (no more signed infobytes) * * Revision 1.9 1996/08/06 19:18:48 pekangas * Fixed to produce legal gmpModules so that mod2gm works * * Revision 1.8 1996/08/02 19:46:10 pekangas * Kluged to skip all instruments above module->numInsts to prevent crashes * * Revision 1.7 1996/07/16 17:35:35 pekangas * Fixed song data conversion loop * * Revision 1.6 1996/07/13 20:19:32 pekangas * Eliminated Visual C warnings * * Revision 1.5 1996/07/13 18:30:19 pekangas * Fixed to compile with Visual C * * Revision 1.4 1996/06/14 16:23:28 pekangas * Removed heap checking to speed up loading * * Revision 1.3 1996/05/24 17:03:24 pekangas * Fixed to work with Watcom C again - using EMPTYARRAY * * Revision 1.2 1996/05/24 16:20:36 jpaana * Fixed to work with gcc * * Revision 1.1 1996/05/22 20:49:33 pekangas * Initial revision * */