//************************************************************************
//**
//**  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//**  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
//**  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
//**  A PARTICULAR PURPOSE.
//**
//**  Copyright (C) 1993, 1994 Microsoft Corporation. All Rights Reserved.
//**
//**  file.c
//**
//**  DESCRIPTION:
//**
//**
//**  HISTORY:
//**     06/14/93          created.
//**
//************************************************************************

#include "globals.h"
#include <commdlg.h>
#include <string.h>
#include "res.h"


//************************************************************************
//**
//**  AppGetFileName();
//**
//**  DESCRIPTION:
//**     This function is a wrapper for the Get[Open/Save]FileName commdlg
//**     chooser dialogs. Based on the fuFlags argument, this function 
//**     will display the appropriate chooser dialog and return the result.
//**
//**  ARGUMENTS:
//**     HWND    hwnd          - Handle to parent window.
//**     LPSTR   lpszFilePath  - Pointer to the buffer to receive the
//**                             the file path.
//**     LPSTR   lpszFileTitle - Pointer to the buffer to receive the 
//**                             file the file title, NULL if no title 
//**                             is wanted.
//**     BOOL     fSave        - TRUE if we are to save a file,
//**                             FALSE if we are to open the file.
//**
//**  RETURNS:
//**     BOOL  -  TRUE if a file was chosen. FALSE if the user canceled 
//**              the operation.
//**
//**  HISTORY:
//**     05/05/93       modified.
//**
//************************************************************************

BOOL FNLOCAL AppGetFileName(
    HWND    hwnd,
    LPSTR   lpszFilePath,
    LPSTR   lpszFileTitle,
    BOOL    fSave)
{
   OPENFILENAME   ofn;
   char           szExtDefault[4];
   char           szExtFilter[256];
   LPSTR          pstr;
   BOOL           f;

   //  Get the extension filter and default extension.
   //
   LoadString(ghinst, IDS_OFN_EXT_DEF, szExtDefault, sizeof(szExtDefault));
   LoadString(ghinst, IDS_OFN_EXT_FILTER, szExtFilter, sizeof(szExtFilter));

   // Parse the bang out of each string,
   // replace with a NULL.
   //
   for (pstr = szExtFilter; 
        '#' != *pstr;)
   {
      // Is it equal to a bang?
      //
      if ('!' == *pstr)
      {
         // Replace with a NULL.
         //
         *pstr = '\0';
         pstr++;
      }
      else
        pstr = AnsiNext(pstr);
   }

   // Reset the file's path.
   //
   lpszFilePath[0] = '\0';

   // If there is a title then reset it also.
   //
   if (lpszFileTitle)
      lpszFileTitle[0] = '\0';

   //  Initialize the OPENFILENAME structure elements.
   //
   memset(&ofn, 0, sizeof(ofn));

   ofn.lStructSize      =  sizeof(OPENFILENAME);
   ofn.hwndOwner        =  hwnd;
   ofn.lpstrFilter      =  szExtFilter;
   ofn.nFilterIndex     =  1L;
   ofn.lpstrFile        =  lpszFilePath;
   ofn.nMaxFile         =  MAX_PATH_LEN;
   ofn.lpstrFileTitle   =  lpszFileTitle;
   ofn.nMaxFileTitle    =  lpszFileTitle ? MAX_TITLE_LEN : 0;
   ofn.lpstrDefExt      =  szExtDefault;

   //  If the fSave is TRUE, then call GetSaveFileName()
   //  otherwise call GetOpenFileName().
   //
   if (fSave)
   {
      // Set the OPENFILENAME flags to save and prompt if we
      // will overwrite an existing file.
      //
      ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;

      // Call the common dialog box function for saving a file.
      //
      f = GetSaveFileName(&ofn);
   }
   else
   {
      // Set the OPENFILENAME flags to open and the file 
      // must exist if we are opening.
      //
      ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;

      // Call the common dialog box function for opening a file.
      //
      f = GetOpenFileName(&ofn);
   }

   return (f);
} //** AppGetFileName()


//************************************************************************
//**
//**  WriteHeaderChunk();
//**
//**  DESCRIPTION:
//**     This function will write the mif header to the file.
//**
//**  ARGUMENTS:
//**     DWORD cInst   -  The instrument number to write.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful.
//**                 Otherwise it will return an error code.
//**
//**  HISTORY:
//**     04/29/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL WriteHeaderChunk(
   DWORD cInst)
{
   MMRESULT    mmr;
   LPMIFHEADER pmifHeader;
   MMCKINFO    chkSub;
   LPSTR       psz;
   DWORD       c;
   LONG        l;

   // Make the unique id for this instrument.
   //
   psz = (LPSTR)gpInst[cInst].pszProductASCII;

   // Store the product name with out spaces as the 
   // instrument's unique identifier.
   //
   c = 0;

   while (('\0' != *psz) && (c <= MAX_STR_LEN))
   {
      // If it's not a space then place it in the buffer.
      //
      if (*psz != ' ')
         gszbuf[c++] = *psz;

      // Get the next character.
      //
      psz++;
   }

   // NULL terminate it.
   //
   gszbuf[c++] = '\0';

   // Allocate memory for the MIF header.
   //
   pmifHeader= AllocPtr(GHND, sizeof(MIFHEADER) + c);
   if (NULL == pmifHeader)
      return(MIFERR_NOMEM);

   // Fill the buffer with the MIFHEADER info.
   //
   pmifHeader->cbStruct  = sizeof(MIFHEADER) + c;
   pmifHeader->dwVersion = gpInst[cInst].dwVersion;
   pmifHeader->dwCreator = gpInst[cInst].dwCreator;
   pmifHeader->cbInstID  = c;
   lstrcpy((LPSTR)pmifHeader->abInstID, gszbuf);
   

   // Setup the "hdr " chunk information.
   //
   chkSub.ckid = mmioFOURCC('h', 'd', 'r', ' ');
   chkSub.cksize = 0;
   
   //  Creating "fmt " Chunk in Destination File
   //
   mmr = mmioCreateChunk(ghmmio, &chkSub, 0);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Error could not create header chunk.
      //
      FreePtr(&pmifHeader);
      return(MIFERR_CANNOTCREATECHUNK);
   }
   
   // Write the "hdr " data to the file.
   //
   l = mmioWrite(ghmmio, (HPSTR)pmifHeader, pmifHeader->cbStruct);
   if (pmifHeader->cbStruct != (DWORD)l)
   {
      // Error didn't write enough bytes.
      //
      FreePtr(&pmifHeader);
      return(MIFERR_BADWRITE);
   }

   // Ascend out of the new chunk to fix up it's size.
   //
   mmioAscend(ghmmio, &chkSub, 0);

   // Free the MIFHEADER that we allocated.
   //
   FreePtr(&pmifHeader);

   return(MMSYSERR_NOERROR);
} //** WriteHeaderChunk()


