/***************************************************************************
*   NAME:  OLDSYSX.C $Revision: 1.2 $
**  COPYRIGHT:
**  "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: oldsysx.c $
* Revision 1.2  1995/03/01 16:58:35  unknown
* Added file header
* Revision 1.1  1995/02/23 15:14:59  sdsmith
* Initial revision
***************************************************************************/
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <string.h>
#include <iw.h>
#include <os.h>
#include "midi.h"
#include "patch.h"

extern struct header file_header;
extern struct header patch_header;
extern struct header program_header;
extern struct header layer_header;
extern struct header wave_header;
extern struct header text_header;
extern struct header data_header;
extern struct header envp_header;
extern struct header pted_header;

struct patch_lib pedlib;


void LoadEditablePatch(int,int,char far *);
void UnLoadEditablePatch(int,int);
int SetPatchParam(int,int,unsigned char,unsigned char);
int SetLayerParam(int,int,int,unsigned char,int);
int SetEnvelopeParam(int,int,int,int,int,unsigned char,int);
int MakeParam(unsigned char nMSB,unsigned char nLSB);
#define MakeParam(nMSB,nLSB) (((nMSB)&0x7F)<<7|((nLSB)&0x7F))

/****************************************************************************

FUNCTION DEFINITION:
DoSysexMessage - executes the current sysex message for the client

DESCRIPTION:
After a sysex message has been successfully parsed, this function is called to
execute the message.

RETURNS: void
*/
void DoSysexMessage(
	int nClient )               // index of the client that sent the sysex message
{                         
	int nMustInformKernel = 0;
	struct SysexMessage RFAR*pMsg;
	struct PatchListEntry RFAR * pPLE;
					
	pMsg = (struct SysexMessage RFAR*)&MidiClients[nClient].SysMsg;
	switch(pMsg->command){
		case SXM_PATCHLOAD:                     
			switch(pMsg->subcmnd){
				case SXMP_LOAD:
#ifdef DEBUG
					OutputDebugStr("MSX: Load Patch\r\n");
#endif
					LoadEditablePatch(
						pMsg->program,
						pMsg->bank,
						pMsg->data);
					// Do channel change here?
					// pPLE = GetEditablePatch(nProg,nBank);
					// if(pPLE)
					//      ProgramAssign(nClient,nChan,nProgram);
					break;
				case SXMP_UNLOAD:
#ifdef DEBUG
					OutputDebugStr("MSX: Unload Patch\r\n");
#endif
					UnLoadEditablePatch(
						pMsg->program,
						pMsg->bank);
					break;
			}
			break;
		case SXM_EDITPATCH:         
#ifdef DEBUG
			OutputDebugStr("MSX: Patch Param ");
#endif
			nMustInformKernel = 
			SetPatchParam(
				pMsg->program,
				pMsg->bank,
				pMsg->subcmnd,
				pMsg->data[0]);
			break;
		case SXM_EDITLAYER:
#ifdef DEBUG
			OutputDebugStr("MSX: Layer Param ");
#endif
			switch(pMsg->subcmnd){
				case SXML_HIGHRANGE:        // One Byte of Data
				case SXML_LOWRANGE:
				case SXML_PANOFFSET:
				case SXML_PANFREQ:
				case SXML_VELMODE:
				case SXML_ATTEN:
				case SXML_FREQCENTER:
				case SXML_LAYEREVENT:
				case SXML_TREMSHAPE:
				case SXML_VIBSHAPE:
					nMustInformKernel = 
					SetLayerParam(
						pMsg->program,
						pMsg->bank,
						pMsg->data[0],
						pMsg->subcmnd,
						pMsg->data[1]);
					break;
				case SXML_TREMDEPTH:        // Two Bytes of Data
				case SXML_TREMRATE:
				case SXML_TREMSWEEP:
				case SXML_TREMDELAY:
				case SXML_VIBDEPTH:
				case SXML_VIBRATE:
				case SXML_VIBSWEEP:
				case SXML_VIBDELAY:
				case SXML_FREQSCALE:
					nMustInformKernel = 
					SetLayerParam(
						pMsg->program,
						pMsg->bank,
						pMsg->data[0],
						pMsg->subcmnd,
						MakeParam(pMsg->data[1],pMsg->data[2]));
					break;
			}
			break;
		case SXM_EDITENVELOPE:
#ifdef DEBUG
			OutputDebugStr("MSX: Envelope Param ");
#endif
			if(pMsg->subcmnd < 5)
				nMustInformKernel = 
				SetEnvelopeParam(
					pMsg->program,
					pMsg->bank,
					pMsg->data[0],
					pMsg->data[1],
					pMsg->data[2],
					pMsg->subcmnd,
					pMsg->data[3]);
			else
				nMustInformKernel = 
				SetEnvelopeParam(
					pMsg->program,
					pMsg->bank,
					pMsg->data[0],
					pMsg->data[1],
					pMsg->data[2],
					pMsg->subcmnd,
					MakeParam(pMsg->data[3],pMsg->data[4]));                
			break;
/* Edit wave commands are on hold for now, use the Digital Music interface to edit waves
		case SXM_EDITWAVE:
			SetWaveParam(nProg,nBank,nLayer,nWave,nCmnd):
			break;
*/
	}
	if(nMustInformKernel){
		if(pMsg->program < 128)
			pPLE = GetMelodicPatch(pMsg->program,pMsg->bank);
		else
			pPLE = GetDrumPatch(pMsg->program & 127,pMsg->bank);
		if(pPLE && pPLE->pPatch && pPLE->pPatch->id.p && (pPLE->nStatus & PS_EDITORPATCH))
			iw_change_patch(pPLE->pPatch, (struct iw_patch RFAR *)pPLE->pPatch->id.p);
	}
}

