//************************************************************************
//**
//**  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.
//**
//**  proc.c
//**
//**  DESCRIPTION:
//**
//**  HISTORY:
//**     05/07/93       created.
//**
//************************************************************************

#include "globals.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <mmreg.h>
#include <shellapi.h>
#include "res.h"

//************************************************************************
//**
//**  NotifyUser();
//**
//**  DESCRIPTION:
//**     This function will notify the user with a message from the 
//**     string resources.
//**
//**  ARGUMENTS:
//**     DWORD dwTitle  -  ID or LPSTR for the title.
//**     UINT  uMessage -  String ID for the message box's message.
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     05/07/93       created.
//**
//************************************************************************

VOID NEAR PASCAL NotifyUser(
   DWORD dwTitle,
   UINT  uMessage)
{
   char  achtitle[MAX_STR_LEN+1];
   char  achmsg[MAX_STR_LEN+1];
   
   // If there is a title then determine where to get it from.
   //
   if (0 != dwTitle)
   {
      // Is dwTitle a string resource ID?
      //
      if (HIWORD(dwTitle) == 0)
      {
         // Yes, so load the string.
         //
         LoadString(ghinst, (UINT)dwTitle, achtitle, MAX_STR_LEN);
      }
      else
      {
         // No, copy the string that dwTitle points 
         // to into our buffer.
         //
         lstrcpy((LPSTR)achtitle, (LPSTR)dwTitle);
      }
   }

   // Get the message string.
   //
   LoadString(ghinst, uMessage, achmsg, MAX_STR_LEN);

   // Show the message to the user.
   //
   MessageBox(ghwndMain, achmsg, 
              (0 == dwTitle) ? NULL : achtitle, 
              MB_OK);

} //** NotifyUser()


//************************************************************************
//**
//**  InitStuff();
//**
//**  DESCRIPTION:
//**     This function will get a standard font to use for text and 
//**     create the list box to show the files.
//**
//**  ARGUMENTS:
//**     HWND  hwnd  -  Handle to the parent window.
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     05/07/93       created.
//**
//************************************************************************

VOID NEAR PASCAL InitStuff(
   HWND  hwnd)
{
   RECT        rc;
   HDC         hdc;
   TEXTMETRIC  tm;

   // Get the strings for the header bar.
   //
   LoadString(ghinst, IDS_HEADER_1, gszHeaderBar[0], 10);
   LoadString(ghinst, IDS_HEADER_2, gszHeaderBar[1], 10);

   // Get a nice stock font to use.
   //
   ghfont = GetStockObject(SYSTEM_FIXED_FONT);

   // Get a reliable dc.
   //
   hdc = GetDC(NULL);

   // Select our nice font into our reliable dc.
   //
   ghfont = SelectObject(hdc, ghfont);

   // Get some font info.
   //
   GetTextMetrics(hdc, &tm);

   // Save the char height.
   //
   giFontHeight = tm.tmHeight;

   // Select original font back into dc.
   //
   ghfont = SelectObject(hdc, ghfont);

   // Let our reliable dc go.
   //
   ReleaseDC(NULL, hdc);

   // Get the size of the client area.
   //
   GetClientRect(hwnd, &rc);

   // Create a list box with room for a header bar.
   //
   ghwndListbox = CreateWindow("listbox", "",
                               WS_CHILD | WS_VISIBLE | WS_VSCROLL |
                               LBS_NOTIFY | LBS_USETABSTOPS |
                               LBS_NOINTEGRALHEIGHT | LBS_MULTIPLESEL |
                               LBS_SORT,
                               0, giFontHeight, 
                               rc.right, rc.bottom - giFontHeight,
                               hwnd, (HMENU)ID_LISTBOX, 
                               ghinst, NULL);

   // Make sure our list box uses our nice font.
   //
   SendMessage(ghwndListbox, WM_SETREDRAW, FALSE, 0L);
   SendMessage(ghwndListbox, WM_SETFONT, (WPARAM)ghfont, 0L);
   SendMessage(ghwndListbox, WM_SETREDRAW, TRUE, 0L);

} //** InitStuff()


//************************************************************************
//**
//**  PaintItAll();
//**
//**  DESCRIPTION:
//**     This function will redraw the header bar in the main window's 
//**     client area.
//**
//**  ARGUMENTS:
//**     HDC   hdc   -  Main window's client DC.
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     04/28/93       created.
//**
//************************************************************************