//************************************************************************
//**
//**  WriteInstChunk();
//**
//**  DESCRIPTION:
//**     This function will write the instrument info to the file.
//**
//**  ARGUMENTS:
//**     DWORD cInst   -  The instrument number to write.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful.
//**                 Otherwise it will return an error code.
//**
//**  HISTORY:
//**     04/29/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL WriteInstChunk(
   DWORD cInst)
{
   MMRESULT       mmr;
   LPMIFINSTINFO  pmifInstrumentInfo;
   MMCKINFO       chkSub;
   DWORD          cbInstrumentChunk;
   LONG           l;

   // Determine the size that the MIFINSTINFO will need to be.
   // We add 1 to each string length since we need to include
   // the NULL in our count.
   //
   cbInstrumentChunk = sizeof(MIFINSTINFO) +
                       lstrlen(gpInst[cInst].pszManufactASCII) + 1 +
                       lstrlen(gpInst[cInst].pszManufactUNICODE) + 1 +
                       lstrlen(gpInst[cInst].pszProductASCII) + 1 +
                       lstrlen(gpInst[cInst].pszProductUNICODE) + 1;

   // Allocate memory for the instrument information.
   //
   pmifInstrumentInfo = AllocPtr(GHND, cbInstrumentChunk);
   if (NULL == pmifInstrumentInfo)
      return(MIFERR_NOMEM);

   // Build the mif inst info.
   //
   pmifInstrumentInfo->cbStruct         = cbInstrumentChunk;
   pmifInstrumentInfo->dwManufactID     = gpInst[cInst].dwManufactID;
   pmifInstrumentInfo->dwProductID      = gpInst[cInst].dwProductID;
   pmifInstrumentInfo->dwRevision       = gpInst[cInst].dwRevision;
   pmifInstrumentInfo->cbManufactASCII  = lstrlen(gpInst[cInst].pszManufactASCII) + 1;
   // Commented out since string aren't being filled in 
   //pmifInstrumentInfo->cbManufactUNICODE  = lstrlen(gpInst[cInst].pszManufactUNICODE) + 1;
   pmifInstrumentInfo->cbProductASCII   = lstrlen(gpInst[cInst].pszProductASCII) + 1;
   // Commented out since string aren't being filled in 
   //pmifInstrumentInfo->cbProductUNICODE   = lstrlen(gpInst[cInst].pszProductUNICODE) + 1;

   // Copy the manufacturer and product names into the buffer.
   //
   lstrcpy((LPSTR)pmifInstrumentInfo->abData, gpInst[cInst].pszManufactASCII);
   lstrcpy((LPSTR)&pmifInstrumentInfo->abData[pmifInstrumentInfo->cbManufactASCII], 
           gpInst[cInst].pszManufactUNICODE);

   lstrcpy((LPSTR)&pmifInstrumentInfo->abData[pmifInstrumentInfo->cbManufactASCII + pmifInstrumentInfo->cbManufactUNICODE], 
           gpInst[cInst].pszProductASCII);

   lstrcpy((LPSTR)&pmifInstrumentInfo->abData[pmifInstrumentInfo->cbManufactASCII + pmifInstrumentInfo->cbManufactUNICODE + pmifInstrumentInfo->cbProductASCII], 
           gpInst[cInst].pszProductUNICODE);

   // Setup the "inst" chunk information.
   //
   chkSub.ckid = mmioFOURCC('i', 'n', 's', 't');
   chkSub.cksize = 0L;
   
   //  Creating a "inst" chunk in file.
   //
   mmr = mmioCreateChunk(ghmmio, &chkSub, 0);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Error could not create instrument chunk.
      //
      FreePtr(&pmifInstrumentInfo);
      return(MIFERR_CANNOTCREATECHUNK);
   }
   
   // Write "inst" the data to the file.
   //
   l = mmioWrite(ghmmio, 
                 (HPSTR)pmifInstrumentInfo, 
                 pmifInstrumentInfo->cbStruct);
   if (pmifInstrumentInfo->cbStruct != (DWORD)l)
   {
      // Error didn't write enough bytes.
      //
      FreePtr(&pmifInstrumentInfo);
      return(MIFERR_BADWRITE);
   }

   // Ascend out of the new chunk to fix up it's size.
   //
   mmioAscend(ghmmio, &chkSub, 0);

   // Free the MIFINSTINFO that we allocated.
   //
   FreePtr(&pmifInstrumentInfo);

   return(MMSYSERR_NOERROR);
} //** WriteInstChunk()


//************************************************************************
//**
//**  WriteCapsChunk();
//**
//**  DESCRIPTION:
//**     This function will write the instrument capabilities to the file.
//**
//**  ARGUMENTS:
//**     DWORD cInst   -  The instrument number to write.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful.
//**                 Otherwise it will return an error code.
//**
//**  HISTORY:
//**     04/29/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL WriteCapsChunk(
   DWORD cInst)
{
   MMRESULT       mmr;
   MIFINSTCAPS    mifInstrumentCaps;
   MMCKINFO       chkSub;
   DWORD          dw;

   // Fill the structure to write to the file.
   //
   mifInstrumentCaps.cbStruct              = sizeof(mifInstrumentCaps);
   mifInstrumentCaps.fdwFlags              = gpInst[cInst].fdwFlags;
   mifInstrumentCaps.dwBasicChannel        = gpInst[cInst].dwBasicChannel;
   mifInstrumentCaps.cNumChannels          = gpInst[cInst].cNumChannels;
   mifInstrumentCaps.cInstrumentPolyphony  = gpInst[cInst].cInstPoly;
   mifInstrumentCaps.cChannelPolyphony     = gpInst[cInst].cChannelPoly;

   // Set up the "caps" chunk information.
   //
   chkSub.ckid = mmioFOURCC('c', 'a', 'p', 's');
   chkSub.cksize = 0;
   
   //  Creating a "inst" chunk in file.
   //
   mmr = mmioCreateChunk(ghmmio, &chkSub, 0);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Error could not create instrument chunk.
      //
      return(MIFERR_CANNOTCREATECHUNK);
   }

   // Write the "caps" data to the file.
   //
   dw = mmioWrite(ghmmio, 
                  (HPSTR)&mifInstrumentCaps, 
                  mifInstrumentCaps.cbStruct);
   if (mifInstrumentCaps.cbStruct != dw)
   {
      // Error didn't write enough bytes.
      //
      return(MIFERR_BADWRITE);
   }

   // Fix the chunk size.
   //
   mmioAscend(ghmmio, &chkSub, 0);

   return(MMSYSERR_NOERROR);
} //** WriteCapsChunk()