struct IndexEntry{
	unsigned short major_id;
	unsigned short minor_id;
	char RFAR *offset;
	unsigned long size;
	int type;
};           

struct IndexBlock{
	struct IndexBlock RFAR *pNext;
	int nNumEntries;
	struct IndexEntry theEntries[128];  
};  
									  
void AddIDToList(struct IndexBlock RFAR*pBlock,unsigned short maj_id,unsigned short min_id,int nType)
{
	int i;
	HGLOBAL hg;
	
	// first look for it in the existing entries
	for(i=0;i<pBlock->nNumEntries;i++){
		if(pBlock->theEntries[i].major_id == maj_id && pBlock->theEntries[i].minor_id == min_id)
			return;
	}
	while(pBlock->pNext){
		pBlock = pBlock->pNext;
		for(i=0;i<pBlock->nNumEntries;i++){
			if(pBlock->theEntries[i].major_id == maj_id && pBlock->theEntries[i].minor_id == min_id)
				return;
		}
	}       
	
	if(i == 128){                   // this block is full
		hg = GlobalAlloc(GMEM_SHARE,sizeof(struct IndexBlock));
		pBlock->pNext = (struct IndexBlock RFAR *)GlobalLock(hg);
		if(!pBlock->pNext)
			return;
		pBlock = pBlock->pNext;
		pBlock->pNext = NULL;
		pBlock->nNumEntries = 0;
		i = 0;
	}
	
	pBlock->theEntries[i].major_id = maj_id;
	pBlock->theEntries[i].minor_id = min_id;
	pBlock->theEntries[i].type = nType;
	pBlock->nNumEntries++;
}
									  
struct IndexEntry RFAR *GetIndexEntry(struct IndexBlock RFAR*pBlock,unsigned short maj_id,unsigned short min_id)
{
	int i;

	while(pBlock){
		for(i=0;i<pBlock->nNumEntries;i++){
			if(pBlock->theEntries[i].major_id == maj_id && pBlock->theEntries[i].minor_id == min_id)
				return (struct IndexEntry RFAR *)&pBlock->theEntries[i];
		}
		pBlock = pBlock->pNext;
	}       
	return NULL;
}

void ClearIndex(struct IndexBlock RFAR*pBlock)
{                                      
	struct IndexBlock RFAR*pNext;
	HGLOBAL hg;
	
	while(pBlock){
		pNext = pBlock->pNext;
		hg = LOWORD(GlobalHandle(HIWORD(pBlock)));
		GlobalUnlock(hg);
		GlobalFree(hg);
		pBlock = pNext;
	}
}

#define MAXENVPSIZE (sizeof(struct iw_envp) + 16 * (sizeof(struct iw_envp_record) + 32 * 2 * sizeof(unsigned short)))