VOID NEAR PASCAL PaintItAll(
   HDC   hdc)
{
   RECT        rc;
   char        szbuf[MAX_FILE_BUF];

   // Select our nice font into the dc.
   //
   SelectObject(hdc, ghfont);

   // Set the header bar's colors.
   //
   SetBkColor(hdc, GetSysColor(COLOR_ACTIVECAPTION));
   SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));

   // Get the rect of the window.
   //
   GetClientRect(ghwndMain, &rc);
   rc.bottom = rc.top + giFontHeight;

   wsprintf(szbuf, "%-25s%-s", 
            (LPSTR)gszHeaderBar[0], (LPSTR)gszHeaderBar[1]);

   // Update the header bar.
   //
   ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rc, NULL, 0, 0);
   TextOut(hdc, rc.left, rc.top, szbuf, lstrlen(szbuf));

} //** PaintItAll()


//************************************************************************
//**
//**  ClearListbox();
//**
//**  DESCRIPTION:
//**     This function will determine the number of items in the 
//**     list box and delete them all.
//**
//**  ARGUMENTS:
//**     VOID
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     04/27/93       created.
//**
//************************************************************************

VOID NEAR PASCAL ClearListbox(
   VOID)
{
   DWORD cListItems;

   // Get the number of items currently in list box.
   //
   cListItems = SendMessage(ghwndListbox, LB_GETCOUNT, 0, 0L);

   // Nuke 'em all!
   //
   while (0L != cListItems--)
      SendMessage(ghwndListbox, LB_DELETESTRING, (WPARAM)cListItems, 0L);

} //** ClearListbox()


//************************************************************************
//**
//**  UpdateListbox();
//**
//**  DESCRIPTION:
//**     This function will add the file names into the list box.
//**
//**  ARGUMENTS:
//**     VOID
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     05/07/93       created.
//**
//************************************************************************

VOID NEAR PASCAL UpdateListbox(
   VOID)
{
   UINT  u;
   char  szbuf[MAX_FILE_BUF];

   // Disable redrawing in the list box.
   //
   SendMessage(ghwndListbox, WM_SETREDRAW, (WPARAM)FALSE, 0L);

   // Clear the current contents of the list box.
   //
   ClearListbox();

   // Add the current files to the listbox.
   //
   for (u = 0; u < guNumFiles; u++)
   {
      wsprintf(szbuf, "%-25s%-s", 
               (LPSTR)gpFiles[u].pszMidiTitle, 
               (LPSTR)gpFiles[u].pszMifFile);

      SendMessage(ghwndListbox, LB_ADDSTRING, 0, 
                  (LPARAM)(LPSTR)szbuf);

   }

   // Set the current selection to the first string.
   //
   SendMessage(ghwndListbox, LB_SETCURSEL, 0, 0L);

   // Enable redrawing of the listbox.
   //
   SendMessage(ghwndListbox, WM_SETREDRAW, (WPARAM)TRUE, 0L);

   // Redraw the main window.
   //
   InvalidateRect(ghwndMain, NULL, FALSE);

   // Set the focus to the list box.
   //
   SetFocus(ghwndListbox);

} //** UpdateListbox()


//************************************************************************
//**
//**  GetFiles();
//**
//**  DESCRIPTION:
//**     Will present the common dialog open file box for the user to
//**     select the files to add patches to, note the multiple select 
//**     option.  It will then create the array of file names and add 
//**     the files to the list box.
//**
//**  ARGUMENTS:
//**     VOID
//**
//**  RETURNS:
//**     MMRESULT    MMSYSERR_NOERROR if successful,
//**                 Otherwise an error code.
//**
//**  HISTORY:
//**     05/07/93       created.
//**
//************************************************************************