//************************************************************************
//**
//**  WriteChannelChunk();
//**
//**  DESCRIPTION:
//**     This function will write the channel type info to the file.
//**
//**  ARGUMENTS:
//**     DWORD cInst   -  The instrument number to write.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful.
//**                 Otherwise it will return an error code.
//**
//**  HISTORY:
//**     04/29/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL WriteChannelChunk(
   DWORD cInst)
{
   MMRESULT          mmr;
   MIFCHANNELHDR     mifChannelHeader;
   LPDWORD           pdwChannelTypes;
   MIFCHANNELINFO    mifChannelInfo;
   MMCKINFO          chkSub;
   DWORD             c;
   DWORD             cChannel;
   LONG              l;

   // Initialize structures.
   //
   _fmemset(&mifChannelHeader, 0, sizeof(mifChannelHeader));
   _fmemset(&chkSub, 0, sizeof(chkSub));

   // Set the size of the channel map info.
   //
   mifChannelHeader.cbChannelSize = sizeof(MIFCHANNELINFO);

   // Set the size of the channel header.
   //
   mifChannelHeader.cbStruct = sizeof(MIFCHANNELHDR);

   // Map any undefined channels to GM.
   //
   mifChannelHeader.fdwFlags |= MIFCHANNELHDR_F_GENERAL_MIDI;

   // Get a nice pointer to the channel info.
   //
   pdwChannelTypes = (LPDWORD)gpInst[cInst].aChannelTypes;

   // How many non-GM channels do we have?
   //
   for ( c = 0;
         c < MAX_CHANNELS;
         c++)
   {
      // Is this a drum channel?
      //
      if (DRUM_CHANNEL == c)
      {
         // Is the channel type General MIDI?
         //
         if (MIFCHANNELINFO_DRUM_CHANNEL != pdwChannelTypes[c])
            mifChannelHeader.cNumChannels++;
      }
      else
      {
         // Is the channel type General MIDI?
         //
         if (MIFCHANNELINFO_GENERAL_CHANNEL != pdwChannelTypes[c])
            mifChannelHeader.cNumChannels++;
      }
   }

   // Setup the "chnl" chunk information.
   //
   chkSub.ckid = mmioFOURCC('c', 'h', 'n', 'l');
   
   //  Creating a "chnl" chunk in file.
   //
   mmr = mmioCreateChunk(ghmmio, &chkSub, 0);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Error could not create instrument chunk.
      //
      return(MIFERR_CANNOTCREATECHUNK);
   }
   
   // Write the header to the file.
   //
   l = mmioWrite(ghmmio, (HPSTR)&mifChannelHeader, mifChannelHeader.cbStruct);
   if (mifChannelHeader.cbStruct != (DWORD)l)
   {
      // Error didn't write enough bytes.
      //
      return(MIFERR_BADWRITE);
   }

   // Set the size for the channel info,
   // it will be the same for all channels.
   //
   mifChannelInfo.cbStruct = sizeof(MIFCHANNELINFO);

   // Write the channel information to the file.
   // IFF the channel type is not GM.
   //
   for ( c = 0, cChannel = mifChannelHeader.cNumChannels;
         (cChannel > 0) && (c < MAX_CHANNELS); 
         c++, cChannel--)
   {
      // Special case the drum channel.
      //
      if (DRUM_CHANNEL == c)
      {
         // OK, now is it really the drum channel?
         //
         if (MIFCHANNELINFO_DRUM_CHANNEL != pdwChannelTypes[c])
         {
            // It's not a General MIDI channel so write it's info
            // to the MIF.
            //
            mifChannelInfo.dwChannel      = c;
            mifChannelInfo.dwChannelType  = pdwChannelTypes[c];

            // Write the new channel info to the MIF.
            //
            l = mmioWrite(ghmmio, 
                          (HPSTR)&mifChannelInfo, 
                          mifChannelInfo.cbStruct);
            if (mifChannelInfo.cbStruct != (DWORD)l)
               return(MIFERR_BADWRITE);
         }
      }
      else
      {
         // Is this a General channel?
         //
         if (MIFCHANNELINFO_GENERAL_CHANNEL != pdwChannelTypes[c])
         {
            // It's NOT a general channel so write it's
            // info to the MIF.
            //
            mifChannelInfo.dwChannel      = c;
            mifChannelInfo.dwChannelType  = pdwChannelTypes[c];

            // Write the new channel info to the MIF.
            //
            l = mmioWrite(ghmmio, 
                          (HPSTR)&mifChannelInfo, 
                          mifChannelInfo.cbStruct);
            if (mifChannelInfo.cbStruct != (DWORD)l)
               return(MIFERR_BADWRITE);
         }
      }
   }

   // Ascend the chunk to correct the size info.
   //
   mmioAscend(ghmmio, &chkSub, NULL);

   return(MMSYSERR_NOERROR);
} //** WriteChannelChunk()


//************************************************************************
//**
//**  WritePatchMapChunk();
//**
//**  DESCRIPTION:
//**     This function will write the patch maps to the file.
//**
//**  ARGUMENTS:
//**     DWORD cInst   -  The instrument number to write.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful.
//**                 Otherwise it will return an error code.
//**
//**  HISTORY:
//**     04/29/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL WritePatchMapChunk(
   DWORD cInst)
{
   MMRESULT          mmr;
   MIFPATCHMAPHDR    mifPatchMapHeader;
   MIFMAPENTRY       mifPatchMap;
   LPBYTE            pbPatchMap;
   MMCKINFO          chkSub;
   UINT              c;
   DWORD             cPatchMaps;
   LONG              l;

   // Get a nice pointer to the patch map information.
   //
   pbPatchMap = gpInst[cInst].aPatchMaps;

   // Initialize the structures.
   //
   _fmemset(&mifPatchMapHeader, 0, sizeof(mifPatchMapHeader));
   _fmemset(&chkSub, 0, sizeof(chkSub));

   // Set the size of the patch map header.
   //
   mifPatchMapHeader.cbStruct = sizeof(mifPatchMapHeader);

   // Patches that are not defined are GM.
   //
   mifPatchMapHeader.fdwFlags |= MIFPATCHMAPHDR_F_GENERAL_MIDI;

   // Now determine the number of patch maps that
   // are NOT General MIDI.
   //
   for ( c = 0; 
         c < MAX_PATCHES; 
         c++)
   {
      // If the patch is not GM then we will need
      // to save it later.
      //
      if (pbPatchMap[c] != c)
         mifPatchMapHeader.cNumPatchMaps++;
   }

   // Setup the "pmap" chunk information.
   //
   chkSub.ckid = mmioFOURCC('p', 'm', 'a', 'p');

   //  Creating a "inst" chunk in file.
   //
   mmr = mmioCreateChunk(ghmmio, &chkSub, 0);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Error could not create instrument chunk.
      //
      return(MIFERR_CANNOTCREATECHUNK);
   }
   
   // Write the header to the file.
   //
   l = mmioWrite(ghmmio, (HPSTR)&mifPatchMapHeader, mifPatchMapHeader.cbStruct);
   if (mifPatchMapHeader.cbStruct != (DWORD)l)
   {
      // Error didn't write enough bytes.
      //
      return(MIFERR_BADWRITE);
   }

   // Save the size of the patch map information.
   //
   mifPatchMap.cbStruct = sizeof(mifPatchMap);

   // Write the channel information to the file.
   // iff the channel type is not general midi.
   //
   for ( c = 0, cPatchMaps = mifPatchMapHeader.cNumPatchMaps; 
         (cPatchMaps > 0) && (c < MAX_PATCHES); 
         c++, c--)
   {
      // Is this patch General MIDI?
      //
      if (pbPatchMap[c] != c)
      {
         // Build the map entry for this patch.
         //
         mifPatchMap.bDst = c;
         mifPatchMap.bSrc = pbPatchMap[c];
         
         // Write the map entry to the MIF.
         //
         l = mmioWrite(ghmmio, (HPSTR)&mifPatchMap, mifPatchMap.cbStruct);
         if (mifPatchMap.cbStruct != (DWORD)l)
         {
            // We did not write enough bytes.
            //
            return(MIFERR_BADWRITE);
         }
      }
   }

   // Ascend the chunk to correct the size info.
   //
   mmioAscend(ghmmio, &chkSub, NULL);

   return(MMSYSERR_NOERROR);
} //** WritePatchMapChunk()