void LoadEditablePatch(
	int nProg,
	int nBank,
	char far *pFileName)
{
	HFILE hf;
	char File[80];
	struct iw_patch RFAR *pPatch;
	struct iw_layer RFAR *pLayer;
	struct iw_layer RFAR * RFAR *ppLayer;
	struct iw_wave RFAR *pWave;
	struct iw_wave RFAR * RFAR *ppWave;
	struct iw_envp RFAR *pEnvp;
	struct iw_data RFAR *pData;
	struct header file_h, pe_h,RFAR *ph;
	int nLayer,nWave,i;
	unsigned char RFAR*cp;
	unsigned char RFAR*pBuf;
	unsigned char RFAR*pPatchBuf;
	unsigned long pos;
	unsigned long length;
	unsigned long sizeof_patch,sizeof_patch_chunk;
	HGLOBAL hg,hPatch;
	struct IndexBlock firstBlock,RFAR *pBlock;
	struct IndexEntry RFAR *pIE;
	int nFoundThePatch = 0;
	struct PatchListEntry RFAR * pPLE;
		
	// Remove any matching patch from the list
	pPLE = nProg < 128 ? GetMelodicPatch(nProg,nBank) : GetDrumPatch(nProg&127,nBank);
	if(pPLE && pPLE->nBank == nBank){
		if(pPLE->nStatus & PS_EDITORPATCH)
			return;
		UncachePatch(pPLE,nProg&127,nProg<128?1:0);
	}
	
	firstBlock.pNext = NULL;
	firstBlock.nNumEntries = 0;
	
	_fstrcpy(File,pedlib.patch_dir);
	_fstrcat(File,pFileName);
	_fstrcat(File,".FFF");
	
	// load the patch file into a global memory chunk
	hf = _lopen(File, READ|OF_SHARE_COMPAT);
	if (hf == HFILE_ERROR) {
		return;
	}
	if (_lread(hf,&file_h,sizeof(file_h)) == HFILE_ERROR) {
bad_patch:
		_lclose(hf);
		return;
	}
	if (_fstrncmp(file_h.tag, file_header.tag, sizeof(file_h.tag)) != 0) goto bad_patch;
	/* load patch edit chunk */
	if (_lread(hf, &pe_h, sizeof(pe_h)) != sizeof(pe_h)) goto bad_patch;
	if (_fstrncmp(pe_h.tag, pted_header.tag, sizeof(pe_h.tag)) != 0) goto bad_patch;
	if (pe_h.length > 65535L) {
		_lclose(hf);
		return;
	}
	if (_lseek(hf,pe_h.length,SEEK_CUR)==-1) goto bad_patch;
	length = file_h.length - (pe_h.length + sizeof(pe_h));
	hg = GlobalAlloc(GMEM_SHARE,length);
	if (hg) pBuf = (unsigned char RFAR *)GlobalLock(hg);
	if (!hg || pBuf == NULL) {
		_lclose(hf);
		if (hg) GlobalFree(hg);
		return;
	}
	cp = pBuf;
	if (length > 65535L) {
		_lclose(hf);
		GlobalUnlock(hg);
		GlobalFree(hg);
		return;
	}
	if (_lread(hf, cp, (WORD)length) != (WORD)length) {
		_lclose(hf);
		GlobalUnlock(hg);
		GlobalFree(hg);
		return;
	}
	_lclose(hf);
	// Pass One: Find and parse the program chunk
	for (pos=0; pos < length; ) {
		ph = (struct header RFAR*)cp;
		pos += sizeof(struct header);
		cp += sizeof(struct header);
		if (_fstrncmp(ph->tag,program_header.tag,4)==0){
			pos += sizeof(struct iw_program);
			cp += sizeof(struct iw_program);
			pPatch = (struct iw_patch RFAR *)(cp + sizeof(struct header));
			if(pPatch->program == nProg && pPatch->bank == nBank){
				nFoundThePatch = 1;
				// Record the offset of the patch and the size of the chunk
				sizeof_patch_chunk = ph->length - (sizeof(struct iw_program) + sizeof(struct header));
				sizeof_patch = sizeof_patch_chunk;
				// Parse the chunk
				ph = (struct header RFAR*)cp;   // points to the patch chunk
				pos += sizeof(struct header);   // points to the patch structure
				cp += sizeof(struct header);
				cp += ph->length;               // points to the first layer chunk
				pos += ph->length;
				for (nLayer=0; nLayer < pPatch->nlayers; nLayer++) {
					ph = (struct header RFAR *)cp;
					pos += sizeof(struct header);
					cp += sizeof(struct header);
					pLayer = (struct iw_layer RFAR *)cp;
					if (_fstrncmp(ph->tag,layer_header.tag,4)!=0) {
bad_patch1:
						GlobalUnlock(hg);
						GlobalFree(hg);
						return;
					}
					// register any required envelopes
					if(pLayer->penv.id.major_id | pLayer->penv.id.minor_id)
						AddIDToList(&firstBlock,pLayer->penv.id.major_id,pLayer->penv.id.minor_id,1);
					if(pLayer->venv.id.major_id | pLayer->venv.id.minor_id)
						AddIDToList(&firstBlock,pLayer->venv.id.major_id,pLayer->venv.id.minor_id,1);
					cp += ph->length;
					pos += ph->length;
					for(nWave=0; nWave < pLayer->nwaves; nWave++) {
						ph = (struct header RFAR *)cp;
						pos += sizeof(struct header);
						cp += sizeof(struct header);
						pWave = (struct iw_wave RFAR *)cp;
						if(_fstrncmp(ph->tag,wave_header.tag,4)!= 0) goto bad_patch1;
						// replace the data id (data_id) with a pointer
						if(pWave->data_id.id.major_id | pWave->data_id.id.minor_id )
							AddIDToList(&firstBlock,pWave->data_id.id.major_id, pWave->data_id.id.minor_id,0);
						pos += ph->length;
						cp += ph->length;
					}   // for each wave
				}       // for each layer
				break;  // Break out of pass one
			}           // if patch is the required patch
		}               // if it is a patch chunk
		cp += ph->length;
		pos += ph->length;
	}                   // for each chunk
								   
	if(!nFoundThePatch){
		GlobalUnlock(hg);
		GlobalFree(hg);
		return;
	}
												
	// Pass Two: Find the referenced envelope and data chunks
	cp = pBuf;
	for (pos=0; pos < length; ) {
		ph = (struct header RFAR*)cp;
		pos += sizeof(struct header);
		cp += sizeof(struct header);
		if (_fstrncmp(ph->tag,data_header.tag,4)==0){
			pData = (struct iw_data RFAR *)cp;
			if(pIE = GetIndexEntry(&firstBlock,pData->id.id.major_id,pData->id.id.minor_id)){
				pIE->type = 0;
				pIE->size = ph->length;
				pIE->offset = cp;
				sizeof_patch += ph->length;
			}
		}
		else if (_fstrncmp(ph->tag,envp_header.tag,4)==0){
			pEnvp = (struct iw_envp RFAR *)cp;
			if(pIE = GetIndexEntry(&firstBlock,pEnvp->id.id.major_id,pEnvp->id.id.minor_id)){
				pIE->type = 1;
				pIE->size = ph->length;
				pIE->offset = cp;
				sizeof_patch += MAXENVPSIZE;
			}
		}
		cp += ph->length;
		pos += ph->length;
	}                   // for each chunk
											
	// allocate a global, fixed block with enough room for the patch, our copy
	// and the PatchListEntry
	hPatch = GlobalAlloc(GMEM_FIXED|GMEM_SHARE,sizeof_patch*2+sizeof(struct PatchListEntry));
	if(hPatch){
		pPatchBuf = (unsigned char RFAR *)GlobalLock(hPatch);
	}       
	if(!hPatch || !pPatchBuf){
		GlobalFree(hPatch);
		GlobalUnlock(hg);
		GlobalFree(hg);
		return;
	}

	// fill in the PatchListEntry fields
	((struct PatchListEntry RFAR*)pPatchBuf)->nBank = nBank;
	((struct PatchListEntry RFAR*)pPatchBuf)->nStatus = PS_EDITORPATCH;
	((struct PatchListEntry RFAR*)pPatchBuf)->nNoteCount = 0;
	((struct PatchListEntry RFAR*)pPatchBuf)->nChannelCount = 0;
	((struct PatchListEntry RFAR*)pPatchBuf)->ClientFlags = 0;
	((struct PatchListEntry RFAR*)pPatchBuf)->hMem = hPatch;
	((struct PatchListEntry RFAR*)pPatchBuf)->nQNoteOut = 0;
	((struct PatchListEntry RFAR*)pPatchBuf)->nQNoteIn = 0;
	((struct PatchListEntry RFAR*)pPatchBuf)->nQClient = -1;
	((struct PatchListEntry RFAR*)pPatchBuf)->nQChannel = nProg<128?-1:9;
										  
	cp = pPatchBuf + sizeof(struct PatchListEntry);
	((struct PatchListEntry RFAR*)pPatchBuf)->pPatch = (struct iw_patch RFAR *)cp;
	
	// copy the program chunk into the buffer
	_fmemcpy(cp,pPatch,(WORD)sizeof_patch_chunk);
	pPatch = (struct iw_patch RFAR *)cp;
	cp += sizeof_patch_chunk;
	
	// for each envelope and data chunk copy it into the buffer and modify its
	// position field
	pBlock = (struct IndexBlock RFAR *)&firstBlock;
	while(pBlock){
		for(i=0;i<pBlock->nNumEntries;i++){
			pIE = (struct IndexEntry RFAR *)&pBlock->theEntries[i];
			if(pIE->type){
				_fmemcpy(cp,pIE->offset,(WORD)pIE->size);
				pIE->offset = cp;
				cp += MAXENVPSIZE;
			}else{
				_fmemcpy(cp,pIE->offset,(WORD)pIE->size);
				pIE->offset = cp;
				cp += pIE->size;
			}
		}
		pBlock = pBlock->pNext;
	}
	
	// release the patch lib buffer
	GlobalUnlock(hg);
	GlobalFree(hg); 

	((struct PatchListEntry RFAR*)pPatchBuf)->pPatch->id.p = 
		pPatchBuf + sizeof(struct PatchListEntry) + sizeof_patch;
		
	// copy the entire patch section into the editor's copy
	_fmemcpy(pPatchBuf + sizeof(struct PatchListEntry) + sizeof_patch,
			 pPatchBuf + sizeof(struct PatchListEntry), (WORD)sizeof_patch);
	
	cp = pPatchBuf + sizeof(struct PatchListEntry) + sizeof(struct iw_patch);
	// resolve ID's into pointers, mirror these changes in the copy
	ppLayer = &pPatch->layers;
	for (nLayer=0; nLayer < pPatch->nlayers; nLayer++) {
		ph = (struct header RFAR *)cp;
		cp += sizeof(struct header);
		pLayer = (struct iw_layer RFAR *)cp;
		*ppLayer = pLayer;
		*(struct iw_layer RFAR * RFAR *)((char RFAR *)ppLayer + sizeof_patch) = 
			(struct iw_layer RFAR *)((char RFAR *)pLayer + sizeof_patch);
		ppLayer = (struct iw_layer RFAR * RFAR *)&pLayer->id.p;
		ppWave = (struct iw_wave RFAR * RFAR *)&pLayer->waves;
		// register any required envelopes
		if(pLayer->penv.id.major_id | pLayer->penv.id.minor_id){
			if(pIE = GetIndexEntry(&firstBlock,pLayer->penv.id.major_id,pLayer->penv.id.minor_id)){
				pLayer->penv.p = pIE->offset;
				*((struct iw_envp RFAR * RFAR *)((char RFAR *)&pLayer->penv.p + sizeof_patch)) = (*(struct iw_envp RFAR * RFAR *)(pIE->offset + sizeof_patch));
			}else{
				pLayer->penv.p = NULL;
			}
		}
		if(pLayer->venv.id.major_id | pLayer->venv.id.minor_id){
			if(pIE = GetIndexEntry(&firstBlock,pLayer->venv.id.major_id,pLayer->venv.id.minor_id)){
				pLayer->venv.p = pIE->offset;
				*(struct iw_envp RFAR * RFAR *)((char RFAR *)&pLayer->venv.p + sizeof_patch) = 
					(struct iw_envp RFAR *)(pIE->offset + sizeof_patch);
			}else{
				pLayer->venv.p = NULL;
			}
		}
		cp += ph->length;
		for(nWave=0; nWave < pLayer->nwaves; nWave++) {
			ph = (struct header RFAR *)cp;
			cp += sizeof(struct header);
			pWave = (struct iw_wave RFAR *)cp;
			*ppWave = pWave;
			*(struct iw_wave RFAR * RFAR *)((char RFAR *)ppWave + sizeof_patch) = 
				(struct iw_wave RFAR *)((char RFAR *)pWave + sizeof_patch);
			ppWave = (struct iw_wave RFAR * RFAR *)&pWave->id.p;
			pWave->m_format = pWave->format;
			pWave->m_start = NULL;
			
			if(_fstrncmp(ph->tag,wave_header.tag,4)!= 0) goto bad_patch1;
			// replace the data id (data_id) with a pointer
			if(pWave->data_id.id.major_id | pWave->data_id.id.minor_id ){
				if(pIE = GetIndexEntry(&firstBlock,pWave->data_id.id.major_id,pWave->data_id.id.minor_id)){
					pWave->data_id.p = pIE->offset;
					*(struct iw_data RFAR * RFAR *)((char RFAR *)&pWave->data_id.p + sizeof_patch) =
						(struct iw_data RFAR *)(pIE->offset + sizeof_patch);
				}else{
					pWave->data_id.p = NULL;
				}
			}
			cp += ph->length;
		}   // for each wave
	}       // for each layer
	ClearIndex(firstBlock.pNext);      
	
	// Add the patch to the patch list
	if(nProg < 128){
		((struct PatchListEntry RFAR*)pPatchBuf)->pNext = MelodicPatchList[nProg];
		MelodicPatchList[nProg] = (struct PatchListEntry RFAR*)pPatchBuf;
	}else{
		((struct PatchListEntry RFAR*)pPatchBuf)->pNext = DrumPatchList[nProg&127];
		MelodicPatchList[nProg&127] = (struct PatchListEntry RFAR*)pPatchBuf;
	}       
}