MMRESULT FAR PASCAL GetFiles(
    VOID)
{
   MMRESULT       mmr;
   char           szExtFilter[256];
   char           achbuf[MAX_FILE_BUF+1];
   char           achtitle[MAX_TITLE_LEN+1];
   UINT           u;
   PSTR           psz;
   PSTR           pszPath;
   OPENFILENAME   ofn;
   BOOL           f;

   // Get the extension filter for this application
   //
   LoadString(ghinst, IDS_OFN_EXT_FILTER1, szExtFilter, sizeof(szExtFilter));

   for (psz = &szExtFilter[0]; '\0' != *psz; psz++)
   {
      if ('!' == *psz)
         *psz = '\0';
   }

   //  initialize the OPENFILENAME members
   //
   memset(&ofn, 0, sizeof(OPENFILENAME));

   // initialize the buffer.
   //
   achbuf[0] = '\0';

   ofn.lStructSize      =  sizeof(OPENFILENAME);
   ofn.hwndOwner        =  ghwndMain;
   ofn.lpstrFilter      =  szExtFilter;
   ofn.nFilterIndex     =  1L;
   ofn.lpstrFile        =  achbuf;
   ofn.nMaxFile         =  MAX_FILE_BUF;
   ofn.lpstrFileTitle   =  achtitle;
   ofn.nMaxFileTitle    =  MAX_TITLE_LEN;
   ofn.lpstrInitialDir  =  NULL;
   ofn.Flags            =  OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | 
                           OFN_ALLOWMULTISELECT;

   // Call the commdlg function.
   //
   f = GetOpenFileName(&ofn);

   if (!f)
   {
      // The user canceled the operation, or the buffer was to small.
      //
      mmr = MMSYSERR_NOERROR;
      goto GetFiles_Exit;
   }

   // Are the files already in the list box?
   //
   if (NULL != gpFiles)
   {
      // Free 'em up!
      //
      GlobalFreePtr(gpFiles);

      // Initialize the pointer to NULL.
      //
      gpFiles = NULL;
   }

   // Initialize the number of files to 0.
   //
   guNumFiles = 0;

   // Go throught the buffer of file names until the end.
   //
   u = 0;

   while ('\0' != achbuf[u])
   {
      if (achbuf[u++] == ' ')
         guNumFiles++;
   }

   // Was there only one file selected?
   //
   if ((0 == guNumFiles) && (0 != lstrlen(achbuf)))
   {
      // We actually have one file, so we'd better reflect it.
      //
      guNumFiles = 1;

      // Yep so allocate memory for only one.
      //
      gpFiles = (LPFILES)GlobalAllocPtr(GHND, sizeof(FILES));

      // Did we get our memory?
      //
      if (NULL == gpFiles)
      {
         // Could not allocate memory for the array of files.
         //
         mmr = MAPADDERR_NOMEM;
         goto GetFiles_Exit;
      }

      // Store the file name.
      //
      lstrcpy((LPSTR)gpFiles[0].pszMidiTitle, (LPSTR)achtitle);
      lstrcpy((LPSTR)gpFiles[0].pszMidiFile, (LPSTR)achbuf);
   }
   else
   {
      // Allocate memory for the files selected.
      //
      gpFiles = (LPFILES)GlobalAllocPtr(GHND, guNumFiles * sizeof(FILES));

      // Did we get our memory?
      //
      if (NULL == gpFiles)
      {
         // Could not allocate memory for the array of files.
         //
         mmr = MAPADDERR_NOMEM;
         goto GetFiles_Exit;
      }

      // Get the path for the files.
      //
      pszPath = strtok(achbuf, " ");

      // Now get each file name and save it in our array.
      //
      u = 0;

      while (NULL != (psz = strtok(NULL, " ")))
      {
         // Save the Midi files title.
         //
         lstrcpy((LPSTR)gpFiles[u].pszMidiTitle, (LPSTR)psz);

         // Save the full path to the Midi file.
         //
         wsprintf(gpFiles[u++].pszMidiFile, "%s\\%s", 
                  (LPSTR)pszPath, (LPSTR)psz);
      }
   }

   mmr = MMSYSERR_NOERROR;

GetFiles_Exit:
   return(mmr);
} //** GetFiles()


//************************************************************************
//**
//**  ConvertMID2RMI();
//**
//**  DESCRIPTION:
//**     Will load the data from the standard Midi file format and save 
//**     it in a RIFF RMID format.  It will leave the original file 
//**     intact unless it has the .rmi extension, in which case it will
//**     overwrite the original file.
//**
//**  ARGUMENTS:
//**     UINT  u  -  The index into our file array, specifying the 
//**                 file to convert.
//**
//**  RETURNS:
//**     MMRESULT    MMSYSERR_NOERROR if successful,
//**                 Otherwise an error code.
//**
//**  HISTORY:
//**     05/08/93       created.
//**
//************************************************************************