//*****************************************************************************
//**
//**  WriteKeyMapChunk();
//**
//**  DESCRIPTION:
//**
//**
//**  ARGUMENTS:
//**     DWORD cInst
//**
//**  RETURNS:
//**     MMRESULT 
//**
//**  HISTORY:
//**     09/10/93       created.
//**
//*****************************************************************************

MMRESULT FNLOCAL WriteKeyMapChunk(
   DWORD cInst)
{
   MMRESULT             mmr;
   MIFKEYMAPHDR         mifKeyMapHeader;
   MIFKEYMAPCHANNELINFO mifKeyMapChannelInfo[MAX_CHANNELS];
   MIFMAPENTRY          mifKeyMapEntry;
   BYTE                 (FAR *pbKeyMap)[MAX_PATCHES];
   LPDWORD              pdwChannelTypes;
   MMCKINFO             chkSub;
   DWORD                cChannel;
   DWORD                cKeyMap;
   DWORD                dw;
   LONG                 l;

   // Get a pointer to the key maps for the given instrument.
   //
   pbKeyMap = gpInst[cInst].aKeyMaps;

   // Get a nice pointer to the channel info.
   //
   pdwChannelTypes = (LPDWORD)gpInst[cInst].aChannelTypes;

   // Initialize the structures to 0.
   //
   _fmemset(&mifKeyMapHeader, 0, sizeof(mifKeyMapHeader));
   _fmemset(&mifKeyMapChannelInfo, 0, sizeof(mifKeyMapChannelInfo));
   _fmemset(&chkSub, 0, sizeof(chkSub));

   // Save the size of the key map header.
   //
   mifKeyMapHeader.cbStruct = sizeof(mifKeyMapHeader);

   // We need to check the key maps for all drum channels to determine
   // if any were re-defined.
   //
   for ( cChannel = 0; 
         cChannel < MAX_CHANNELS; 
         cChannel++)
   {
      // Is the channel type a drum channel?
      //
      if (MIFCHANNELINFO_DRUM_CHANNEL == pdwChannelTypes[cChannel])
      {
         // Set up the values for this channels key maps.
         //
         mifKeyMapChannelInfo[cChannel].cbStruct  = sizeof(MIFKEYMAPCHANNELINFO);
         mifKeyMapChannelInfo[cChannel].dwChannel = cChannel;
         mifKeyMapChannelInfo[cChannel].cNumKeyMaps = 0;
         mifKeyMapChannelInfo[cChannel].cbKeyMapSize = sizeof(MIFMAPENTRY);
         mifKeyMapChannelInfo[cChannel].fdwFlags |= MIFKEYMAPCHANNELINFO_F_GENERAL_MIDI;

         // Check the channels key maps for non-GM maps.
         //
         for ( cKeyMap = 0;
               cKeyMap < MAX_KEY_MAPS;
               cKeyMap++)
         {
            // Is the current key map General MIDI?
            //
            if (pbKeyMap[cChannel][cKeyMap] != cKeyMap)
            {
               // Increment the total number of key maps that
               // are to be saved in the MIF.
               //
               mifKeyMapHeader.cNumKeyMaps++;

               // Increment this channels count of key maps.
               //
               mifKeyMapChannelInfo[cChannel].cNumKeyMaps;

               // Continue on to the next channel.
               //
               break;
            }
         }
      }
   }

   // Setup the "key " chunk information.
   //
   chkSub.ckid = mmioFOURCC('k', 'e', 'y', ' ');
   
   //  Creating a "chnl" chunk in file.
   //
   mmr = mmioCreateChunk(ghmmio, &chkSub, 0);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Error could not create instrument chunk.
      //
      return(MIFERR_CANNOTCREATECHUNK);
   }
   
   // Write the header to the file.
   //
   dw = mmioWrite(ghmmio, (HPSTR)&mifKeyMapHeader, mifKeyMapHeader.cbStruct);
   if (mifKeyMapHeader.cbStruct != dw)
   {
      // Error didn't write enough bytes.
      //
      return(MIFERR_BADWRITE);
   }
   
   // Write the channel information to the file.
   // IFF the channel type is not general midi.
   //
   for ( cChannel = 0; 
         cChannel < mifKeyMapHeader.cNumKeyMaps; 
         cChannel++)
   {
      // Write the new channel info to the MIF.
      //
      l = mmioWrite(ghmmio, 
                    (HPSTR)&mifKeyMapChannelInfo[cChannel],   
                    sizeof(MIFKEYMAPCHANNELINFO));
      if (sizeof(MIFKEYMAPCHANNELINFO) != (DWORD)l)
         return(MIFERR_BADWRITE);


      // Write the key maps for this channel.
      //
      for ( cKeyMap = 0;
            cKeyMap < mifKeyMapChannelInfo[cChannel].cNumKeyMaps;
            cKeyMap++)
      {
         // Build the key map entry to write.
         //
         mifKeyMapEntry.bDst = (BYTE)cKeyMap;

         // Get the channel that the key map belongs to.
         //
         dw = mifKeyMapChannelInfo[cChannel].dwChannel;

         // Get the source map.
         //
         mifKeyMapEntry.bSrc = (BYTE)pbKeyMap[dw][cKeyMap];

         // Write the new channel info to the MIF.
         //
         l = mmioWrite(ghmmio, 
                     (HPSTR)&mifKeyMapEntry, 
                     sizeof(mifKeyMapEntry));
         if (sizeof(mifKeyMapEntry) != (DWORD)l)
            return(MIFERR_BADWRITE);
      }
   }

   // Ascend the chunk to correct the size info.
   //
   mmioAscend(ghmmio, &chkSub, NULL);

   return(MMSYSERR_NOERROR);
} //** WriteKeyMapChunk()


//************************************************************************
//**
//**  SaveConfigFile();
//**
//**  DESCRIPTION:
//**     This function will save the current mif file in memory under 
//**     the name that is currently in gszmifName.
//**
//**  ARGUMENTS:
//**     VOID
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if the file was successfully saved.
//**                 Otherwise an error code is returned.
//**
//**  HISTORY:
//**     04/29/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL SaveConfigFile(
   VOID)
{
   MMRESULT mmr;
   MMCKINFO chkList;
   DWORD    c;

   // Attempt to open the file, create it if necessary.
   //
   ghmmio = mmioOpen(gszmifName, 
                     NULL, 
                     MMIO_CREATE | MMIO_ALLOCBUF | MMIO_READWRITE);
   if (NULL == ghmmio)
   {
      // Error could not create the file.
      //
      return(MIFERR_CANNOTCREATEFILE);
   }

   // Loop through each instrument and save the data.
   //
   for ( c = 0; 
         c < gdwNumInsts; 
         c++)
   {
      // Set the LIST chunk type.
      //
      chkList.fccType = mmioFOURCC('M', 'M', 'A', 'P');

      // Initialize the size to zero.
      //
      chkList.cksize = 0L;

      // Create the list chunk "MMAP".
      //
      mmr = mmioCreateChunk(ghmmio, &chkList, MMIO_CREATELIST);
      if (MMSYSERR_NOERROR != mmr)
      {
         // Return an error code that we have a message for.
         //
         mmr = MIFERR_CANNOTCREATECHUNK;
         break;
      }
      
      // Write header chunk.
      // 
      mmr = WriteHeaderChunk(c);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Write instrument information chunk.
      //
      mmr = WriteInstChunk(c);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Write the instrument capabilities chunk.
      //
      mmr = WriteCapsChunk(c);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Write the channel types chunk.
      //
      mmr = WriteChannelChunk(c);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Write the patch map chunk.
      //
      mmr = WritePatchMapChunk(c);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Write the key map chunk.
      //
      mmr = WriteKeyMapChunk(c);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Ascend chunk to fix chunk size.
      //
      mmioAscend(ghmmio, &chkList, 0);
   }

   // Close the file.
   //
   mmioClose(ghmmio, 0);
   ghmmio = NULL;

   return(mmr);
} //** SaveConfigFile()