void UnLoadEditablePatch(
	int nProg,
	int nBank)
{
	int nClient,nChan;
	HGLOBAL hg;
	struct PatchListEntry RFAR *pPLE;
	struct PatchListEntry RFAR *pPrev=NULL;
	
	// Find the patch list entry
	if(nProg < 128){
		for(pPLE = MelodicPatchList[nProg];pPLE;pPrev = pPLE,pPLE = pPLE->pNext){
			if(pPLE->nBank == nBank && (pPLE->nStatus & PS_EDITORPATCH))
				break;
		}
	}else{
		for(pPLE = DrumPatchList[nProg&127];pPLE;pPrev = pPLE,pPLE = pPLE->pNext){
			if(pPLE->nBank == nBank && (pPLE->nStatus & PS_EDITORPATCH))
				break;
		}
	}
	if(pPLE){
		if(pPLE->nChannelCount){
			for(nClient = 0;nClient < MAXMIDICLIENTS;nClient++)
				for(nChan = 0;nChan < 16; nChan++)
					if(MidiClients[nClient].CurPatch[nChan]==pPLE)
						MidiClients[nClient].CurPatch[nChan]=NULL;
		}
		// If necessary unload it from memory
		if(pPLE->nStatus & PS_LOADED)
			UnloadProgramPatch(pPLE->pPatch);
		// Remove it from the patch list
		if(pPrev)
			pPrev->pNext = pPLE->pNext;
		else if(nProg<128)
			MelodicPatchList[nProg] = pPLE->pNext;
		else
			DrumPatchList[nProg&127] = pPLE->pNext;
	
		// Release the global memory block, this also releases the patch lib memory
		hg = pPLE->hMem;
		GlobalUnlock(hg);
		GlobalFree(hg);
	}
}