MMRESULT NEAR PASCAL ConvertMID2RMI(
   UINT  u)
{
   MMRESULT mmr;
   LONG     lsize;
   LONG     l;
   HPSTR    hp;
   HMMIO    hmmioMID;
   HMMIO    hmmioRMI;
   MMCKINFO chkRIFF;
   MMCKINFO chkSub;
   LPSTR    psz;

   // Open the Standard Midi file.
   //
   hmmioMID = mmioOpen(gpFiles[u].pszMidiFile, NULL, 
                       MMIO_ALLOCBUF | MMIO_READ);

   // Seek to the end of the file and determine the size
   //
   lsize = mmioSeek(hmmioMID, 0, SEEK_END);

   // Reset the file pointer to the beginning.
   //
   mmioSeek(hmmioMID, 0, SEEK_SET);

   // Allocate memory for the Midi data.
   //
   hp = (HPSTR)GlobalAllocPtr(GHND, lsize);

   if (NULL == hp)
   {
      // Could not allocate memory for the Midi data.
      //
      mmr = MAPADDERR_NOMEM;
      goto ConvertMID2RMI_Exit;
   }

   l = mmioRead(hmmioMID, hp, lsize);

   if (lsize != l)
   {
      // Could not read all of the file's data.
      //
      mmr = MAPADDERR_BADREAD;
      goto ConvertMID2RMI_Exit;
   }

   // Close the MID file.
   //
   mmioClose(hmmioMID, 0);

   // Construct our new RMI file name.
   //
   psz = gpFiles[u].pszMidiFile;

   while (*psz != '.')
      psz++;
   
   lstrcpy((LPSTR)++psz, (LPSTR)"rmi");

   psz = gpFiles[u].pszMidiTitle;
 
   while (*psz != '.')
      psz++;

   lstrcpy((LPSTR)++psz, (LPSTR)"rmi");

   // Open the new file.
   //
   hmmioRMI = mmioOpen(gpFiles[u].pszMidiFile, NULL, 
                       MMIO_READWRITE | MMIO_ALLOCBUF | MMIO_CREATE);

   if (NULL == hmmioRMI)
   {
      // Could not open the new file.
      //
      mmr = MAPADDERR_BADOPEN;
      goto ConvertMID2RMI_Exit;
   }

   // Create the RIFF chunk.
   //
   chkRIFF.fccType = FOURCC_RMID;
   chkRIFF.cksize  = 0L;
   mmr = mmioCreateChunk(hmmioRMI, &chkRIFF, MMIO_CREATERIFF);

   if (MMSYSERR_NOERROR != mmr)
   {
      mmr = MAPADDERR_BADWRITE;
      goto ConvertMID2RMI_Exit;
   }

   // Create the data chunk.
   //
   chkSub.ckid    = FOURCC_DATA;
   chkSub.cksize  = 0L;
   mmr = mmioCreateChunk(hmmioRMI, &chkSub, 0);

   if (MMSYSERR_NOERROR != mmr)
   {
      mmr = MAPADDERR_BADWRITE;
      goto ConvertMID2RMI_Exit;
   }

   // Write the data.
   //
   l = mmioWrite(hmmioRMI, hp, lsize);

   if (lsize != l)
   {
      // Could not write the Midi data to the file.
      //
      mmr = MAPADDERR_BADWRITE;
      goto ConvertMID2RMI_Exit;
   }

   // Free the Midi data.
   //
   GlobalFreePtr(hp);

   // Ascend out of the chunks to fix the sizes.
   //
   mmioAscend(hmmioRMI, &chkSub, NULL);
   mmioAscend(hmmioRMI, &chkRIFF, NULL);

   // Close the RMI file.
   //
   mmioClose(hmmioRMI, 0);

   mmr = MMSYSERR_NOERROR;

ConvertMID2RMI_Exit:

   return(mmr);

} //** ConvertMID2RMI()


//************************************************************************
//**
//**  AddMIF2RMI();
//**
//**  DESCRIPTION:
//**     This function will read the mif file that is associated with
//**     the Midi file, and add the _FIRST_ "MMAP" in the mif to the Midi 
//**     file as a LIST chunk "MMAP".
//**
//**  ARGUMENTS:
//**     UINT  u     -  Index in the gpFiles array for the file.
//**
//**  RETURNS:
//**     MMRESULT    -  MMSYSERR_NOERROR if successful,
//**                    Otherwise an error code.
//**
//**  HISTORY:
//**     05/08/93       created.
//**
//************************************************************************