//************************************************************************
//**
//**  SaveConfigFileAs();
//**
//**  DESCRIPTION:
//**     This function will allow the user to save the current mif file
//**     under a different name.
//**
//**  ARGUMENTS:
//**     VOID
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if the function either saves the
//**                 file correctly or if the user does _NOT_ choose a
//**                 name to save the file as.
//**
//**  HISTORY:
//**     04/29/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL SaveConfigFileAs(
   VOID)
{
   MMRESULT mmr;
   char     szTitle[MAX_TITLE_LEN+1];
   char     szName[MAX_PATH_LEN+1];
   BOOL     f;

   // Get the name to save the file as.
   //
   f = AppGetFileName(ghwndMain, szName, szTitle, TRUE);
   if (f)
   {
      // Save the temp's.
      //
      lstrcpy(gszmifTitle, szTitle);
      lstrcpy(gszmifName, szName);

      // Try and save the file.
      //
      mmr = SaveConfigFile();
      if (MMSYSERR_NOERROR != mmr)
         return(mmr);

      // Set the windows title.
      //
      SetWindowTitle();
   }

   return(MMSYSERR_NOERROR);
} //** SaveConfigFileAs()


//************************************************************************
//**
//**  ReadHeaderChunk();
//**
//**  DESCRIPTION:
//**     This function will read the mif header from the file.
//**
//**  ARGUMENTS:
//**     LPMMCKINFO pchkParent   -  Pointer to the parent chunk.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful. Otherwise it will
//**                 return an error code.
//**
//**  HISTORY:
//**     05/03/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL ReadHeaderChunk(
   LPMMCKINFO pchkParent)
{
   LPMIFHEADER pmifHeader;
   MMRESULT    mmr;
   MMCKINFO    chkSub;
   LONG        l;

   // We are looking for the instruments header chunk.
   //
   chkSub.ckid = mmioFOURCC('h', 'd', 'r', ' ');
   
   // Descend to the "hdr " chunk in this list.
   //
   mmr = mmioDescend(ghmmio, &chkSub, pchkParent, MMIO_FINDCHUNK);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Could not find the chunk.
      //
      return(MIFERR_CANNOTFINDCHUNK);
   }

   // We found the "hdr " chunk, now check it's size and
   // see if it is one that we can read.
   // We check to make sure that the size of the chunks is
   // greater than a MIFHEADER, this ensures that the mif
   // has some sort of unique name at the end.
   //
   if (sizeof(MIFHEADER) >= chkSub.cksize)
   {
      // The sizeof the mif header is not what we expected.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Allocate memory for the header.
   //
   pmifHeader = AllocPtr(GHND, chkSub.cksize);
   if (NULL == pmifHeader)
   {
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_NOMEM);
   }

   // Read in the whole chunk into our buffer.
   //
   l = mmioRead(ghmmio, (HPSTR)pmifHeader, chkSub.cksize);
   if (chkSub.cksize != (DWORD)l)
   {
      // We didn't read in the amount of data that was
      // expected, return in error.
      //
      FreePtr(&pmifHeader);
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADREAD);
   }

   // Save the header info in the current instrument.
   //
   gpInst[gdwCurrInst].dwVersion = pmifHeader->dwVersion;
   gpInst[gdwCurrInst].dwCreator = pmifHeader->dwCreator;

   // Currently we only deal with MAX_STR_LEN number of bytes
   // for the unique name even though it is possible that 
   // the unique name can be longer.  Someone might update this
   // sometime....
   //
   _fstrncpy((LPSTR)gpInst[gdwCurrInst].szInstID,
             (LPSTR)pmifHeader->abInstID, 
             (UINT)min(pmifHeader->cbInstID, MAX_STR_LEN));

   // The strcpy that we use won't add this if our array
   // is to small for the whole name.
   //
   gpInst[gdwCurrInst].szInstID[MAX_STR_LEN] = '\0';

   // Ascend out of the chunk.
   //
   mmioAscend(ghmmio, &chkSub, 0);

   // Free the memory used by the MIFHEADER.
   //
   FreePtr(&pmifHeader);

   // Return success.
   //
   return(MMSYSERR_NOERROR);
} //** ReadHeaderChunk()


//************************************************************************
//**
//**  ReadInstChunk();
//**
//**  DESCRIPTION:
//**     This function will read the instrument information chunk from
//**     the mif file.
//**
//**  ARGUMENTS:
//**     LPMMCKINFO pchkParent   -  Pointer to the parent chunk.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful. Otherwise it will
//**                 return an error code.
//**
//**  HISTORY:
//**     05/03/93       created.
//**     09/10/93       updated this and that.
//**
//************************************************************************