struct iw_patch RFAR *FindPatch(
	int nProg,
	int nBank)
{
	struct PatchListEntry RFAR *pPLE;
	
	if(nProg < 128)
		pPLE = GetMelodicPatch(nProg,nBank);
	else
		pPLE = GetDrumPatch(nProg & 127,nBank);
	if( !pPLE || pPLE->nBank != nBank || !(pPLE->nStatus & PS_EDITORPATCH))
		{
#ifdef DEBUG
		OutputDebugStr("couldn't find patch ");
#endif
		return NULL;
		}
	return pPLE->pPatch->id.p;  // the patch itself belongs to the kernel we will
								// edit the copy pointed to by id.p
}

int SetPatchParam(
	int nProg,
	int nBank,
	unsigned char nCmnd,
	unsigned char nData)
{
	struct iw_patch RFAR *pPatch;
								
	pPatch = FindPatch(nProg,nBank);
	if(!pPatch)
		{
#ifdef DEBUG
		OutputDebugStr("\r\n");         // FindPatch() already printed "couldn't find patch "
#endif
		return 0;
		}
	switch(nCmnd){
		case SXMP_LAYERMODE:
#ifdef DEBUG
			OutputDebugStr("Layer Mode\r\n");
#endif
			pPatch->layer_mode = nData;
			break;
		case SXMP_EXCLUSIONMODE:
#ifdef DEBUG
			OutputDebugStr("Exclusion Mode\r\n");
#endif
			pPatch->exclusion_mode = nData;
			break;
		case SXMP_EXCLUSIONGROUP:
#ifdef DEBUG
			OutputDebugStr("Exclusion Group\r\n");
#endif
			pPatch->exclusion_group = nData;
			break;
		case SXMP_EFFECTONE:
#ifdef DEBUG
			OutputDebugStr("Effect One\r\n");
#endif
			pPatch->effect1 = nData;
			break;
		case SXMP_EFFECTONEDEPTH:
#ifdef DEBUG
			OutputDebugStr("Effect One Depth\r\n");
#endif
			pPatch->effect1_depth = nData;
			break;
		case SXMP_EFFECTTWO:
#ifdef DEBUG
			OutputDebugStr("Effect Two\r\n");
#endif
			pPatch->effect2 = nData;
			break;
		case SXMP_EFFECTTWODEPTH:
#ifdef DEBUG
			OutputDebugStr("Effect Two Depth\r\n");
#endif
			pPatch->effect2_depth = nData;
			break;
	}            
	return 1;
}