MMRESULT NEAR PASCAL AddMIF2RMI(
   UINT  u)
{
   MMRESULT mmr;
   HMMIO    hmmioRMI;         // Handle to the rmi file.
   HMMIO    hmmioMIF;         // Handle to the mif file.
   MMCKINFO chkMIF;           // Chunk
   MMCKINFO chkRMI;           // Chunk
   HPSTR    hp;               // Pointer to the MMAP data.
   DWORD    dw;
   DWORD    cbData;

   // Does this file have a MIF file associated with it?
   //
   if ('\0' == gpFiles[u].pszMifFile[0])
   {
      // No, so return.
      // This is _NOT_ an error, the user might have just wanted to
      // convert a Standard Midi file to an RMI.
      //
      mmr = MMSYSERR_NOERROR;
      goto AddMIF2RMI_Exit;
   }

   // Try and open the .rmi file for reading/writing.
   //
   hmmioRMI = mmioOpen(gpFiles[u].pszMidiFile, NULL, MMIO_READWRITE);

   if (NULL == hmmioRMI)
   {
      // Could not open the file.
      //
      mmr = MAPADDERR_BADOPEN;
      goto AddMIF2RMI_Exit;
   }

   // Do we already have a "MMAP" list chunk?
   //
   chkRMI.fccType = FOURCC_MMAP;
   mmr = mmioDescend(hmmioRMI, &chkRMI, NULL, MMIO_FINDLIST);

   if (MMSYSERR_NOERROR == mmr)
   {
      // We can only have one MMAP in an RMI.
      //
      mmioClose(hmmioRMI, 0);
      mmr = MAPADDERR_MMAPPRESENT;
      goto AddMIF2RMI_Exit;
   }

   // Try and open the .mif file for reading.
   //
   hmmioMIF = mmioOpen(gpFiles[u].pszMifFile, NULL, MMIO_READ);

   if (NULL == hmmioMIF)
   {
      // Could not open the file.
      //
      mmr = MAPADDERR_BADOPEN;
      goto AddMIF2RMI_Exit;
   }

   // Descend to the "MMAP" list chunk in the mif file.
   //
   chkMIF.fccType = FOURCC_MMAP;
   mmr = mmioDescend(hmmioMIF, &chkMIF, NULL, MMIO_FINDLIST);

   if (MMSYSERR_NOERROR != mmr)
   {
      // Could not find the list chunk.
      //
      mmr = MAPADDERR_BADREAD;
      goto AddMIF2RMI_Exit;
   }

   // The amount of data we need to read is the size of the list
   // chunk, chkList.cksize, minus the sizeof the FOURCC for the
   // MMAP, since this will be written in the mmioCreateChunk()
   // call below.
   //
   cbData = chkMIF.cksize - sizeof(DWORD);

   // Read the "MMAP" chunk from the .mif file and write it
   // to the newly created "MMAP" chunk in the .rmi file.
   //
   hp = (HPSTR)GlobalAllocPtr(GHND, cbData);

   if (NULL == hp)
   {
      // Could not allocate memory for the data.
      //
      mmr = MAPADDERR_NOMEM;
      goto AddMIF2RMI_Exit;
   }

   // Try and read the mif information.
   //
   dw = (DWORD)mmioRead(hmmioMIF, hp, cbData);

   if (cbData != dw)
   {
      // Could not read all of the information.
      //
      mmr = MAPADDERR_BADREAD;
      goto AddMIF2RMI_Exit;
   }

   // Close the mif file.
   //
   mmioClose(hmmioMIF, 0);

   // Create a list chunk of "MMAP" in the rmi file.
   //
   chkRMI.fccType = FOURCC_MMAP;
   chkRMI.cksize  = 0L;
   mmr = mmioCreateChunk(hmmioRMI, &chkRMI, MMIO_CREATELIST);

   if (MMSYSERR_NOERROR != mmr)
   {
      mmr = MAPADDERR_BADWRITE;
      goto AddMIF2RMI_Exit;
   }

   // Try and write the infromation to the rmi file.
   //
   dw = (DWORD)mmioWrite(hmmioRMI, hp, cbData);

   if (cbData != dw)
   {
      // Could not write all of the information.
      //
      mmr = MAPADDERR_BADWRITE;
      goto AddMIF2RMI_Exit;
   }

   // Free the data.
   //
   GlobalFreePtr(hp);

   // Ascend out of the new list chunk to fix the size.
   //
   mmr = mmioAscend(hmmioRMI, &chkRMI, 0);

   mmr = mmioFlush(hmmioRMI, 0);

   // Close the rmi file.
   //
   mmr = mmioClose(hmmioRMI, 0);

   // No errors.
   //
   mmr = MMSYSERR_NOERROR;

AddMIF2RMI_Exit:

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


//************************************************************************
//**
//**  IsStandardMidi();
//**
//**  DESCRIPTION:
//**     This function will check to see if the Midi file is a standard
//**     Midi file.
//**
//**  ARGUMENTS:
//**     UINT  u     -  The index of the file to check.
//**
//**  RETURNS:
//**     BOOL           TRUE if the file is standard Midi, 
//**                    FALSE if it is not standard Midi.
//**
//**  HISTORY:
//**     05/09/93       created.
//**
//************************************************************************

BOOL NEAR PASCAL IsStandardMidi(
   UINT  u)
{
   HMMIO hmmio;
   BOOL  f;
   DWORD dw;
   
   // Open the file to check.
   //
   hmmio = mmioOpen(gpFiles[u].pszMidiFile, NULL, MMIO_READ);

   // Read the first four bytes of data.
   //
   mmioRead(hmmio, (HPSTR)(DWORD FAR *)&dw, sizeof(DWORD));

   // Close the file.
   //
   mmioClose(hmmio, 0);

   // Check for the Standard Midi File signature.
   //
   f = (FOURCC_MThd == dw) ? TRUE : FALSE;

   return(f);

} //** IsStandardMidi()


//************************************************************************
//**
//**  IsRMID();
//**
//**  DESCRIPTION:
//**     This function will check to see if a file is a Midi file in a 
//**     RIFF.
//**
//**  ARGUMENTS:
//**     UINT u      -  The index of the file to check.
//**
//**  RETURNS:
//**     BOOL           TRUE if the file is a RIFF Midi file (RMID),
//**                    FALSE if it is not.
//**
//**  HISTORY:
//**     05/09/93       created
//**
//************************************************************************

BOOL NEAR PASCAL IsRMID(
   UINT u)
{
   HMMIO    hmmio;
   MMCKINFO chk;
   MMRESULT mmr;

   // Open the file to check.
   //
   hmmio = mmioOpen(gpFiles[u].pszMidiFile, NULL, MMIO_READ);

   // Do we have an true rmi RIFF file?
   //
   chk.fccType = FOURCC_RMID;
   mmr = mmioDescend(hmmio, &chk, NULL, MMIO_FINDRIFF);

   // Close the file.
   //
   mmioClose(hmmio, 0);

   return(!((BOOL)mmr));
} //** IsRMID()



//************************************************************************
//**
//**  SaveFile();
//**
//**  DESCRIPTION:
//**     Will first check if the file needs to be converted to an rmi 
//**     file.  Then it will add the mif file to the rmi file.
//**
//**  ARGUMENTS:
//**     UINT  u  -  Index into our array of files for the file to save.
//**
//**  RETURNS:
//**     MMRESULT    MMSYSERR_NOERROR if successful,
//**                 Otherwise an error code.
//**
//**  HISTORY:
//**     05/08/93       created.
//**
//************************************************************************

MMRESULT NEAR PASCAL SaveFile(
   UINT  u)
{
   MMRESULT mmr;

   // Determine what type of file it is, 
   // and if any conversion is necessary.
   //
   if (IsStandardMidi(u))
   {
      mmr = ConvertMID2RMI(u);

      if (MMSYSERR_NOERROR != mmr)
      {
         mmr = MAPADDERR_BADWRITE;
         goto SaveFile_Exit;
      }
   }
   else if (!IsRMID(u))
   {
      mmr = MAPADDERR_BADFILE;
      goto SaveFile_Exit;
   }

   // Now we need to add the mif file to the rmi file.
   //
   mmr = AddMIF2RMI(u);

   if (MMSYSERR_NOERROR != mmr)
   {
      // Something went wrong in the merging.
      //
      goto SaveFile_Exit;
   }

   mmr = MMSYSERR_NOERROR;

SaveFile_Exit:

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


//************************************************************************
//**
//**  GetMifFile();
//**
//**  DESCRIPTION:
//**     This function will bring up the common file open box to allow,
//**     the user to select the mif file to associate with the Midi file.
//**
//**  ARGUMENTS:
//**     UINT  u     -  Index of the Midi file.
//**
//**  RETURNS:
//**     MMRESULT       MMSYSERR_NOERROR if successful,
//**                    Otherwise an error code.
//**
//**  HISTORY:
//**     05/09/93       created.
//**
//************************************************************************

MMRESULT NEAR PASCAL GetMifFile(
   UINT  u)
{
   MMRESULT       mmr;
   char           szExtFilter[256];
   PSTR           psz; 
   OPENFILENAME   ofn;
   BOOL           f;

   // Get the extension filter for this application
   //
   LoadString(ghinst, IDS_OFN_EXT_FILTER2, szExtFilter, sizeof(szExtFilter));

   for (psz = &szExtFilter[0]; '\0' != *psz; psz++)
   {
      if ('!' == *psz)
         *psz = '\0';
   }

   //  initialize the OPENFILENAME members
   //
   memset(&ofn, 0, sizeof(OPENFILENAME));

   // Null out the Mif file name.
   //
   gpFiles[u].pszMifFile[0] = '\0';

   ofn.lStructSize      =  sizeof(OPENFILENAME);
   ofn.hwndOwner        =  ghwndMain;
   ofn.lpstrFilter      =  szExtFilter;
   ofn.nFilterIndex     =  1L;
   ofn.lpstrFile        =  gpFiles[u].pszMifFile;
   ofn.nMaxFile         =  MAX_STR_LEN;
   ofn.lpstrInitialDir  =  NULL;
   ofn.Flags            =  OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | 
                           OFN_ALLOWMULTISELECT;

   // Call the commdlg function.
   //
   f = GetOpenFileName(&ofn);

   if (!f)
   {
      // The user canceled the operation, or the buffer was to small.
      //
      mmr = MMSYSERR_NOERROR;
      goto GetMifFile_Exit;
   }

   // Update the list box to show the changes.
   //
   UpdateListbox();

   // Enable the save menu.
   //
   EnableMenuItem(GetMenu(ghwndMain),
                  IDM_FILESAVE,
                  MF_BYCOMMAND | MF_ENABLED);

   mmr = MMSYSERR_NOERROR;

GetMifFile_Exit:

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


//************************************************************************
//**
//**  MenuCommands();
//**
//**  DESCRIPTION:
//**     Process the menu commands entered by the user.
//**
//**  ARGUMENTS:
//**     WPARAM   wParam   -  Menu command.
//**     LPARAM   lParam   -  Menu command specific data.
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     05/07/93       created.
//**
//************************************************************************

VOID NEAR PASCAL MenuCommands(
   WPARAM   wParam,
   LPARAM   lParam)
{
   MMRESULT mmr;
   UINT     u;

   switch (wParam)
   {
      case IDM_FILEOPEN:
         // Open the file(s) to convert.
         //
         mmr = GetFiles();

         if (MMSYSERR_NOERROR != mmr)
         {
            // Could not load the files, explaing why.
            //
            NotifyUser(0, mmr);
            goto MenuCommands_Exit;
         }

         // Add the new files to the listbox.
         //
         UpdateListbox();

         break;

      case IDM_FILESAVE:
         // Update all of the files currently listed.
         //
         for (u = 0; u < guNumFiles; u++)
         {
            // Save the file
            //
            mmr = SaveFile(u);

            // Did we succeed?
            //
            if (MMSYSERR_NOERROR != mmr)
            {
               // There was an error while trying to save the file.
               //
               NotifyUser(0, mmr);
               break;
            }
         }

         // We are done saveing so clear the listbox.
         //
         ClearListbox();

         // Free the files.
         //
         GlobalFreePtr(gpFiles);
         gpFiles = NULL;

         // Disable the Save menu item.
         //
         EnableMenuItem(GetMenu(ghwndMain),
                        IDM_FILESAVE,
                        MF_BYCOMMAND | MF_GRAYED);

         break;

      case IDM_FILEEXIT:
         // Quit the application.
         //
         DestroyWindow(ghwndMain);

         break;

      case IDM_ABOUT:
         // Show the about box.
         //
         About();
         
         break;

      case ID_LISTBOX:

         // Did the user double click on a file?
         //
         if (LBN_DBLCLK == HIWORD(lParam))
         {
            // Get the curretnly selected file.
            //
            u = (UINT)SendMessage(ghwndListbox, LB_GETCURSEL, 0, 0L);

            // Allow them to select a mif file to associate.
            //
            mmr = GetMifFile(u);

            // Did we succeed?
            //
            if (MMSYSERR_NOERROR != mmr)
               NotifyUser(0, mmr);
         }

         break;
   }

MenuCommands_Exit:

   return;
} //** MenuCommands()


//************************************************************************
//**
//**  ReSizeListBox();
//**
//**  DESCRIPTION:
//**
//**
//**  ARGUMENTS:
//**     WORD  wWidth
//**     WORD  wHeight
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     05/07/93       created.
//**
//************************************************************************

VOID NEAR PASCAL ReSizeListBox(
   WORD  wWidth,
   WORD  wHeight)
{
   // Do we have a list box to size?
   //
   if (NULL != ghwndListbox)
   {
      // Resize the list box to fill the client 
      // are of our main window.
      //
      MoveWindow(ghwndListbox, 
                 0, giFontHeight, 
                 wWidth, wHeight - giFontHeight, FALSE);
   }
} //** ReSizeListBox()


//************************************************************************
//**
//**  ProcessDroppedFiles();
//**
//**  DESCRIPTION:
//**     This function will associate the dropped file with the items
//**     that are selected in the list box.
//**     NOTE! This function does not check to see if the file is a 
//**     valid .mif file. That is done when the file is saved.
//**
//**  ARGUMENTS:
//**     HDROP hdrop -  Handle to the dropped data.
//**
//**  RETURNS:
//**     VOID 
//**
//**  HISTORY:
//**     05/08/93       created.
//**
//************************************************************************

VOID NEAR PASCAL ProcessDroppedFiles(
   HDROP hdrop)
{
   UINT     u;
   UINT     uNumItems;
   HMENU    hmenu;
   int FAR  *piItems;
   char     achbuf[MAX_STR_LEN+1];

   // How many files were dropped?
   //
   u = DragQueryFile(hdrop, (UINT)-1, NULL, 0);

   // We only accept one file at a time.
   //
   if (u > 1)
   {
      MessageBox(NULL, "Only one .mif file can be include in a give midi file.",
                 NULL, MB_OK);
      goto ProcessDroppedFiles_Exit;
   }

   // Get the name of the file that was dropped.
   //
   DragQueryFile(hdrop, 0, achbuf, MAX_STR_LEN);

   // How many files are selected?
   //
   uNumItems = (UINT)SendMessage(ghwndListbox, LB_GETSELCOUNT, 0, 0L);

   if (0 == uNumItems)
   {
      // No files are selected, so we have nothing to associate
      // the maps with.
      //
      NotifyUser(0, MAPADDERR_NONESELECTED);
      goto ProcessDroppedFiles_Exit;
   }

   // Allocate memory for the maximum number of items selected.
   //
   piItems = (int FAR *)GlobalAllocPtr(GHND, sizeof(int) * uNumItems);

   // Did we get the memory?
   //
   if (NULL == piItems)
   {
      // Could not allocate memory for the list.
      //
      //
      NotifyUser(0, MAPADDERR_NOMEM);
      goto ProcessDroppedFiles_Exit;
   }

   // Get fill the array with the indecies of the items selected.
   //
   SendMessage(ghwndListbox, LB_GETSELITEMS, 
               (WPARAM)uNumItems, (LPARAM)(int FAR *)piItems);

   // Go through the array and set each item's pszMifFile to
   // the file that was dropped.
   //
   for (u = 0; u < uNumItems; u++)
      lstrcpy((LPSTR)gpFiles[piItems[u]].pszMifFile, (LPSTR)achbuf);

   // Free the memory for the items.
   //
   GlobalFreePtr(piItems);

   // Clear the current contents of the list box.
   // 
   ClearListbox();

   // Update the list box.
   //
   UpdateListbox();

   // Since we now have data to save, un-gray the save menu.
   //
   hmenu = GetMenu(ghwndMain);
   EnableMenuItem(hmenu, IDM_FILESAVE, MF_BYCOMMAND | MF_ENABLED);

ProcessDroppedFiles_Exit:

   DragFinish(hdrop);

   return;
} //** ProcessDroppedFiles()


//************************************************************************
//**
//**  MainProc();
//**
//**  DESCRIPTION:
//**     Processes the messages for the application.
//**
//**  ARGUMENTS:
//**     HWND     hwnd     -  Window that we process messages for.
//**     UINT     umsg     -  Message ID.
//**     WPARAM   wParam   -  Message dependant info.
//**     LPARAM   lParam   -  Message dependant info.
//**
//**  RETURNS:
//**     LRESULT  -  Return value depends upon the message.
//**
//**  HISTORY:
//**     04/22/93       created.
//**
//************************************************************************

LRESULT CALLBACK __export MainProc(
   HWND     hwnd,
   UINT     umsg,
   WPARAM   wParam,
   LPARAM   lParam)
{
   PAINTSTRUCT ps;

   switch (umsg)
   {
      case WM_CREATE:
         // Initialize some stuff.
         //
         InitStuff(hwnd);

         return(0L);

      case WM_SIZE:
         // Size the list box to the window size.
         //
         ReSizeListBox(LOWORD(lParam), HIWORD(lParam));

         return(0L);

      case WM_DROPFILES:

         // Some one is trying to drop a file on us.
         //
         ProcessDroppedFiles((HDROP)wParam);

         return(0L);

      case WM_COMMAND:
         // Process menu options.
         //
         MenuCommands(wParam, lParam);

         return(0L);

      case WM_PAINT:
         // Paint the main window if it's needed.
         //
         BeginPaint(hwnd, &ps);
         PaintItAll(ps.hdc);
         EndPaint(hwnd, &ps);

         return(0L);

      case WM_DESTROY:
         // Free the memory and kill the list box.
         //
         if (NULL != gpFiles)
            GlobalFreePtr(gpFiles);

         DestroyWindow(ghwndListbox);
         PostQuitMessage(0);

         return(0L);
   }

   return(DefWindowProc(hwnd, umsg, wParam, lParam));
} //** MainProc()