MMRESULT FNLOCAL ReadInstChunk(
   LPMMCKINFO pchkParent)
{
   MIFINSTINFO    mifInstInfo;
   LPSTR          pszManufactASCII;
   LPSTR          pszManufactUNICODE;
   LPSTR          pszProductASCII;
   LPSTR          pszProductUNICODE;
   MMRESULT       mmr;
   MMCKINFO       chkSub;
   LONG           l;

   // We are looking for the instrument information chunk.
   //
   chkSub.ckid = mmioFOURCC('i', 'n', 's', 't');
   
   // Descend to the "inst" chunk in this list.
   //
   mmr = mmioDescend(ghmmio, &chkSub, pchkParent, MMIO_FINDCHUNK);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Could not find the chunk.
      //
      return(MIFERR_CANNOTFINDCHUNK);
   }

   // We found the "inst" chunk, now check it's size and
   // see if it is one that we can read.
   //
   if (sizeof(MIFINSTINFO) > chkSub.cksize)
   {
      // The sizeof the mif header is not what we expected.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Read in the standard instrument data,
   // don't read in the name bytes yet.
   //
   l = mmioRead(ghmmio, (HPSTR)&mifInstInfo, sizeof(mifInstInfo));
   if (sizeof(mifInstInfo) != (DWORD)l)
   {
      // We didn't read in the amount of data that was
      // expected, return in error.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADREAD);
   }

   // Build the inst info struct.
   //
   gpInst[gdwCurrInst].dwManufactID = mifInstInfo.dwManufactID;
   gpInst[gdwCurrInst].dwProductID  = mifInstInfo.dwProductID;
   gpInst[gdwCurrInst].dwRevision   = mifInstInfo.dwRevision;

   // Allocate memory for the ASCII version of the
   // instrument's manufacturer name.
   //

   if (0 != mifInstInfo.cbManufactASCII)
   {
      pszManufactASCII = AllocPtr(GHND, mifInstInfo.cbManufactASCII);
      if (NULL == pszManufactASCII)
      {
         // We could not allocate memory for the string.
         //
         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_NOMEM);
      }
   }

   // Allocate memory for the UNICODE version of the
   // instrument's manufacturer name.
   //

   // Added if statement.  No MIFs have unicode strings yet so this was
   // always failing 
   if (0 != mifInstInfo.cbManufactUNICODE)
   {
       pszManufactUNICODE = AllocPtr(GHND, mifInstInfo.cbManufactUNICODE);
       if (NULL == pszManufactUNICODE)
       {
           // We could not allocate memory for the string.
           //
           FreePtr(&pszManufactASCII);

           mmioAscend(ghmmio, &chkSub, 0);
           return(MIFERR_NOMEM);
       }
   }

   // Allocate memory for the ASCII version of the
   // instrument's product name.
   //

   if (0 != mifInstInfo.cbProductASCII)
   {
      pszProductASCII = AllocPtr(GHND, mifInstInfo.cbProductASCII);
      if (NULL == pszProductASCII)
      {
         // We could not allocate memory for the string.
         //
         FreePtr(&pszManufactASCII);
         FreePtr(&pszManufactUNICODE);

         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_NOMEM);
      }
   }

   // Allocate memory for the UNICODE version of the
   // instrument's product name.
   //
   // Added if statement.  No MIFs have unicode strings yet so this was
   // always failing when reading in MIFs 
   if (0 != mifInstInfo.cbProductUNICODE)
   {
      pszProductUNICODE = AllocPtr(GHND, mifInstInfo.cbProductUNICODE);
      if (NULL == pszProductUNICODE)
      {
         // We could not allocate memory for the string.
         //
         FreePtr(&pszManufactASCII);
         FreePtr(&pszManufactUNICODE);
         FreePtr(&pszProductASCII);

         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_NOMEM);
      }
   }


   // Read in the string for the ASCII version of the 
   // instrument's manufacturer name.
   //
   if (0 != mifInstInfo.cbManufactASCII)
   {
      l = mmioRead(ghmmio, (HPSTR)pszManufactASCII, mifInstInfo.cbManufactASCII);
      if (mifInstInfo.cbManufactASCII != (DWORD)l)
      {
         // We could not read in the string.
         //
         FreePtr(&pszManufactASCII);
         FreePtr(&pszManufactUNICODE);
         FreePtr(&pszProductASCII);
         FreePtr(&pszProductUNICODE);

         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADREAD);
      }
   }

   // Read in the string for the UNICODE version of the 
   // instrument's manufacturer name.
   //
   // Added if statement.  No MIFs have unicode strings yet so this was
   // always failing 
   if (0 != mifInstInfo.cbManufactUNICODE)
   {
      l = mmioRead(ghmmio, (HPSTR)pszManufactUNICODE, mifInstInfo.cbManufactUNICODE);
      if (mifInstInfo.cbManufactUNICODE != (DWORD)l)
      {
         // We could not read in the string.
         //
         FreePtr(&pszManufactASCII);
         FreePtr(&pszManufactUNICODE);
         FreePtr(&pszProductASCII);
         FreePtr(&pszProductUNICODE);

         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADREAD);
       }
   }

   // Read in the string for the ASCII version of the 
   // instrument's product name.
   //
   if (0 != mifInstInfo.cbProductASCII)
   {
      l = mmioRead(ghmmio, (HPSTR)pszProductASCII, mifInstInfo.cbProductASCII);
      if (mifInstInfo.cbProductASCII != (DWORD)l)
      {
         // We could not read in the string.
         //
         FreePtr(&pszManufactASCII);
         FreePtr(&pszManufactUNICODE);
         FreePtr(&pszProductASCII);
         FreePtr(&pszProductUNICODE);

         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADREAD);
      }
   }

   // Read in the string for the UNICODE version of the 
   // instrument's product name.
   //
   // Added if statement.  No MIFs have unicode strings yet so this was
   // always failing when reading in MIFs 
   if (0 != mifInstInfo.cbProductUNICODE)
   {
      l = mmioRead(ghmmio, (HPSTR)pszProductUNICODE, mifInstInfo.cbProductUNICODE);
      if (mifInstInfo.cbProductUNICODE != (DWORD)l)
      {
         // We could not read in the string.
         //
         FreePtr(&pszManufactASCII);
         FreePtr(&pszManufactUNICODE);
         FreePtr(&pszProductASCII);
         FreePtr(&pszProductUNICODE);

         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADREAD);
      }
   }

   // Complete the inst info struct. 
   //
   gpInst[gdwCurrInst].pszManufactASCII = pszManufactASCII;
   gpInst[gdwCurrInst].pszManufactUNICODE = pszManufactUNICODE;
   gpInst[gdwCurrInst].pszProductASCII = pszProductASCII;
   gpInst[gdwCurrInst].pszProductUNICODE = pszProductUNICODE;

   mmioAscend(ghmmio, &chkSub, 0);

   return(MMSYSERR_NOERROR);
} //** ReadInstChunk()


//************************************************************************
//**
//**  ReadCapsChunk();
//**
//**  DESCRIPTION:
//**     This function will read the instrument capabilities chunk from
//**     the mif file.
//**
//**  ARGUMENTS:
//**     LPMMCKINFO pchkParent   -  Pointer to the parent chunk.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful. Otherwise it will
//**                 return an error code.
//**
//**  HISTORY:
//**     05/03/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL ReadCapsChunk(
   LPMMCKINFO pchkParent)
{
   MIFINSTCAPS    mifInstCaps;
   MMRESULT       mmr;
   MMCKINFO       chkSub;
   LONG           l;

   // We are looking for the instrument capabilities chunk.
   //
   chkSub.ckid = mmioFOURCC('c', 'a', 'p', 's');
   
   // Descend to the "caps" chunk in this list.
   //
   mmr = mmioDescend(ghmmio, &chkSub, pchkParent, MMIO_FINDCHUNK);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Could not find the chunk.
      //
      return(MIFERR_CANNOTFINDCHUNK);
   }

   // We found the "caps" chunk, now check it's size and
   // see if it is one that we can read.
   //
   if (sizeof(mifInstCaps) != chkSub.cksize)
   {
      // The sizeof the mif header is not what we expected.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Read the instrument's capabilities from the file.
   //
   l = mmioRead(ghmmio, (HPSTR)&mifInstCaps, sizeof(mifInstCaps));
   if (sizeof(mifInstCaps) != l)
   {
      // We didn't read in the amount of data that was
      // expected, return in error.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADREAD);
   }

   // Save the mif instrument capabilities.
   //
   gpInst[gdwCurrInst].dwBasicChannel = mifInstCaps.dwBasicChannel;
   gpInst[gdwCurrInst].cNumChannels   = mifInstCaps.cNumChannels;
   gpInst[gdwCurrInst].cInstPoly      = mifInstCaps.cInstrumentPolyphony;
   gpInst[gdwCurrInst].cChannelPoly   = mifInstCaps.cChannelPolyphony;
   gpInst[gdwCurrInst].fdwFlags       = mifInstCaps.fdwFlags;

   // Ascend out of the capabilities chunk.
   //
   mmioAscend(ghmmio, &chkSub, 0);

   return(mmr);
} //** ReadCapsChunk()