struct iw_layer RFAR *FindLayer(
	struct iw_patch RFAR *pPatch,
	int nLayer)
{
	struct iw_layer RFAR *pLayer;
	int i;
	
	if(!pPatch)
		return NULL;
		
	pLayer = pPatch->layers;
	for(i=0;i<nLayer;i++){
		if(!pLayer)
			break;
		pLayer = pLayer->id.p;
	}
	return pLayer;
}

int SetLayerParam(
	int nProg,
	int nBank,
	int nLayer,
	unsigned char nCmnd,
	int nData)
{
	struct iw_patch RFAR *pPatch;
	struct iw_layer RFAR *pLayer;
		
	pPatch = FindPatch(nProg,nBank);
	pLayer = FindLayer(pPatch,nLayer);
	if(!pLayer)
		{
#ifdef DEBUG
		OutputDebugStr("couldn't find layer\r\n");
#endif
		return 0;
		}
		
	switch(nCmnd){        
		case SXML_HIGHRANGE:
#ifdef DEBUG
			OutputDebugStr("High Range\r\n");
#endif
			pLayer->high_range = nData;
			break;
		case SXML_LOWRANGE:
#ifdef DEBUG
			OutputDebugStr("Low Range\r\n");
#endif
			pLayer->low_range = nData;
			break;
		case SXML_PANOFFSET:
#ifdef DEBUG
			OutputDebugStr("Pan Offset\r\n");
#endif
			pLayer->pan = nData;
			break;
		case SXML_PANFREQ:
#ifdef DEBUG
			OutputDebugStr("Pan Frequency\r\n");
#endif
			pLayer->pan_freq_scale = nData;
			break;
		case SXML_TREMDEPTH:
#ifdef DEBUG
			OutputDebugStr("Tremolo Depth\r\n");
#endif
			pLayer->tremolo.depth = nData;
			break;
		case SXML_TREMRATE:
#ifdef DEBUG
			OutputDebugStr("Tremolo Rate\r\n");
#endif
			pLayer->tremolo.freq = nData;
			break;
		case SXML_TREMSWEEP:
#ifdef DEBUG
			OutputDebugStr("Tremolo Sweep\r\n");
#endif
			pLayer->tremolo.sweep = nData;
			break;
		case SXML_TREMDELAY:
#ifdef DEBUG
			OutputDebugStr("Tremolo Delay\r\n");
#endif
			pLayer->tremolo.delay = nData;
			break;
		case SXML_TREMSHAPE:              
#ifdef DEBUG
			OutputDebugStr("Tremolo Shape\r\n");
#endif
			pLayer->tremolo.shape = nData;
			break;
		case SXML_VIBDEPTH:
#ifdef DEBUG
			OutputDebugStr("Vibrato Depth\r\n");
#endif
			pLayer->vibrato.depth = nData;
			break;
		case SXML_VIBRATE:
#ifdef DEBUG
			OutputDebugStr("Vibrato Rate\r\n");
#endif
			pLayer->vibrato.freq = nData;
			break;
		case SXML_VIBSWEEP:
#ifdef DEBUG
			OutputDebugStr("Vibrato Sweep\r\n");
#endif
			pLayer->vibrato.sweep = nData;
			break;
		case SXML_VIBDELAY:
#ifdef DEBUG
			OutputDebugStr("Vibrato Delay\r\n");
#endif
			pLayer->vibrato.delay = nData;
			break;
		case SXML_VIBSHAPE:
#ifdef DEBUG
			OutputDebugStr("Vibrato Shape\r\n");
#endif
			pLayer->vibrato.shape = nData;
			break;
		case SXML_VELMODE: 
#ifdef DEBUG
			OutputDebugStr("Velocity Mode\r\n");
#endif
			pLayer->velocity_mode = nData;
			break;
		case SXML_ATTEN:
#ifdef DEBUG
			OutputDebugStr("Attenuation\r\n");
#endif
			pLayer->attenuation = nData;
			break;
		case SXML_FREQSCALE:
#ifdef DEBUG
			OutputDebugStr("Frequency Scale\r\n");
#endif
			pLayer->freq_scale = nData;
			break;
		case SXML_FREQCENTER:
#ifdef DEBUG
			OutputDebugStr("Frequency Center\r\n");
#endif
			pLayer->freq_center = nData;
			break;
		case SXML_LAYEREVENT:
#ifdef DEBUG
			OutputDebugStr("Layer Event\r\n");
#endif
			pLayer->layer_event = nData;
			break;
	}            
	return 1;
}