//************************************************************************
//**
//**  ReadChannelChunk();
//**
//**  DESCRIPTION:
//**     This function will read the channel type chunk from the mif file.
//**
//**  ARGUMENTS:
//**     LPMMCKINFO pchkParent   -  Pointer to the parent chunk.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful. Otherwise it will
//**                 return an error code.
//**
//**  HISTORY:
//**     05/03/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL ReadChannelChunk(
   LPMMCKINFO pchkParent)
{
   MMRESULT          mmr;
   MMCKINFO          chkSub;
   MIFCHANNELHDR     mifChannelHeader;
   MIFCHANNELINFO    mifchnlinfo;
   DWORD             dwChannel;
   DWORD             c;
   LONG              l;

   // We are looking for the instrument channel definitions.
   //
   chkSub.ckid = mmioFOURCC('c', 'h', 'n', 'l');
   
   // Descend to the "chnl" chunk in this list.
   //
   mmr = mmioDescend(ghmmio, &chkSub, pchkParent, MMIO_FINDCHUNK);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Could not find the chunk.
      //
      return(MIFERR_CANNOTFINDCHUNK);
   }

   // We found the "chnl" chunk, now check it's size and
   // make sure it's at least as big as a MIFCHANNELHDR.
   //
   if (sizeof(MIFCHANNELHDR) > chkSub.cksize)
   {
      // The sizeof the mif header is not what we expected.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Read the channel header in.
   //
   l = mmioRead(ghmmio, (HPSTR)&mifChannelHeader, sizeof(mifChannelHeader));
   if (sizeof(mifChannelHeader) != l)
   {
      // Couldn't read in all of the header.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADREAD);
   }

   // Are there channels defined in the mif?
   //
   if (0 != mifChannelHeader.cNumChannels)
   {
      // Nothing else to read here.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MMSYSERR_NOERROR);
   }

   // Are the channels that are defined a size
   // that we recognize?
   //
   if (sizeof(MIFCHANNELINFO) != mifChannelHeader.cbChannelSize)
   {
      // We don't recognize what format the channel 
      // information is in.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Read all the channels that are defined in the MIF.
   //
   for ( c = 0; 
         c < mifChannelHeader.cNumChannels; 
         c++)
   {
      // Read the channel type information from the mif.
      //
      l = mmioRead(ghmmio, (HPSTR)&mifchnlinfo, sizeof(MIFCHANNELINFO));
      if (sizeof(MIFCHANNELINFO) != l)
      {
         // Didn't read the correct amount of inforamtion.
         //
         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADREAD);
      }

      // What channel is the non-general type for?
      //
      dwChannel = mifchnlinfo.dwChannel;

      // Store the channel type definition in our channel list.
      //
      CURR_CHANNEL_TYPE(dwChannel) = mifchnlinfo.dwChannelType;
   }

   return(MMSYSERR_NOERROR);
} //** ReadChannelChunk()


//************************************************************************
//**
//**  ReadPatchMapChunk();
//**
//**  DESCRIPTION:
//**     This function will read the patch maps chunk from the mif file.
//**
//**  ARGUMENTS:
//**     LPMMCKINFO pchkParent   -  Pointer to the parent chunk.
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if successful. Otherwise it will
//**                 return an error code.
//**
//**  HISTORY:
//**     05/03/93       created.
//**
//************************************************************************

MMRESULT FNLOCAL ReadPatchMapChunk(
   LPMMCKINFO pchkParent)
{
   MMRESULT          mmr;
   MMCKINFO          chkSub;
   MIFPATCHMAPHDR    mifPatchMapHeader;
   MIFMAPENTRY       mifPatchMap;
   DWORD             c;
   LONG              l;

   // We are looking for the patch map for the instrument.
   //
   chkSub.ckid = mmioFOURCC('p', 'm', 'a', 'p');
   
   // Descend to the "pmap" chunk in this list.
   //
   mmr = mmioDescend(ghmmio, &chkSub, pchkParent, MMIO_FINDCHUNK);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Could not find the chunk.
      //
      return(MIFERR_CANNOTFINDCHUNK);
   }

   // We found the "pmap" chunk, now check it's size and
   // make sure it's at least as big as a MIFPATCHMAPHDR.
   //
   if (sizeof(MIFPATCHMAPHDR) > chkSub.cksize)
   {
      // The sizeof the mif header is not what we expected.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Read the channel header in.
   //
   l = mmioRead(ghmmio, (HPSTR)&mifPatchMapHeader, sizeof(mifPatchMapHeader));
   if (sizeof(mifPatchMapHeader) != l)
   {
      // Couldn't read in all of the header.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADREAD);
   }

   // Are there any patches defined that we need to read?
   //
   if (0 == mifPatchMapHeader.cNumPatchMaps)
   {
      // There are no key maps defined in the mif.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MMSYSERR_NOERROR);
   }

   // Are the patch maps that are defined a size that we can
   // deal with?
   //
   if (sizeof(MIFMAPENTRY) != mifPatchMapHeader.cbPatchMapSize)
   {
      // The patch maps that are defined are not in
      // a format that we can deal with.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Read all the key maps that are defined.
   //
   for ( c = 0; 
         c < mifPatchMapHeader.cNumPatchMaps; 
         c++)
   {
      // Read the key map channel information from the mif.
      //
      l = mmioRead(ghmmio, (HPSTR)&mifPatchMap, sizeof(MIFMAPENTRY));
      if (sizeof(MIFMAPENTRY) != l)
      {
         // Didn't read the correct amount of inforamtion.
         //
         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADREAD);
      }

      // Store the patch map in our array.
      //
      CURR_PATCH_MAP(mifPatchMap.bDst) = mifPatchMap.bSrc;
   }

   mmioAscend(ghmmio, &chkSub, 0);

   // Return success.
   //
   return(MMSYSERR_NOERROR);
} //** ReadPatchMapChunk()


//*****************************************************************************
//**
//**  ReadKeyMapChunk();
//**
//**  DESCRIPTION:
//**     This function will read in the key maps for the drum channels.
//**
//**
//**  ARGUMENTS:
//**     LPMMCKINFO pchkParent
//**
//**  RETURNS:
//**     MMRESULT 
//**
//**  HISTORY:
//**     09/10/93       created.
//**
//*****************************************************************************

MMRESULT FNLOCAL ReadKeyMapChunk(
   LPMMCKINFO pchkParent)
{
   MMRESULT             mmr;
   MMCKINFO             chkSub;
   MIFKEYMAPHDR         mifKeyMapHeader;
   MIFKEYMAPCHANNELINFO mifKeyMapChannelInfo;
   MIFMAPENTRY          mifKeyMap;
   DWORD                c;
   DWORD                cKeyMap;
   LONG                 l;

   // We are looking for the key map header for the instrument.
   //
   chkSub.ckid = mmioFOURCC('k', 'e', 'y', ' ');
   
   // Descend to the "key " chunk in this list.
   //
   mmr = mmioDescend(ghmmio, &chkSub, pchkParent, MMIO_FINDCHUNK);
   if (MMSYSERR_NOERROR != mmr)
   {
      // Could not find the chunk.
      //
      return(MIFERR_CANNOTFINDCHUNK);
   }

   // We found the "key " chunk, now check it's size and
   // make sure it's at least as big as a MIFKEYMAPHDR.
   //
   if (sizeof(MIFKEYMAPHDR) > chkSub.cksize)
   {
      // The sizeof the mif header is not what we expected.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADCHUNK);
   }

   // Read the channel header in.
   //
   l = mmioRead(ghmmio, (HPSTR)&mifKeyMapHeader, sizeof(mifKeyMapHeader));
   if (sizeof(mifKeyMapHeader) != l)
   {
      // Couldn't read in all of the header.
      //
      mmioAscend(ghmmio, &chkSub, 0);
      return(MIFERR_BADREAD);
   }

   // Are there any patches defined that we need to read?
   //
   if (0 == mifKeyMapHeader.cNumKeyMaps)
   {
      // There are no key maps defined in the mif.
      // 
      mmioAscend(ghmmio, &chkSub, 0);
      return(MMSYSERR_NOERROR);
   }

   // Read all the patch maps that are defined.
   //
   for ( c = 0; 
         c < mifKeyMapHeader.cNumKeyMaps; 
         c++)
   {
      // Read the key map channel information.
      //
      l = mmioRead(ghmmio, 
                   (HPSTR)&mifKeyMapChannelInfo, 
                   sizeof(mifKeyMapChannelInfo));
      if (sizeof(mifKeyMapChannelInfo) != l)
      {
         // We didn't read in the key map's channel information
         // correctly.
         //
         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADREAD);
      }

      // Are there key maps defined for this channel in
      // a format that we can deal with?
      //
      if (sizeof(MIFMAPENTRY) != mifKeyMapChannelInfo.cbKeyMapSize)
      {
         // We don't know how to deal with the key maps
         // that are defined in the mif.
         //
         mmioAscend(ghmmio, &chkSub, 0);
         return(MIFERR_BADCHUNK);
      }

      // Read in all the key maps for the channel.
      //
      for ( cKeyMap = 0;
            cKeyMap < mifKeyMapChannelInfo.cNumKeyMaps;
            cKeyMap++)
      {
         // Read the key map entry from the mif.
         //
         l = mmioRead(ghmmio, (HPSTR)&mifKeyMap, sizeof(mifKeyMap));
         if (sizeof(mifKeyMap) != l)
         {
            // Didn't read the correct amount of inforamtion.
            //
            mmioAscend(ghmmio, &chkSub, 0);
            return(MIFERR_BADREAD);
         }

         // Store the key map in our array.
         //
         CURR_KEY_MAP(mifKeyMapChannelInfo.dwChannel, mifKeyMap.bDst) = mifKeyMap.bSrc;
      }
   }

   mmioAscend(ghmmio, &chkSub, 0);

   // Return success.
   //
   return(MMSYSERR_NOERROR);
} //** ReadKeyMapChunk()


//************************************************************************
//**
//**  OpenConfigFile();
//**
//**  DESCRIPTION:
//**     This function will open and read in a mif file.
//**
//**  ARGUMENTS:
//**     VOID
//**
//**  RETURNS:
//**     MMRESULT -  MMSYSERR_NOERROR if there was not an error,
//**                 otherwise it will return an error code.
//**
//**  HISTORY:
//**     04/22/93       created.
//**     09/10/93       changed a few things.
//**
//************************************************************************

MMRESULT FNLOCAL OpenConfigFile(
   VOID)
{
   MMRESULT       mmr;
   char           szTitle[MAX_TITLE_LEN+1];
   char           szName[MAX_PATH_LEN+1];
   HMMIO          hmmio;
   MMCKINFO       chkParent;
   BOOL           f;

   // Get the file name to open.
   //
   f = AppGetFileName(ghwndMain, szName, szTitle, FALSE);
   if (!f)
   {
      // We didn't get a name.
      // But this is _NOT_ always an error so let return in a good state.
      //
      return(MMSYSERR_NOERROR);
   }

   // Open the file for reading.
   //
   hmmio = mmioOpen(szName, NULL, MMIO_ALLOCBUF | MMIO_READ);
   if (NULL == hmmio)
   {
      // Error could not create the file.
      //
      return(MIFERR_CANNOTCREATEFILE);
   }

   // Disable redrawing of the list box while we load the 
   // new MIF.
   //
   SendMessage(HWND_LIST_BOX, WM_SETREDRAW, FALSE, 0L);

   // Clear the list box of it's current contents
   // in preparation for the new file.
   //
   SendMessage(HWND_LIST_BOX, LB_RESETCONTENT, 0, 0L);

   // Clean up the current editing session.
   //
   CleanUp();

   // Now save our temp stuff.
   //
   ghmmio = hmmio;

   // Now loop through all of the "MMAP" list chunks and load them in.
   //
   for (;;)
   {
      // Search for a "MMAP" list chunk.
      //
      chkParent.fccType = mmioFOURCC('M', 'M', 'A', 'P');

      mmr = mmioDescend(ghmmio, &chkParent, NULL, MMIO_FINDLIST);
      if (MMIOERR_CHUNKNOTFOUND == mmr)
      {
         // We couldn't find one so we are done.
         // We must reset the return code so that it doesn't show
         // an error.
         //
         mmr = MMSYSERR_NOERROR;
         break;
      }

      // Allocate a new MIF struct.
      //
      mmr = NewInstrument();
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Read in the header chunk.
      //
      mmr = ReadHeaderChunk(&chkParent);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Read in the instrument info chunk.
      //
      mmr = ReadInstChunk(&chkParent);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Read in the instrument caps chunk.
      //
      mmr = ReadCapsChunk(&chkParent);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Read in the channel type chunk.
      //
      mmr = ReadChannelChunk(&chkParent);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Read in the patch map chunk.
      //
      mmr = ReadPatchMapChunk(&chkParent);
      if (MMSYSERR_NOERROR != mmr)
         break;

      // Read in the key map chunk.
      //
      mmr = ReadKeyMapChunk(&chkParent);
      if (MMSYSERR_NOERROR != mmr)
         break;

      mmioAscend(ghmmio, &chkParent, 0);
   }

   // Did we succeed?
   //
   if (MMSYSERR_NOERROR != mmr)
   {
      // We failed somewhere.
      //
      CleanUp();
      return(mmr);
   }

   // Save the name of the file.
   //
   lstrcpy(gszmifName, szName);
   lstrcpy(gszmifTitle, szTitle);

   // Setup the dialog box for the new mif file
   //
   SetupDialog();

   // Close the file.
   //
   mmioClose(ghmmio, 0);
   ghmmio = NULL;

   return(MMSYSERR_NOERROR);
} //** OpenConfigFile()