/*  

DESCRIPTION
Returns a pointer to a specific envelope record given the envelope header and the index of 
the required record.  This will work for records 0 through num_envelopes (one past the last
record).
*/
struct iw_envp_record RFAR *FindEnvelopeRecord(
	struct iw_envp_header RFAR *pEHdr,
	int nRec)
{
	struct iw_envp_record RFAR *pRec;
	int i=0,nPoints;
	
	pRec = (struct iw_envp_record RFAR *)((char RFAR *)pEHdr + sizeof(struct iw_envp_header));
	while(i<nRec){
		nPoints = pRec->nattack + pRec->nrelease;
		pRec = (struct iw_envp_record RFAR *)((char RFAR *)pRec + sizeof(struct iw_envp_record) +
				4 * nPoints);
		i++;
	}
	return pRec;
}


int SetEnvelopeParam(
	int nProg,
	int nBank,
	int nLayer,
	int nEnv,
	int nType,
	unsigned char nCmnd,
	int nData)
{
	struct iw_patch RFAR *pPatch;
	struct iw_layer RFAR *pLayer;
	struct iw_envp RFAR *pEnvp;
	struct iw_envp_header RFAR *pEHdr;
	struct iw_envp_record RFAR *pERec;
	unsigned short RFAR *pVar;
	unsigned short RFAR *pEndOfData;
	char RFAR *pFrom;
	char RFAR *pTo;
	int nPoint;
	int result = 0;
	
	// First resolve the pointers to the envelope and the envelope's header
	pPatch = FindPatch(nProg,nBank);
	pLayer = FindLayer(pPatch,nLayer);
	if(!pLayer)
		{
#ifdef DEBUG
		OutputDebugStr("couldn't find layer\r\n");
#endif
		return 0;
		}
		
	pEnvp = nType?pLayer->penv.p:pLayer->venv.p;

	if(!pEnvp)
		{
#ifdef DEBUG
		if(nType)
			OutputDebugStr("no pitch envelope\r\n");
		else
			OutputDebugStr("no volume envelope\r\n");
#endif
		return 0;
		}
		
	pEHdr = &pEnvp->h;
	
	if(nCmnd > SXME_NUMENV){            // Find the correct envelope and its variable data
		pERec = FindEnvelopeRecord(pEHdr,nEnv);
		pVar = (unsigned short RFAR *)((char RFAR *)pERec + sizeof(struct iw_envp_record));
	}
	
	switch(nCmnd){
		case SXME_MODE:
#ifdef DEBUG
			OutputDebugStr("mode\r\n");
#endif
			pEHdr->mode = nData;
			result = 1;
			break;
		case SXME_INDEX:
#ifdef DEBUG
			OutputDebugStr("index\r\n");
#endif
			pEHdr->index_type = nData;
			result = 1;
			break;
		case SXME_NUMENV:               
#ifdef DEBUG
			OutputDebugStr("number of envelopes\r\n");
#endif
			// If decreasing just reduce the number
			if(nData < pEHdr->num_envelopes){
				pEHdr->num_envelopes = nData;
				result = 1;
			}               
			// If increasing add envelope records to the end
			else if(nData > pEHdr->num_envelopes){
				if(nData > 16)  // Don't allow more than 16
					break;
				for(nEnv=pEHdr->num_envelopes;nEnv<nData;nEnv++){
					pERec = FindEnvelopeRecord(pEHdr,nEnv);
					pERec->nattack = 0;
					pERec->nrelease = 0;
					pERec->hirange = 0;
				}       
				result = 1;
			}
			break;
		case SXME_HIRANGE:
#ifdef DEBUG
			OutputDebugStr("high range\r\n");
#endif
			pERec->hirange = nData;
			result = 1;
			break;
		case SXME_SOFFSET:
#ifdef DEBUG
			OutputDebugStr("sustain offset\r\n");
#endif
			pERec->sustain_offset = nData;
			result = 1;
			break;
		case SXME_SRATE:
#ifdef DEBUG
			OutputDebugStr("sustain rate\r\n");
#endif
			pERec->sustain_rate = nData;
			result = 1;
			break;
		case SXME_RRATE:
#ifdef DEBUG
			OutputDebugStr("release rate\r\n");
#endif
			pERec->release_rate = nData;
			result = 1;
			break;
		case SXME_NATTACK:              // This changes the size of the envelopes section
#ifdef DEBUG
			OutputDebugStr("number of attack points\r\n");
#endif
			pFrom = (char RFAR *)&pVar[pERec->nattack * 2];
			pTo = (char RFAR *)&pVar[nData * 2];
			pERec->nattack = nData;
		case SXME_NRELEASE:
#ifdef DEBUG
			OutputDebugStr("number of release points\r\n");
#endif
			if(nData > 16)
				break;
			if(nCmnd==SXME_NRELEASE){
				pFrom = (char RFAR *)&pVar[(pERec->nattack + pERec->nrelease)* 2];
				pTo = (char RFAR *)&pVar[(pERec->nattack + nData)* 2];
				pERec->nrelease = nData;
			}
			if(pFrom == pTo)
				break;
			pERec = FindEnvelopeRecord(pEHdr,pEHdr->num_envelopes);
			_fmemmove(pTo,pFrom,(char RFAR *)pERec - pFrom);
			result = 1;
			break;
		default:
#ifdef DEBUG
			OutputDebugStr("set point parameter\r\n");
#endif
			if(nCmnd < 0x10)
				break;
			nPoint = (nCmnd - 0x10)>>1;
			if(nPoint > 32)
				break;
			pVar[nCmnd - 0x10] = nData;
			result = 1;
			break;
	}   
	return result;
}
