/*
 * MIDIMAP.C
 *
 * Copyright (C) 1990 Microsoft Corporation.  All rights reserved.
 *
 * File I/O support routines for MIDI mapper and control panel.
 *
 * History:
 *
 * gregsi       30-Apr-91 midimap.ini -> midimap.cfg
 * t-mikemc     27-Sep-90 mapKeyMapInSetup, mapPatchMapInSetup, mapExists,
 *                        and mapGetUsageCount now all return DWORDS.
 *                        mapGetSize can return an error now.
 * t-mikemc     24-Sep-90 Added count to MMTABLE data structure.  Made
 *                        deleting setups and patchmaps update usage count
 *                        of patchmaps and keymaps, respectively.
 * t-mikemc     23-Sep-90 Finally got that wNameID taken out of memory
 *                        data structures.
 * t-mikemc     22-Sep-90 Made MmaperrWriteSetup look for invalid port ID's
 *                        and write "invalid" device names correctly.
 * t-mikemc     21-Sep-90 Mucked about with the mapEnumerate function to
 *                        allow for enumeration of ports accessed by a setup.
 * t-mikemc     31-Jul-90 Added volume mapping and mapGetUsageCount API.
 * t-mikemc     15-Jul-90 Slew-o-changes! Munged Write/Read/Enum/Delete
 *                        API's together into single functions.  Finished
 *                        up work on 'use-count' for patch and key maps.
 *                        Commented and generally cleaned up lots-o-code.
 * t-mikemc     05-Jul-90 Added mapPatchMapInSetup and mapKeyMapInSetup
 *                        functions.
 * t-mikemc     13-Jun-90 Removed wSize element from setup data structure.
 *                        Setups now read in size of each referenced
 *                        patchmap.
 * t-mikemc     20-May-90 Added DWORD dwFlags to all internal file data
 *                        structures.
 * t-mikemc     11-May-90 Changed enumeration functions so they send
 *                        name and description.
 * t-mikemc     19-Apr-90 Changed routines to use binary file format.
 * t-mikemc     17-Apr-90 Created.
 */

//      Please note that the use of "goto" statements in this module isn't
//      disgusting.
//
//      brucemo

//      Well it disgusts me.  I put readability before writeability.
//      The compiler generates no less than 17 messages saying that a
//      variable may be used before initialised, and that means that
//      either the thing is full of bugs (so Dijkstra was right) or
//      else it's having a tough time following the flow of the logic.
//      Me too.
//      For good measure, the optimisation in compilers is almost always
//      inhibited by goto.
//      LaurieGr

#include <windows.h>
#include <string.h>
#define MMNOMCI
#define MMNOJOY
#define MMNOSOUND
#define MMNOWAVE
#include <mmsystem.h>
#if defined(WIN32)
#include <port1632.h>
#endif //WIN32
#include "hack.h"
#include "midimap.h"
#include "midi.h"
#include "extern.h"

/*-=-=-=-=- Global Definitions  -=-=-=-=-*/

#define MM_VERSION      1               // mapfile version number

#define MM_NUMSETUP     100             // number of setups
#define MM_NUMPATCH     100             // number of patchmaps
#define MM_NUMKEY       100             // number of keymaps

#define MAP_FOPEN       1               // open file
#define MAP_FCREATE     2               // create/open file
#define MAP_FCLOSE      3               // close file

#define MAKEID(id)      ((LPSTR) (LONG) (id))

#define LSB             1               // LSB for usage byte
#define MSB             128             // MSB for usage byte

#define MAX_UNIQUENAME  32              // max length of unique name

/*-=-=-=-=- Internal Data Structures    -=-=-=-=-*/

#pragma pack(1)

// Internal file data structures

typedef struct midimapkey_tag {
                  WORD    wUsing;                 // number of patchmaps using this
                  BYTE    bKMap[MIDIPATCHSIZE];   // translate table for key map
                  DWORD   dwFlags;                // flags
} MMKEY;
typedef MMKEY UNALIGNED FAR *LPMMKEY;

typedef struct midimappatch_tag {
                  WORD    wUsing;                 // number of setups using this
                  BYTE    bVMax;                  // max volume scalar
                  WORD    wPMap[MIDIPATCHSIZE];   // lobyte=xlat table, hibyte=volume  // a PATCHARRAY?
                  DWORD   dwSize;                 // size of patchmap
                  WORD    idxKMapNames[MIDIPATCHSIZE];    // keymap name table indexes // a KEYARRAY?
                  DWORD   dwFlags;                // flags
} MMPATCH;
typedef MMPATCH UNALIGNED FAR *LPMMPATCH;

typedef struct midimapchannel_tag {
                  WORD    wChannel;               // port channel of device
                  BYTE    szDevice[MAXPNAMELEN];  // device name
                  WORD    idxPMapName;            // patchmap name table index
                  DWORD   dwFlags;                // flags
} MMCHANNEL;
typedef MMCHANNEL UNALIGNED FAR *LPMMCHANNEL;

typedef struct midimapsetup_tag {
                  MMCHANNEL chMap[16];            // array of channel maps
                  DWORD   dwFlags;                // flags
} MMSETUP;
typedef MMSETUP UNALIGNED FAR *LPMMSETUP;

typedef struct midimaptableentry_tag {
                  BYTE    szName[MMAP_MAXNAME];   // name of map
                  BYTE    szDesc[MMAP_MAXDESC];   // description of map
                  WORD    idxEntry;               // index of this entry in table
                  DWORD   doData;                 // file offset of data structure
} MMTABLEENTRY;
typedef MMTABLEENTRY UNALIGNED FAR *LPMMTABLEENTRY;

typedef struct midimaptableheader_tag {
                  WORD    wEntrys;                // number of entries in table
                  WORD    wUsed;                  // number of entries being used
} MMTABLEHEADER;
typedef MMTABLEHEADER UNALIGNED FAR *LPMMTABLEHEADER;

typedef struct midimapheader_tag {
                  WORD    wVersion;               // version number of file
                  DWORD   dwGarbage;              // garbage collection bytes
                  WORD    idxCurSetup;            // current setup table index
                  WORD    oSetup;                 // setup table offset
                  WORD    oPatch;                 // patchmap table offset
                  WORD    oKey;                   // keymap table offset
} MMHEADER;

// Internal memory data structures.

typedef struct midimaptable_tag {
                  WORD            wCount;         // times this table has been opened
                  MMTABLEHEADER   mmHeader;       // header for this table
                  HANDLE          hEntrys;        // handle to array of entrys
} MMTABLE;
typedef MMTABLE UNALIGNED FAR *LPMMTABLE;

typedef struct uniquemap_tag {
                  BYTE    szName[MAX_UNIQUENAME];// unique map or port name
                  DWORD   dwOffset;               // offset from base or device ID
                  HANDLE  hNext;                  // next one
} UNIQUEMAP;
typedef UNIQUEMAP UNALIGNED FAR *LPUNIQUEMAP;

#pragma pack()

/*-=-=-=-=- Function Prototypes -=-=-=-=-*/

#define STATIC /**/
STATIC MMAPERR  NEAR PASCAL     MmaperrAddMap(UINT, LPVOID, LPDWORD);
STATIC MMAPERR  NEAR PASCAL     MmaperrChangeUsing(UINT, UINT, int);
STATIC MMAPERR  NEAR PASCAL     MmaperrEnumPorts(ENUMPROC, UINT, HWND, LPSTR);
STATIC MMAPERR  NEAR PASCAL     MmaperrFileAccess(int, int);
STATIC VOID     NEAR PASCAL     VFreeUniqueList(LPHANDLE);
STATIC VOID     NEAR PASCAL     VFreeTable(UINT);
STATIC MMAPERR  NEAR PASCAL     MmaperrGarbage(UINT);
STATIC DWORD    NEAR PASCAL     DwGetMapSize(UINT, LPSTR);
STATIC DWORD    NEAR PASCAL     DwGetSetupSize(LPSTR);
STATIC LPMMTABLEENTRY NEAR PASCAL LpGetTableEntry(HANDLE, LPSTR);
STATIC LPSTR    NEAR PASCAL     LszGetUniqueAtOffset (DWORD, HANDLE);
STATIC DWORD    NEAR PASCAL     LiNotUnique(LPSTR, LPHANDLE, DWORD);
STATIC MMAPERR  NEAR PASCAL     MmaperrReadKeymap (LPSTR, LPMIDIKEYMAP);
STATIC DWORD    NEAR PASCAL     DwReadPatchmap(LPSTR, LPMIDIPATCHMAP, BOOL);
STATIC MMAPERR  NEAR PASCAL     MmaperrReadSetup(LPSTR, LPMIDIMAP);
STATIC MMAPERR  NEAR PASCAL     MmaperrReadTable(UINT);
STATIC MMAPERR  NEAR PASCAL     MmaperrWriteKeymap(LPMIDIKEYMAP);
STATIC MMAPERR  NEAR PASCAL     MmaperrWritePatchmap(PATCHMAP FAR*);
STATIC MMAPERR  NEAR PASCAL     MmaperrWriteSetup (SETUP FAR*);
STATIC MMAPERR  NEAR PASCAL     MmaperrWriteTabEntry(UINT,
                                        UINT, LPMMTABLEENTRY);
STATIC MMAPERR  NEAR PASCAL     MmaperrWriteTabHeader(UINT, LPMMTABLEHEADER);

/*-=-=-=-=- Global Constants    -=-=-=-=-*/
TCHAR RegEntry[] =
    TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Midimap");
TCHAR RegCurrent[] = TEXT("Mapping Name");

/*-=-=-=-=- Global Variables    -=-=-=-=-*/

char aszMapperPath[MAXPATHLEN+1]; // A Path Buffer to mapper file (real or temp)

static HANDLE   hPortList,      // Unique port names list.
                hPatchList,     // Unique patchmaps list.
                hKeyList;       // Unique keymaps list.
static HANDLE   hSetupTable,    // Setup table handle.
                hPatchTable,    // Patch table handle.
                hKeyTable;      // Key table handle.
static HFILE    iFile = HFILE_ERROR;    // File handle.
static UINT     ucFileOpen;     // times the (.CFG?) file has been opened

static BOOL     fEditing;

//      -       -       -       -       -       -       -       -       -

void FAR PASCAL mapConnect(LPSTR lpszTmpPath)
{
        // mapFileVersion will force the mapper to open the file
        // so we can change the filename from under it anyway
        lstrcpy(aszMapperPath,lpszTmpPath);
        fEditing = TRUE;
}

void FAR PASCAL mapDisconnect(void)
{
        // We can't leave the temporary file open.  Otherwise someone
        // might reference it after the applet disconnects.

        if (iFile != HFILE_ERROR)
        {
                ucFileOpen = 1; // Force it to close
                (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        }

        fEditing = FALSE;
}

/*
 * @doc INTERNAL
 *
 * @api int | mapFileVersion | This function obtains the version number of the
 *      user's midi data file.
 *
 * @rdesc Returns a DWORD, the high word of which is an error code.  If the
 *      error code is MMAPERR_SUCCESS, the low word contains the version
 *      number.
 */

DWORD FAR PASCAL mapFileVersion (void)
{
        MMHEADER        mmHeader;
        DWORD   dwRet;
        MMAPERR mmaperr;

        // This kludge is here in case MMSYSTEM crashes inside one of the
        // midimap functions that has the file open. In this case, iFile
        // will be non-null and the cpl applet will think there is no
        // midimap.cfg file.

        hPortList = NULL;
        hPatchList = NULL;
        hKeyList = NULL;
        hSetupTable = NULL;
        hPatchTable = NULL;
        hKeyTable = NULL;
        iFile = HFILE_ERROR;
        ucFileOpen = 0;
        mmaperr = MmaperrFileAccess(MAP_FOPEN,OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return MAKELONG(0, mmaperr);

        dwRet = ( _lread(iFile, (LPSTR)&mmHeader,sizeof(MMHEADER))
                    != sizeof(MMHEADER)
                )
                ? MAKELONG(0, MMAPERR_READ)
                : MAKELONG(mmHeader.wVersion, MMAPERR_SUCCESS);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return dwRet;
} /* mapFileVersion */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api BOOL | mapInitMapFile | This function initializes the user's
 *      midi data file.  It creates a file in the user's windows directory,
 *      and fills it with the necessary header information.  If the file
 *      exists, it will be truncated to zero length first.
 *
 * @rdesc Returns non-zero if successful, otherwise zero.
 */

//      Issues:
//
//      1.      Need to install "DeleteFile" code.

MMAPERR FAR PASCAL mapInitMapFile (void)
{
        SETUP           setup;
        MMHEADER        mmHeader;
        MMTABLEHEADER   mmtHeader;
        MMTABLEENTRY    mmtEntry;
        MMAPERR mmaperr;
        WORD            wNum        ;

        if ((mmaperr = MmaperrFileAccess(MAP_FCREATE,
                0)) != MMAPERR_SUCCESS)
                return mmaperr;
        mmHeader.wVersion = MM_VERSION;
        mmHeader.dwGarbage = 0L;
        mmHeader.idxCurSetup = 0;       // Set later in mapSetCurrentSetup
        mmHeader.oSetup = (wNum         = sizeof(MMHEADER));
        mmHeader.oPatch = ( wNum += sizeof(MMTABLEHEADER) + MM_NUMSETUP*sizeof(MMTABLEENTRY) );
        mmHeader.oKey = (wNum += sizeof(MMTABLEHEADER) + MM_NUMPATCH*sizeof(MMTABLEENTRY) );
        if (_lwrite(iFile, (LPSTR)&mmHeader, sizeof(MMHEADER))
           != sizeof(MMHEADER)
           ) {
                mmaperr = MMAPERR_WRITE;
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
exit01:         // Delete file here.
                return mmaperr;
        }
        _fmemset(&mmtEntry, 0, sizeof(MMTABLEENTRY));
        mmtHeader.wUsed = 0;
        {   UINT i;
            for (i = 0; i < 3; i++) {
                    switch (i) {
                    case 0:
                            mmtHeader.wEntrys = MM_NUMSETUP;
                            break;
                    case 1:
                            mmtHeader.wEntrys = MM_NUMPATCH;
                            break;
                    case 2:
                            mmtHeader.wEntrys = MM_NUMKEY;
                            break;
                    }
                    if (_lwrite(iFile, (LPSTR)&mmtHeader, sizeof(MMTABLEHEADER))
                       != sizeof(MMTABLEHEADER)
                       ) {
                            mmaperr = MMAPERR_WRITE;
                            goto exit00;
                    }
                    for (wNum         = mmtHeader.wEntrys; wNum        ; wNum        --)
                            if (_lwrite(iFile, (LPSTR)&mmtEntry,
                                    sizeof(MMTABLEENTRY)) !=
                                    sizeof(MMTABLEENTRY)) {
                                    mmaperr = MMAPERR_WRITE;
                                    goto exit00;
                            }
            }
        }
        _fmemset(&setup, 0, sizeof(SETUP));
        LoadString( hLibInst
                  , IDS_VANILLANAME
                  , setup.aszSetupName
                  , sizeof(setup.aszSetupName)
                  );
        LoadString( hLibInst
                  , IDS_VANILLADESC
                  , setup.aszSetupDescription
                  , sizeof(setup.aszSetupDescription)
                  );
        {   UINT wChan;
            for (wChan = 0; wChan < 16; wChan++) {
                    setup.channels[wChan].wDeviceID = MMAP_ID_NOPORT;
                    setup.channels[wChan].wChannel = (WORD)wChan;
            }
        }
        if ((mmaperr = MmaperrWriteSetup(&setup)) != MMAPERR_SUCCESS)
                goto exit00;
        if ((mmaperr = mapSetCurrentSetup(MAKEID(1))) != MMAPERR_SUCCESS)
                goto exit00;
        if ((mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0)) != MMAPERR_SUCCESS)
                goto exit01;            // 01, not 00.
        return MMAPERR_SUCCESS;
} /* mapInitMapFile */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api UINT | mapSetCurrentSetup | This function sets the name of the current
 *      setup.
 *
 * @parm LPSTR | lpSetupName | Specifies the name of a setup which you
 *      want to be the current setup.
 *
 * @rdesc The return value is currently undefined.
 */

//      Issues:
//
//      1.      None.

MMAPERR FAR PASCAL mapSetCurrentSetup (LPSTR lpSetupName)
{
        LPMMTABLEENTRY  lpmmEntry;
        MMHEADER        mmHeader;
        MMAPERR mmaperr;

        HKEY    hKey;
        LONG    lRet;

        /*
        ** See if the information is stored in the registry.
        ** If so write the stuff there.  Otherwise fall thru and try to
        ** write the stuff into the mapper file.
        */
        lRet = RegOpenKey( HKEY_LOCAL_MACHINE, RegEntry, &hKey );

        if ( lRet == ERROR_SUCCESS ) {

              lRet = RegSetValueEx( hKey, RegCurrent, 0L, REG_SZ,
                                    (LPBYTE)lpSetupName,
                                    sizeof(TCHAR) * (1 + lstrlen(lpSetupName)));
              RegCloseKey( hKey );

              if ( lRet == ERROR_SUCCESS) {
                  return MMAPERR_SUCCESS;
              }
        }

        if ((mmaperr = MmaperrFileAccess(MAP_FOPEN,
                OF_READWRITE)) != MMAPERR_SUCCESS)
                return mmaperr;
        if ((mmaperr = MmaperrReadTable(MMAP_SETUP)) != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        lpmmEntry = LpGetTableEntry(hSetupTable,
                lpSetupName);
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit01:         VFreeTable(MMAP_SETUP);
                goto exit00;
        }
        if (_lread(iFile, (LPSTR)&mmHeader,
                sizeof(MMHEADER)) != sizeof(MMHEADER)) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        mmHeader.idxCurSetup = lpmmEntry->idxEntry;
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_WRITE;
                goto exit01;
        }
        if (_lwrite(iFile, (LPSTR)&mmHeader,
                sizeof(MMHEADER)) != sizeof(MMHEADER)) {
                mmaperr = MMAPERR_WRITE;
                goto exit01;
        }
        VFreeTable(MMAP_SETUP);
        if ((mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0)) != MMAPERR_SUCCESS)
                return mmaperr;
        return MMAPERR_SUCCESS;
} /* mapSetCurrentSetup */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api LPSTR | mapGetCurrentSetup | This function retrieves the name of the
 * current setup.
 *
 * @parm LPSTR | lpBuf | Specifies a buffer into which the current setup
 *      name will be copied.
 *
 * @parm UINT | wSize | Specifies the size in bytes of <p lpBuf>.
 *
 * @rdesc Returns an MMAP error, or zero on success.
 *
 * @comm You should make sure that the supplied buffer and size are at
 *      least MMAP_MAXNAME characters (defined in MMSYSTEM.H).
 */

//      Issues:
//
//      1.      None.

MMAPERR FAR PASCAL mapGetCurrentSetup(
        LPSTR   lpBuf,
        UINT    uSize)
{
        LPMMTABLEENTRY  lpmmEntry;
        MMHEADER        mmHeader;
        MMAPERR mmaperr;
        LPSTR           lpEntryName;
        LPSTR           lp;

        HKEY    hKey;
        LONG    lRet;

        /*
        ** See if the information is stored in the registry.
        ** If so read the stuff from there.  Otherwise fall thru and try to
        ** get the stuff from the mapper file.
        */
        lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                             RegEntry,
                             0L,
                             KEY_QUERY_VALUE,
                             &hKey );

        if (lRet == ERROR_SUCCESS) {

            DWORD  dwType, dwLen;

            dwLen = uSize;

            lRet = RegQueryValueEx( hKey, RegCurrent, 0L, &dwType,
                                    (LPBYTE)lpBuf, &dwLen );
            RegCloseKey( hKey );

            if ( lRet == ERROR_SUCCESS) {
                return MMAPERR_SUCCESS;
            }
        }

        /* attempt to open file, read setup table */
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(MMAP_SETUP);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        // read the file header
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit01:         VFreeTable(MMAP_SETUP);
                goto exit00;
        }
        if (_lread(iFile, (LPSTR)&mmHeader, sizeof(MMHEADER))
           != sizeof(MMHEADER)
           ) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        lpmmEntry = LpGetTableEntry(hSetupTable, MAKEID(mmHeader.idxCurSetup)); // Always succeed.
        // lstrncmp (lpBuf, lpmmEntry->szName, uSize - 1);
        for (lp = lpBuf, lpEntryName = (LPSTR)(lpmmEntry->szName); --uSize; )
                *lp++ = *lpEntryName++;
        *lp = '\0';
        // free table, close file and leave
        VFreeTable(MMAP_SETUP);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return MMAPERR_SUCCESS;
} /* mapGetCurrentSetup */

//      -       -       -       -       -       -       -       -       -

MMAPERR FAR PASCAL mapReadSetup(
        LPSTR   lszSetupName,
        SETUP FAR*      lpSetup)
{
        LPMMCHANNEL     lpmmChan;
        LPMMTABLEENTRY  lpmmEntry;
        MMSETUP mmSetup;
        MMAPERR mmaperr;
        UINT    wNumDevs;
        BOOL    fNoPort;
        int     i;
        UINT    wDev;

        // open file, read in pertinent tables
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)  {
                return mmaperr;
        }
        fNoPort = FALSE;
        mmaperr = MmaperrReadTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        lpmmEntry = LpGetTableEntry(hSetupTable, lszSetupName);
        if (lpmmEntry == NULL) {
                mmaperr = MMAPERR_INVALIDSETUP;
exit01:         VFreeTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
                goto exit00;
        }
        // read in setup data from file
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        if (_lread(iFile, (LPSTR)&mmSetup, sizeof(MMSETUP)) != sizeof(MMSETUP)){
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        // copy over setup name, description, and flags
        lstrcpy(lpSetup->aszSetupName, (LPCSTR)(lpmmEntry->szName));
        lstrcpy(lpSetup->aszSetupDescription, (LPCSTR)(lpmmEntry->szDesc));
        lpSetup->dFlags = mmSetup.dwFlags;
        // grab the number of devices in the current environment
        wNumDevs = midiOutGetNumDevs();
        // set up a couple of optimization-pointers
        lpmmChan = (LPMMCHANNEL)(mmSetup.chMap); // MIPS silliness
        for (i = 0; i < 16; i++, lpmmChan++) {
                // convert device name to device ID.  If device name doesn't
                // exist in the current environment, set ID to MMAP_ID_NOPORT
                if (!*lpmmChan->szDevice)
                        wDev = wNumDevs;
                else
                    for (wDev = 0; wDev < wNumDevs; wDev++) {
                        MIDIOUTCAPS     moCaps;

                        midiOutGetDevCaps(wDev, &moCaps, sizeof(MIDIOUTCAPS));
                        if (!lstrcmpi( moCaps.szPname
                                     , (LPCSTR)(lpmmChan->szDevice)))
                                    break;
                    }
                // copy over channel and flag info
                lpSetup->channels[i].wChannel = lpmmChan->wChannel;
                lpSetup->channels[i].dFlags = lpmmChan->dwFlags;
                if (wDev < wNumDevs)
                        lpSetup->channels[i].wDeviceID = (WORD)wDev;
                else {
                        lpSetup->channels[i].wDeviceID = MMAP_ID_NOPORT;
                        // this error code only if there was a port
                        // name but it does not exist on the current system.
                        if (*lpmmChan->szDevice)
                                fNoPort = TRUE;
                }
                // if channel has no patchmap then on to next channel
                if (!lpmmChan->idxPMapName) {
                        lstrcpy(lpSetup->channels[i].aszPatchName, szNone);
                        continue;
                }
                lpmmEntry = LpGetTableEntry(hPatchTable, MAKEID(lpmmChan->idxPMapName));
                lstrcpy( lpSetup->channels[i].aszPatchName
                       , (LPCSTR)(lpmmEntry->szName));
        }
        VFreeTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
        MmaperrFileAccess(MAP_FCLOSE, 0);
        if (fNoPort)
                return MMAPERR_INVALIDPORT;
        return MMAPERR_SUCCESS;
}

MMAPERR FAR PASCAL mapReadPatchMap(
        LPSTR   lszPatchMapName,
        PATCHMAP FAR*   lpPatch)
{
        LPMMTABLEENTRY  lpmmEntry;
        MMPATCH mmPatch;
        UNALIGNED WORD  *lpwKey;
        MMAPERR mmaperr;
        int     i;

        // open file, read in pertinent tables
        mmaperr = MmaperrFileAccess(MAP_FOPEN,OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(MMAP_PATCH | MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        lpmmEntry = LpGetTableEntry(hPatchTable, lszPatchMapName);
        if (lpmmEntry == NULL) {
                mmaperr = MMAPERR_INVALIDPATCH;
exit01:         VFreeTable(MMAP_PATCH | MMAP_KEY);
                goto exit00;
        }
        // read in patch data from file
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        if ( _lread(iFile, (LPSTR)&mmPatch, sizeof(MMPATCH))
           != sizeof(MMPATCH)
           ) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        // copy data from file structure to memory structure
        lstrcpy(lpPatch->aszPatchMapName, (LPCSTR)(lpmmEntry->szName));
        lstrcpy(lpPatch->aszPatchMapDescription, (LPCSTR)(lpmmEntry->szDesc));
        // set up an optimization pointer
        lpwKey = mmPatch.idxKMapNames;
        for (i = 0; i < MIDIPATCHSIZE; i++, lpwKey++) {
                lpPatch->keymaps[i].bVolume = HIBYTE(mmPatch.wPMap[i]);
                lpPatch->keymaps[i].bDestination = LOBYTE(mmPatch.wPMap[i]);
                if (!*lpwKey) {
                        lstrcpy(lpPatch->keymaps[i].aszKeyMapName, szNone);
                        continue;
                }
                lpmmEntry = LpGetTableEntry(hKeyTable, MAKEID(*lpwKey));
                lstrcpy( lpPatch->keymaps[i].aszKeyMapName
                       , (LPCSTR)(lpmmEntry->szName));
        }
        // free table, close file and leave
        VFreeTable(MMAP_PATCH | MMAP_KEY);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return MMAPERR_SUCCESS;
}

//      -       -       -       -       -       -       -       -       -

/* @doc INTERNAL
 *
 * @api DWORD | mapRead | Read a map specified by <p lpName> into a buffer
 *      specified by <p lpvBuf>.  This includes any patchmaps or keymaps
 *      that the map may contain.
 *
 * @parm UINT | wFlag | Specifies the type of map to be read into memory.
 *      It may be any of the following:
 *
 *      @flag MMAP_SETUP | Read a setup.
 *      @flag MMAP_PATCH | Read a patchmap.
 *      @flag MMAP_KEY | Read a keymap.
 *
 * @parm LPSTR | lpName | Specifies the name of the map of which you
 *      want read into memory.
 *
 * @rdesc Returns an MMAP error code, or zero on success.
 */

//      Issues:
//
//      1.      None.

MMAPERR FAR PASCAL mapRead(
        UINT    uFlag,
        LPSTR   lpName,
        LPVOID  lpvBuf)
{
        switch (uFlag) {
                DWORD   dwRet;

        case MMAP_SETUP:
                return MmaperrReadSetup(lpName, (LPMIDIMAP)lpvBuf);
        case MMAP_PATCH:
                dwRet = DwReadPatchmap(lpName,(LPMIDIPATCHMAP)lpvBuf, FALSE);
                if (dwRet < MMAPERR_MAXERROR)
                        return LOWORD(dwRet);
                return MMAPERR_SUCCESS;
        case MMAP_KEY:
                return MmaperrReadKeymap(lpName, (LPMIDIKEYMAP)lpvBuf);
        }
} /* mapRead */

//      -       -       -       -       -       -       -       -       -

//      MmaperrReadSetup
//
//      Read a setup into a buffer.  This includes any patchmaps or keymaps
//      that the setup may contain.
//
//      NOTE:  This function will return MMAPERR_INVALIDPORT if the setup to
//      be read accesses ports that are not available on the system.  That is,
//      if it accesses ports not listed as a 'midix=xxx.drv' entry under the
//      [drivers] section in system.ini.  The entire setup WILL BE read into
//      memory but the uDeviceID for the channels with inaccessible ports will
//      be set to MMAP_ID_NOPORT.  This error also has NO EFFECT on the
//      active/inactive status of any such channels.
//
//      Issues:
//
//      1.      There's a comment about something breaking a segment
//              boundary in here, but if it does break the segment boundary
//              the way it's dealt with it looks like it will wipe out.
//
//      2.      Freeing the "hKeyList" chain may be unnecessary if the
//              patch map read routine cleans up after itself properly.

STATIC  MMAPERR NEAR PASCAL MmaperrReadSetup(
        LPSTR   lpName,
        LPMIDIMAP       lpmMap)
{
        LPMIDICHANNELMAP lpChan;
        LPMMCHANNEL     lpmmChan;
        LPMIDIPATCHMAP  lpPatch;
        LPMMTABLEENTRY  lpmmEntry;
        MMSETUP mmSetup;
        MIDIOUTCAPS     moCaps;
        MMAPERR mmaperr;
        DWORD   dwOffset;
        UINT    wNumDevs;
        BOOL    fNoPort;
        int     i;
        UINT    wDev;

        // open file, read in pertinent tables
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        fNoPort = FALSE;
        mmaperr = MmaperrReadTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        dwOffset = sizeof(MIDIMAP);
        lpmmEntry = LpGetTableEntry(hSetupTable, lpName);
        if (lpmmEntry == NULL) {
                mmaperr = MMAPERR_INVALIDSETUP;
exit01:         VFreeTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
                goto exit00;
        }
        // read in setup data from file
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        if (_lread(iFile, (LPSTR)&mmSetup, sizeof(MMSETUP)) != sizeof(MMSETUP)){
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        // copy over setup name, description, and flags
        lstrcpy(lpmMap->szName, (LPCSTR)(lpmmEntry->szName));
        lstrcpy(lpmMap->szDesc, (LPCSTR)(lpmmEntry->szDesc));
        lpmMap->dwFlags = mmSetup.dwFlags;
        // grab the number of devices in the current environment
        wNumDevs = midiOutGetNumDevs();
        // set up a couple of optimization-pointers
        lpChan = lpmMap->chMap;
        lpmmChan = (LPMMCHANNEL)(mmSetup.chMap); // MIPS silliness
        for (i = 0; i < 16; i++, lpChan++, lpmmChan++) {
                // convert device name to device ID.  If device name doesn't
                // exist in the current environment, set ID to MMAP_ID_NOPORT
                if (!*lpmmChan->szDevice)
                        wDev = wNumDevs;
                else
                        for (wDev = 0; wDev < wNumDevs; wDev++) {
                                midiOutGetDevCaps(wDev, &moCaps, sizeof(MIDIOUTCAPS));
                                if (!lstrcmpi(moCaps.szPname,
                                              (LPCSTR)(lpmmChan->szDevice)))
                                        break;
                        }
                // copy over channel and flag info
                lpChan->wChannel = lpmmChan->wChannel;
                lpChan->dwFlags = lpmmChan->dwFlags;
                if (wDev < wNumDevs)
                        lpChan->wDeviceID = (WORD)wDev;
                else {
                        lpChan->wDeviceID = MMAP_ID_NOPORT;
                        // this error code only if there was a port
                        // name but it does not exist on the current system.
                        if (*lpmmChan->szDevice)
                                fNoPort = TRUE;
                }
                // if channel has no patchmap then on to next channel
                if (!lpmmChan->idxPMapName)
                        continue;
                // channel has a patchmap - if its not unique, point offset
                // to first occurance, otherwise offset = dwOffset.
                // assuming this patchmap ID is valid!
                lpmmEntry = LpGetTableEntry(hPatchTable,
                        MAKEID(lpmmChan->idxPMapName));
                lpChan->oPMap = LiNotUnique( (LPSTR)(lpmmEntry->szName)
                                           , &hPatchList
                                           , dwOffset
                                           );
                if (lpChan->oPMap == -1L) {
                        mmaperr = MMAPERR_MEMORY;
exit02:                 VFreeUniqueList(&hPatchList);
                        VFreeUniqueList(&hKeyList);
                        goto exit01;
                } else if (!lpChan->oPMap) {
                        DWORD   dwRet;

                        lpChan->oPMap = dwOffset;
                        // setup patchmap pointer; could break segment bounds
                        lpPatch = (LPMIDIPATCHMAP)((LPSTR)lpmMap + dwOffset);
                        // read in patchmap (this also updates global offset)
                        dwRet = DwReadPatchmap(0L, lpPatch, TRUE);
                        if (dwRet < MMAPERR_MAXERROR) {
                                mmaperr = LOWORD(dwRet);
                                goto exit02;
                        }
                        dwOffset += dwRet;
                }
        }
        VFreeUniqueList(&hPatchList);
        VFreeUniqueList(&hKeyList);
        VFreeTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        if (fNoPort)
                return MMAPERR_INVALIDPORT;
        return MMAPERR_SUCCESS;
} /* MmaperrReadSetup */

//      -       -       -       -       -       -       -       -       -

//      DwReadPatchmap
//
//      Read a patchmap into a buffer.  This includes any keymaps the patchmap
//      may contain.
//
//      Remarks:
//
//      1.      Use of "fInSetup" flag is very bogus, may cause problems, is
//              probably wrong.

STATIC  DWORD NEAR PASCAL DwReadPatchmap(
        LPSTR   lpName,
        LPMIDIPATCHMAP  lpPatch,
        BOOL    fInSetup)
{
        LPMMTABLEENTRY  lpmmEntry;
        LPMIDIKEYMAP    lpKey;
        MMPATCH mmPatch;
        UNALIGNED WORD *lpwKey;
        MMAPERR mmaperr;
        DWORD   dwOffset;
        int     i;

        // open file, read in pertinent tables
        mmaperr = MmaperrFileAccess(MAP_FOPEN,OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return MAKELONG(mmaperr, 0);
        mmaperr = MmaperrReadTable(MMAP_PATCH |MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return MAKELONG(mmaperr, 0);
        }
        // save global offset then initialize it to patch size
        dwOffset = sizeof(MIDIPATCHMAP);
        lpmmEntry = LpGetTableEntry(hPatchTable, lpName);
        if (lpmmEntry == NULL) {
                mmaperr = MMAPERR_INVALIDPATCH;
exit01:         VFreeTable(MMAP_PATCH | MMAP_KEY);
                goto exit00;
        }
        // read in patch data from file
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        if (_lread(iFile, (LPSTR)&mmPatch,sizeof(MMPATCH)) != sizeof(MMPATCH)) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        // copy data from file structure to memory structure
        lstrcpy(lpPatch->szName, (LPCSTR)(lpmmEntry->szName));
        lstrcpy(lpPatch->szDesc, (LPCSTR)(lpmmEntry->szDesc));
        _fmemcpy((LPSTR)lpPatch->wPMap, (LPSTR)mmPatch.wPMap, MIDIPATCHSIZE * sizeof(WORD));
        _fmemset((LPSTR)lpPatch->okMaps, 0, MIDIPATCHSIZE * sizeof(DWORD));
        lpPatch->dwFlags = mmPatch.dwFlags;
        lpPatch->bVMax = mmPatch.bVMax;
        // set up an optimization pointer
        lpwKey = mmPatch.idxKMapNames;
        for (i = 0; i < MIDIPATCHSIZE; i++, lpwKey++) {
            // if no keymap for this patch then continue
            if (!*lpwKey)
                    continue;
            // patch has a keymap - if its unique point offset to
            // to first occurance, otherwise offset = dwOffset.
            // assuming this keymap ID is valid!
            lpmmEntry = LpGetTableEntry(hKeyTable, MAKEID(*lpwKey));
            lpPatch->okMaps[i] = LiNotUnique( (LPSTR)(lpmmEntry->szName)
                                            , &hKeyList, dwOffset);
            if (lpPatch->okMaps[i] == -1L) {
                mmaperr = MMAPERR_MEMORY;
                goto exit01;
            } else if (!lpPatch->okMaps[i]) {
                lpPatch->okMaps[i] = dwOffset;
                // set the keymap pointer; could break segment bounds
                lpKey = (LPMIDIKEYMAP)((LPSTR)lpPatch + dwOffset);
                // read in the keymap, update global offset
                mmaperr = MmaperrReadKeymap(0L, lpKey);
                if (mmaperr != MMAPERR_SUCCESS) {
                    VFreeUniqueList(&hKeyList);
                    goto exit01;
                }
                dwOffset += sizeof(MIDIKEYMAP);
            }
        }
        if (!fInSetup) {
                // if we're not called from ReadSetup, free the unique
                // keymap name list.
                VFreeUniqueList(&hKeyList);
        }
        // free table, close file and leave
        VFreeTable(MMAP_PATCH | MMAP_KEY);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return dwOffset;
} /* DwReadPatchmap */

//      -       -       -       -       -       -       -       -       -

//      MmaperrReadKeymap
//
//      Read a keymap into a buffer.
//
//      Remarks:
//
//      1.      None.

STATIC  MMAPERR NEAR PASCAL MmaperrReadKeymap(
        LPSTR   lpName,
        LPMIDIKEYMAP    lpKey)
{
        LPMMTABLEENTRY  lpmmEntry;
        MMKEY           mmKey;
        MMAPERR mmaperr;

        // open file, read in pertinent tables
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        // assuming keymap exists
        lpmmEntry = LpGetTableEntry(hKeyTable, lpName);
        // read in key data from file
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit01:         VFreeTable(MMAP_KEY);
                goto exit00;
        }
        if (_lread(iFile, (LPSTR)&mmKey, sizeof(mmKey)) != sizeof(mmKey)) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        // copy data from file structure to memory structure
        lstrcpy(lpKey->szName, (LPCSTR)(lpmmEntry->szName));
        lstrcpy(lpKey->szDesc, (LPCSTR)(lpmmEntry->szDesc));
        _fmemcpy(lpKey->bKMap, (LPCSTR)(mmKey.bKMap), MIDIPATCHSIZE * sizeof(BYTE));
        lpKey->dwFlags = mmKey.dwFlags;
        // free table, close file and leave
        VFreeTable(MMAP_KEY);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return MMAPERR_SUCCESS;
} /* MmaperrReadKeymap */

//      -       -       -       -       -       -       -       -       -

/* @doc INTERNAL
 *
 * @api DWORD | mapWrite | Write a map specified by <p lpvMap> to the
 *      midi data file.
 *
 * @parm UINT | uFlag | Specifies the type of map to write.
 *      It may be any of the following:
 *
 *      @flag MMAP_SETUP | Write a setup.
 *      @flag MMAP_PATCH | Write a patchmap.
 *      @flag MMAP_KEY   | Write a keymap.
 *
 * @parm LPVOID | lpvMap | Specifies the map to write to the midi data file.
 *
 * @rdesc Returns non-zero if it could do the write, zero if it failed.
 */

//      Remarks:
//
//      1.      None.

MMAPERR FAR PASCAL mapWrite(
        UINT    uFlag,
        LPVOID  lpvMap)
{
        switch (uFlag) {
        case MMAP_SETUP :
                return MmaperrWriteSetup((SETUP FAR*)lpvMap);
        case MMAP_PATCH :
                return MmaperrWritePatchmap((PATCHMAP FAR*)lpvMap);
        case MMAP_KEY :
                return MmaperrWriteKeymap((LPMIDIKEYMAP)lpvMap);
        }
} /* mapWrite */

//      -       -       -       -       -       -       -       -       -

//      MmaperrWriteSetup
//
//      Write a setup to the midi data file.

STATIC  MMAPERR NEAR PASCAL MmaperrWriteSetup(SETUP FAR* lpSetup)
{
        HANDLE          hPatchUsage;
        LPMMTABLE       lpmmTable;
        LPMMTABLEENTRY  lpmmEntry;
        LPMMCHANNEL     lpmmChan;
        LPBYTE          lpbPatchUsage = NULL;  // Kill spurious use before set diagnostic
        MMSETUP         mmOldSetup;
        MMSETUP         mmSetup;
        MIDIOUTCAPS     moCaps;
        MMAPERR mmaperr;
        DWORD   doData;
        UINT    wNumDevs;
        UINT    wOldDevs;
        UINT    uSize;
        BOOL    fExists;
        UINT    uIndex;
        UINT    wDev;

        // open file, read in pertinent tables
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READWRITE);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(MMAP_SETUP | MMAP_PATCH);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        // check to see if setup name already exists
        fExists = FALSE;
        lpmmEntry = LpGetTableEntry(hSetupTable, lpSetup->aszSetupName);
        if (lpmmEntry != NULL) {
            // setup name exists.  if the description has changed, we
            // have to re-write the table entry with new description
            if (lstrcmpi((LPCSTR)(lpmmEntry->szDesc)
                        , lpSetup->aszSetupDescription)) {
                lstrcpy( (LPSTR)(lpmmEntry->szDesc)
                       , lpSetup->aszSetupDescription);
                mmaperr = MmaperrWriteTabEntry(MMAP_SETUP, 0, lpmmEntry);
                if (mmaperr != MMAPERR_SUCCESS) {
exit01:             VFreeTable(MMAP_SETUP | MMAP_PATCH);
                    goto exit00;
                }
            }
            doData = lpmmEntry->doData;
            fExists = TRUE;
        }
        // name non-existent, so add a new table entry
        else if ((mmaperr = MmaperrAddMap(MMAP_SETUP, (LPVOID)lpSetup, &doData))
                != MMAPERR_SUCCESS
                )
                goto exit01;
        // zero-out the new setup data structure
        _fmemset((LPSTR)&mmSetup, 0, sizeof(MMSETUP));
        // obtain the number of entrys in the patch table
        lpmmTable = (LPMMTABLE)GlobalLock(hPatchTable);
        uSize = lpmmTable->mmHeader.wEntrys;
        GlobalUnlock(hPatchTable);
        // if there are any patchmaps
        hPatchUsage = NULL;
        if (uSize) {
                // Create table which is 'number-of-patchmaps' in length
                hPatchUsage = GlobalAlloc(GHND, (LONG)uSize);
                if (hPatchUsage == NULL) {
                        mmaperr = MMAPERR_MEMORY;
                        goto exit01;
                }
                lpbPatchUsage = (LPBYTE)GlobalLock(hPatchUsage);
                // if this is not a new map
                if (fExists) {
                        // read in old setup data from file
                        if (_llseek(iFile, doData, 0) == -1L) {
                                mmaperr = MMAPERR_READ;
exit02:                         if (hPatchUsage != NULL) {
                                        GlobalUnlock(hPatchUsage);
                                        GlobalFree(hPatchUsage);
                                }
                                goto exit01;
                        }
                        if ( _lread(iFile, (LPSTR)&mmOldSetup,sizeof(MMSETUP))
                           != sizeof(MMSETUP)
                           ) {
                                mmaperr = MMAPERR_READ;
                                goto exit02;
                        }
                        // set LSB of usage table indices where old setup
                        // referenced any respective patchmaps
                        for (uIndex = 0; uIndex < 16; uIndex++)
                        {
                            UINT    u;
                            u = mmOldSetup.chMap[uIndex].idxPMapName;
                            if (u)
                                 lpbPatchUsage[u - 1] = LSB;
                        }
                }
        }
        // get the number of devices
        wNumDevs = wOldDevs = midiOutGetNumDevs();
        // enumerate any invalid ports from old setup this is not a new map
        if (fExists) {
                uIndex = 0;
                lpmmChan = (LPMMCHANNEL)(mmOldSetup.chMap); // MIPS silliness
        } else {
                uIndex = 16;
                lpmmChan = 0;                           // wasn't set. LKG
        }
        for (; uIndex < 16; uIndex++, lpmmChan++) {
            // find out if the port is in the current environment
            wDev = *lpmmChan->szDevice ? 0 : wNumDevs;
            for (; wDev < wNumDevs; wDev++) {
                    midiOutGetDevCaps(wDev, &moCaps, sizeof(MIDIOUTCAPS));
                    if (!lstrcmpi(moCaps.szPname, (LPCSTR)(lpmmChan->szDevice)))
                            break;
            }
            // if not, add the unique port name to the invalid port list
            if (wDev == wNumDevs) {
                DWORD   dwUniqueRet;

                // unique-list entry will save this offset
                dwUniqueRet = LiNotUnique( (LPSTR)(lpmmChan->szDevice)
                                         , &hPortList
                                         , (DWORD)wOldDevs);
                if (dwUniqueRet == -1L) {
                        mmaperr = MMAPERR_MEMORY;
exit03:                 VFreeUniqueList(&hPortList);
                        goto exit02;
                } else if (!dwUniqueRet)
                        wOldDevs++;
            }
        }
        // copy over the data for each channel
        lpmmChan = (LPMMCHANNEL)(mmSetup.chMap); // MIPS silliness
        for (uIndex = 0; uIndex < 16; uIndex++, lpmmChan++) {
                // convert the device ID to a device name.
                if (lpSetup->channels[uIndex].wDeviceID == MMAP_ID_NOPORT)
                        *lpmmChan->szDevice = 0;
                else if (lpSetup->channels[uIndex].wDeviceID >= wNumDevs)
                    lstrcpy( (LPSTR)(lpmmChan->szDevice)
                           , LszGetUniqueAtOffset( (DWORD)lpSetup->channels[uIndex].wDeviceID
                                                 , hPortList
                                                 )
                           );
                else {
                        midiOutGetDevCaps( lpSetup->channels[uIndex].wDeviceID
                                         , &moCaps
                                         , sizeof(MIDIOUTCAPS)
                                         );
                        lstrcpy((LPSTR)(lpmmChan->szDevice), moCaps.szPname);
                }
                // copy over channel number and flags.
                lpmmChan->wChannel = lpSetup->channels[uIndex].wChannel;
                lpmmChan->dwFlags = lpSetup->channels[uIndex].dFlags;
                if (lpmmChan->dwFlags & MMAP_PATCHMAP) {
                        // do I want a uniqueness-check in here?
                        // get the table entry
                        lpmmEntry = LpGetTableEntry(hPatchTable,
                                lpSetup->channels[uIndex].aszPatchName);
                        // set patch name table entry index
                        lpmmChan->idxPMapName = lpmmEntry->idxEntry;
                        // set MSB at current table index where
                        // this setup references a patchmap
                        if (uSize)
                                lpbPatchUsage[lpmmChan->idxPMapName - 1] |= MSB;
                }
                // not needed if MMAP_PATCHMAP isn't there, seems clean though
                else
                        lpmmChan->idxPMapName = 0;
        }
        // if there are any patchmaps
        if (uSize) {
            // Check for differences in patchmap referencing.  Compare
            // what patchmaps are now being used to what patchmaps were
            // being used before.  Change the using count accordingly.

            for (uIndex = 0; uIndex < uSize; uIndex++, lpbPatchUsage++)
                if ((*lpbPatchUsage & MSB) &&
                    (!(*lpbPatchUsage & LSB))) {
                    mmaperr = MmaperrChangeUsing(MMAP_PATCH,uIndex + 1, 1);
                    if (mmaperr != MMAPERR_SUCCESS)
                            goto exit03;
                } else if (! (*lpbPatchUsage & MSB)
                          && (*lpbPatchUsage & LSB)
                          )
                    mmaperr = MmaperrChangeUsing(MMAP_PATCH,uIndex + 1, -1);
                    if (mmaperr != MMAPERR_SUCCESS)
                            goto exit03;
            // nuke the usage table
            GlobalUnlock(hPatchUsage);
            GlobalFree(hPatchUsage);
            hPatchUsage = NULL;
        }
        // copy over the setup flags.
        mmSetup.dwFlags = lpSetup->dFlags;
        // write the setup
        if (_llseek(iFile, doData, 0) == -1L) {
                mmaperr = MMAPERR_WRITE;
                goto exit03;
        }
        if ( _lwrite(iFile, (LPSTR)&mmSetup, sizeof(MMSETUP))
           != sizeof(MMSETUP)
           ) {
                mmaperr = MMAPERR_WRITE;
                goto exit03;
        }
        // free the unique invalid port name list
        VFreeUniqueList(&hPortList);
        // free tables, close file and leave
        VFreeTable(MMAP_SETUP | MMAP_PATCH);
        mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        return MMAPERR_SUCCESS;
} /* MmaperrWriteSetup */

//      -       -       -       -       -       -       -       -       -

//      MmaperrWritePatchmap
//
//      Write a patchmap to the midi data file.

STATIC  MMAPERR NEAR PASCAL MmaperrWritePatchmap(
        PATCHMAP FAR*   lpPatch)
{
        KEYMAP FAR*     keymap;
        HANDLE          hKeyUsage;
        LPMMTABLE       lpmmTable;
        LPMMTABLEENTRY  lpmmEntry;
        MMPATCH mmOldPatch;
        MMPATCH mmPatch;
        UNALIGNED WORD *lpidxNames;
        LPBYTE  lpbKeyUsage = NULL;  // Kill spurious use before set diagnostic
        DWORD   doData;
        DWORD   dwOffset;
        UINT    uSize = 0;
        BOOL    fExists = FALSE;
        MMAPERR mmaperr;
        UINT    uIndex;

        // open file, read in pertinent tables
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READWRITE);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(MMAP_PATCH | MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        // check to see if patch name already exists
        lpmmEntry = LpGetTableEntry(hPatchTable, lpPatch->aszPatchMapName);
        if (lpmmEntry != NULL) {
            // patch name exists.  if the description has changed, we
            // have to re-write the table entry with new description
            if (lstrcmpi( (LPSTR)(lpmmEntry->szDesc)
                        , lpPatch->aszPatchMapDescription)) {
                lstrcpy( (LPSTR)(lpmmEntry->szDesc)
                       , lpPatch->aszPatchMapDescription);
                mmaperr = MmaperrWriteTabEntry(MMAP_PATCH, 0, lpmmEntry);
                if (mmaperr != MMAPERR_SUCCESS) {
exit01:                 VFreeTable(MMAP_PATCH | MMAP_KEY);
                        goto exit00;
                }
            }
            doData = lpmmEntry->doData;
            fExists = TRUE;
        }
        // name is non-existent, so add a new table entry
        else {
                mmaperr = MmaperrAddMap(MMAP_PATCH,lpPatch, &doData);
                if (mmaperr != MMAPERR_SUCCESS)
                        goto exit01;
        }
        // zero-out the patch data structure.
        _fmemset((LPSTR)&mmPatch, 0, sizeof(MMPATCH));
        // obtain the number of entrys in the key table
        lpmmTable = (LPMMTABLE)GlobalLock(hKeyTable);
        uSize = lpmmTable->mmHeader.wEntrys;
        GlobalUnlock(hKeyTable);
        // Create table which is 'number-of-keymaps' in length
        hKeyUsage = NULL;
        if (uSize) {
                hKeyUsage = GlobalAlloc(GHND, (LONG)uSize);
                if (hKeyUsage == NULL) {
                        mmaperr = MMAPERR_MEMORY;
                        goto exit01;
                }
                lpbKeyUsage = (LPBYTE)GlobalLock(hKeyUsage);
        }
        // if not a new patchmap, we need old usage count
        if (fExists) {
                // read in old patch data from file
                if (_llseek(iFile, doData, 0) == -1L) {
                        mmaperr = MMAPERR_READ;
exit02:                 if (hKeyUsage != NULL) {
                                GlobalUnlock(hKeyUsage);
                                GlobalFree(hKeyUsage);
                        }
                        goto exit01;
                }
                if ( _lread(iFile, (LPSTR)&mmOldPatch, sizeof(MMPATCH))
                   != sizeof(MMPATCH)
                   ) {
                        mmaperr = MMAPERR_READ;
                        goto exit02;
                }
                mmPatch.wUsing = mmOldPatch.wUsing;
                // if there are keymaps and map is not new, set LSB
                // of usage table indices where old patchmap referenced
                // any respective keymaps
                if (uSize) {
                        lpidxNames = mmOldPatch.idxKMapNames;
                        for (uIndex = 0; uIndex < MIDIPATCHSIZE; uIndex++, lpidxNames++)
                                if (*lpidxNames)
                                        lpbKeyUsage[*lpidxNames - 1] = LSB;
                }
        }
        dwOffset = sizeof(MIDIPATCHMAP);
        // set max volume scalar to minimum value
        mmPatch.bVMax = 1;
        // copy over data for each patch entry
        keymap = lpPatch->keymaps;
        lpidxNames = mmPatch.idxKMapNames;
        for (uIndex = 0; uIndex < MIDIPATCHSIZE; uIndex++, keymap++, lpidxNames++) {
                DWORD   dwUniqueRet;

                mmPatch.wPMap[uIndex] = ((WORD)keymap->bVolume << 8) | keymap->bDestination;

                // if the current volume is greater than the max volume
                // then set max volume to it
                if (keymap->bVolume > mmPatch.bVMax)
                        mmPatch.bVMax = keymap->bVolume;

                // if patch has no keymap, zero out keymap table entry
                // index in file data structure and continue
                if (!lstrcmpi(keymap->aszKeyMapName, szNone)) {
                        *lpidxNames = 0;
                        continue;
                }
                // get the table entry
                lpmmEntry = LpGetTableEntry(hKeyTable, keymap->aszKeyMapName);
                // set keymap table entry index in file data structure
                *lpidxNames = lpmmEntry->idxEntry;
                // if keymap is unique, add size to global offset
                dwUniqueRet = LiNotUnique(keymap->aszKeyMapName, &hKeyList, dwOffset);
                if (dwUniqueRet == -1L) {
                        mmaperr = MMAPERR_MEMORY;
exit03:                 VFreeUniqueList(&hKeyList);
                        goto exit02;
                } else if (!dwUniqueRet) {
                        DWORD   dwSize;

                        dwSize = DwGetMapSize(MMAP_KEY,keymap->aszKeyMapName);
                        if (dwSize < MMAPERR_MAXERROR) {
                                mmaperr = LOWORD(dwSize);
                                goto exit03;
                        }
                        dwOffset += dwSize;
                        // set MSB at current usage table index where
                        // this patchmap references a keymap
                        if (uSize)
                                lpbKeyUsage[*lpidxNames - 1] |= MSB;
                }
        }
        // if there are any keymaps
        if (uSize) {
            // Check for differences in keymap referencing.  Compare
            // what keymaps are now being used to what keymaps were being
            // used before.  Change the using count accordingly.

            for (uIndex = 0; uIndex < uSize; uIndex++, lpbKeyUsage++)
                if ((*lpbKeyUsage & MSB) && (!(*lpbKeyUsage & LSB))) {
                    mmaperr = MmaperrChangeUsing(MMAP_KEY,uIndex + 1, 1);
                    if (mmaperr != MMAPERR_SUCCESS)
                            goto exit03;
                } else if (! (*lpbKeyUsage & MSB) &&
                    (*lpbKeyUsage & LSB))
                    mmaperr = MmaperrChangeUsing(MMAP_KEY, uIndex + 1, -1);
                    if (mmaperr != MMAPERR_SUCCESS)
                            goto exit03;
            // nuke the usage table
            GlobalUnlock(hKeyUsage);
            GlobalFree(hKeyUsage);
            hKeyUsage = NULL;
        }
        // set patch size to global offset.
        mmPatch.dwSize = dwOffset;
        // write the patchmap
        if (_llseek(iFile, doData, 0) == -1L) {
                mmaperr = MMAPERR_WRITE;
                goto exit03;
        }
        if ( _lwrite(iFile, (LPSTR)&mmPatch,sizeof(MMPATCH))
           != sizeof(MMPATCH)
           ) {
                mmaperr = MMAPERR_WRITE;
                goto exit03;
        }
        // free the unique keymap name list
        VFreeUniqueList(&hKeyList);
        // free tables, close file and leave
        VFreeTable(MMAP_PATCH | MMAP_KEY);
        mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        return MMAPERR_SUCCESS;
} /* MmaperrWritePatchmap */

//      -       -       -       -       -       -       -       -       -

//      MmaperrWriteKeymap
//
//      Write a keymap to the midi data file.
//
//      1.      If this routine fails it will corrupt the database.

STATIC  MMAPERR NEAR PASCAL MmaperrWriteKeymap (LPMIDIKEYMAP lpKey)
{
        LPMMTABLEENTRY  lpmmEntry;
        MMKEY   mmOldKey;
        MMKEY   mmKey;
        MMAPERR mmaperr;
        DWORD   doData;
        BOOL    fExists;

        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READWRITE);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        lpmmEntry = LpGetTableEntry(hKeyTable, lpKey->szName);
        if (lpmmEntry != NULL) {
            if (lstrcmpi((LPSTR)(lpmmEntry->szDesc), lpKey->szDesc)) {
                lstrcpy((LPSTR)(lpmmEntry->szDesc), lpKey->szDesc);
                mmaperr = MmaperrWriteTabEntry(MMAP_KEY, 0, lpmmEntry);
                if (mmaperr != MMAPERR_SUCCESS) {
exit01:             VFreeTable(MMAP_KEY);
                    goto exit00;
                }
            }
            doData = lpmmEntry->doData;
            fExists = TRUE;
        } else {
                mmaperr = MmaperrAddMap(MMAP_KEY, (LPVOID)lpKey, &doData);
                if (mmaperr != MMAPERR_SUCCESS)
                        goto exit01;
                mmKey.wUsing = 0;
                fExists = FALSE;
        }
        // zero-out the keymap data structure
        _fmemset((LPSTR)&mmKey, 0, sizeof(MMKEY));
        // if not a new keymap, get old usage count
        if (fExists) {
                if ( _llseek(iFile, doData, 0) == -1L) {
                        mmaperr = MMAPERR_READ;
                        goto exit01;
                }
                if ( _lread(iFile, (LPSTR)&mmOldKey, sizeof(MMKEY))
                   != sizeof(MMKEY)
                   ) {
                        mmaperr = MMAPERR_READ;
                        goto exit01;
                }
                mmKey.wUsing = mmOldKey.wUsing;
        }
        // copy over flags and keymap data
        mmKey.dwFlags = lpKey->dwFlags;
        _fmemcpy( (LPSTR)(mmKey.bKMap)
                , lpKey->bKMap
                , MIDIPATCHSIZE * sizeof(BYTE));
        if (_llseek(iFile, doData, 0) == -1L) {
                mmaperr = MMAPERR_WRITE;
                goto exit01;
        }
        if (_lwrite(iFile, (LPSTR)&mmKey, sizeof(MMKEY)) != sizeof(MMKEY)) {
                mmaperr = MMAPERR_WRITE;
                goto exit01;
        }
        VFreeTable(MMAP_KEY);
        mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        return MMAPERR_SUCCESS;
} /* MmaperrWriteKeymap */

//      -       -       -       -       -       -       -       -       -

//      MmaperrAddMap
//
//      Generic add map routine.
//      Add a map table entry, increment table use count.
//
//      Issues:
//
//      1.      None.

STATIC  MMAPERR NEAR PASCAL MmaperrAddMap(
        UINT    uFlag,
        LPVOID  lpvMap,
        LPDWORD lpdoData)
{
        HANDLE  hTable;
        LPMMTABLE       lpmmTable;
        LPMMTABLEENTRY  lpmmEntry;
        LPSTR   lpName;
        LPSTR   lpDesc;
        MMTABLEENTRY    mmEntry;
        MMAPERR mmaperr;
        DWORD   doData;
        WORD    wEntry;
        WORD    wEntrys;

        switch (uFlag) {
        case MMAP_SETUP :
                hTable = hSetupTable;
                lpName = ((SETUP FAR*)lpvMap)->aszSetupName;
                lpDesc = ((SETUP FAR*)lpvMap)->aszSetupDescription;
                break;
        case MMAP_PATCH :
                hTable = hPatchTable;
                lpName = ((PATCHMAP FAR*)lpvMap)->aszPatchMapName;
                lpDesc = ((PATCHMAP FAR*)lpvMap)->aszPatchMapDescription;
                break;
        case MMAP_KEY :
                hTable = hKeyTable;
                lpName = ((LPMIDIKEYMAP)lpvMap)->szName;
                lpDesc = ((LPMIDIKEYMAP)lpvMap)->szDesc;
                break;
        default:lpDesc = NULL;  // Kill spurious use before set diagnostic
                lpName = NULL;  // Kill spurious use before set diagnostic
                hTable = NULL;  // Kill spurious use before set diagnostic
        }
        lpmmTable = (LPMMTABLE)GlobalLock(hTable);
        wEntrys = lpmmTable->mmHeader.wEntrys;
        lpmmEntry = (LPMMTABLEENTRY)GlobalLock(lpmmTable->hEntrys);
        for (wEntry = 0; wEntry < wEntrys; wEntry++, lpmmEntry++)
                if (!*lpmmEntry->szName)
                        break;
        GlobalUnlock(lpmmTable->hEntrys);
        if (wEntry == wEntrys) {
                // Filled table;
                mmaperr = MMAPERR_FULL;
                goto exit00;
        }
        _fmemset(&mmEntry, 0, sizeof(MMTABLEENTRY));
        lstrcpy((LPSTR)(mmEntry.szName), lpName);
        lstrcpy((LPSTR)(mmEntry.szDesc), lpDesc);
        mmEntry.idxEntry = wEntry + 1;
        doData = mmEntry.doData = _llseek(iFile, 0L, 2);
        if (doData == -1L) {
                mmaperr = MMAPERR_WRITE;
exit00:         GlobalUnlock(hTable);
                return mmaperr;
        }
        mmaperr = MmaperrWriteTabEntry(uFlag, 0, &mmEntry);
        if (mmaperr != MMAPERR_SUCCESS)
                goto exit00;
        lpmmTable->mmHeader.wUsed++;
        mmaperr = MmaperrWriteTabHeader( uFlag
                                     , (LPMMTABLEHEADER)(&lpmmTable->mmHeader));
        if (mmaperr != MMAPERR_SUCCESS)
                goto exit00;
        GlobalUnlock(hTable);
        *lpdoData = doData;
        return MMAPERR_SUCCESS;
} /* MmaperrAddMap */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api DWORD | mapGetSize | This function retrieves the size in bytes
 *      that will be required to read an entire map into memory.  This
 *      includes the size of any patchmaps and keymaps that the map may
 *      contain.
 *
 * @parm UINT | uFlag | Specifies the type of map of which to get the size.
 *      It may be any of the following:
 *
 *      @flag MMAP_SETUP | Get the size of a setup.
 *      @flag MMAP_PATCH | Get the size of a patchmap.
 *      @flag MMAP_KEY | Get the size of a keymap.
 *
 * @parm LPSTR | lpName | Specifies the name of the map of which you
 *      want to get the size.
 *
 * @rdesc An MMAP error code, or the size in bytes required to read in the
 *      map specified by <p lpName>.  If the return value is greater than
 *      MMAPERR_MAXERROR then the return value is a size and not an error.
 */

//
//      This function returns either an error code or the size of the thing.
//      The way you tell the difference is to guess.  The best way to guess
//      is to compare the return value against MMAPERR_MAXERROR, and if it is
//      greater than or equal to that value you've got a size, not an error.
//
//      Issues:
//
//      1.      None.

DWORD   FAR PASCAL mapGetSize(
        UINT    uFlag,
        LPSTR   lpName)
{
        switch (uFlag) {
        case MMAP_SETUP :
                return DwGetSetupSize(lpName);
        case MMAP_PATCH :
        case MMAP_KEY :
                return DwGetMapSize(uFlag, lpName);
        }
} /* mapGetSize */

//      -       -       -       -       -       -       -       -       -

//      DwGetSetupSize
//
//      Get the size of a setup.
//
//      This function returns either an error code or the size of the thing.
//      The way you tell the difference is to guess.  The best way to guess
//      is to compare the return value against MMAPERR_MAXERROR, and if it is
//      greater than or equal to that value you've got a size, not an error.
//
//      Issues:
//
//      1.      None.

STATIC  DWORD NEAR PASCAL DwGetSetupSize(
        LPSTR   lpName)
{
        LPMMTABLEENTRY  lpmmEntry;
        LPMMCHANNEL     lpmmChan;
        MMAPERR mmaperr;
        MMSETUP mmSetup;
        DWORD   dwRet;
        DWORD   dwOffset;
        int     i;

        mmaperr = MmaperrFileAccess(MAP_FOPEN,OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return (DWORD)mmaperr;
        mmaperr = MmaperrReadTable(MMAP_SETUP | MMAP_PATCH);
        if (mmaperr != MMAPERR_SUCCESS) {
                dwRet = (DWORD)mmaperr;
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return dwRet;
        }
        // if name isn't in the file, get outta here
        lpmmEntry = LpGetTableEntry(hSetupTable, lpName);
        if (lpmmEntry == NULL) {
                dwRet = (DWORD)MMAPERR_INVALIDSETUP;
exit01:         VFreeTable(MMAP_SETUP | MMAP_PATCH);
                goto exit00;
        }
        // intialize offset
        dwOffset = sizeof(MIDIMAP);
        // read in the setup data
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                dwRet = (DWORD)MMAPERR_READ;
                goto exit01;
        }
        if (_lread(iFile, (LPSTR)&mmSetup,
                sizeof(MMSETUP)) != sizeof(MMSETUP)) {
                dwRet = (DWORD)MMAPERR_READ;
                goto exit01;
        }
        lpmmChan = (LPMMCHANNEL)(mmSetup.chMap); // MIPS silliness
        for (i = 0; i < 16; i++, lpmmChan++) {
                DWORD   dwUniqueRet;

                // if no patchmap then continue
                if (!lpmmChan->idxPMapName)
                        continue;
                // assuming patchmap actually exists here
                lpmmEntry = LpGetTableEntry(hPatchTable,
                        MAKEID(lpmmChan->idxPMapName));
                // if patchmap is unique, add size to global offset
                dwUniqueRet = LiNotUnique( (LPSTR)(lpmmEntry->szName)
                                         , &hPatchList
                                         , dwOffset);
                if (dwUniqueRet == -1L) {
                        dwRet = (DWORD)MMAPERR_MEMORY;
                        goto exit01;
                } else if (!dwUniqueRet) {
                        dwRet = DwGetMapSize(MMAP_PATCH,NULL);
                        if (dwRet < MMAPERR_MAXERROR) {
                                VFreeUniqueList(&hPatchList);
                                goto exit01;
                        }
                        dwOffset += dwRet;
                }
        }
        // free the unique patchmap list
        VFreeUniqueList(&hPatchList);
        VFreeTable(MMAP_SETUP | MMAP_PATCH);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return (dwOffset < (DWORD)MMAPERR_MAXERROR) ? (DWORD)MMAPERR_INVALIDSETUP : dwOffset;
} /* DwGetSetupSize */

//      -       -       -       -       -       -       -       -       -

//      DwGetMapSize
//
//      Get the size of a patchmap or keymap.
//
//      This function returns either an error code or the size of the thing.
//      The way you tell the difference is to guess.  The best way to guess
//      is to compare the return value against MMAPERR_MAXERROR, and if it is
//      greater than or equal to that value you've got a size, not an error.
//
//      Remarks:
//
//      1.      None.

STATIC  DWORD NEAR PASCAL DwGetMapSize(
        UINT    uFlag,
        LPSTR   lpName)
{
        LPMMTABLEENTRY lpmmEntry;
        LPHANDLE lphTable;
        MMPATCH mmPatch;
        MMAPERR mmaperr;
        DWORD   dwRet;

        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return (DWORD)mmaperr;
        mmaperr = MmaperrReadTable(uFlag);
        if (mmaperr != MMAPERR_SUCCESS) {
                dwRet = (DWORD)mmaperr;
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return dwRet;
        }
        lphTable = (uFlag == MMAP_PATCH) ? &hPatchTable : &hKeyTable;
        lpmmEntry = LpGetTableEntry(*lphTable, lpName);
        if (lpmmEntry == NULL) {
                dwRet = (uFlag == MMAP_PATCH)
                        ? (DWORD)MMAPERR_INVALIDPATCH
                        : (DWORD)MMAPERR_INVALIDKEY;
                VFreeTable(uFlag);
                goto exit00;
        }
        switch (uFlag) {
        case MMAP_PATCH :
                // patchmaps have the size stored in the file.
                if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                        dwRet = (DWORD)MMAPERR_READ;
                        break;
                }
                if ( _lread(iFile, (LPSTR)&mmPatch, sizeof(MMPATCH))
                   != sizeof(MMPATCH)
                   ) {
                        dwRet = (DWORD)MMAPERR_READ;
                        break;
                }
                dwRet = mmPatch.dwSize;
                if (dwRet < (DWORD)MMAPERR_MAXERROR)
                        dwRet = MMAPERR_INVALIDPATCH;
                break;
        case MMAP_KEY:
                // keymaps are all the same size.
                dwRet = sizeof(MIDIKEYMAP);
                break;
        default: dwRet = (DWORD)MMAPERR_READ;
        }
        VFreeTable(uFlag);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return dwRet;
} /* DwGetMapSize */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api DWORD | mapDelete | This function deletes a map from the midi
 *       data file.
 *
 * @parm UINT | uFlag | Specifies the type of map to be deleted.
 *      It may be any of the following:
 *
 *      @flag MMAP_SETUP | Delete a setup.
 *      @flag MMAP_PATCH | Delete a patchmap.
 *      @flag MMAP_KEY | Delete a keymap.
 *
 * @parm LPSTR | lpName | Specifies the name of the map to be deleted.
 *
 * @rdesc Returns a MMAP error code or zero on success.
 */

//      Issues:
//
//      1.      If this function fails it will corrupt the database.

MMAPERR FAR PASCAL mapDelete(
        UINT    uFlag,
        LPSTR   lpName)
{
        HANDLE          hUsage;
        LPMMCHANNEL     lpmmChan = NULL; // Kill spurious use before set diag
        LPMMTABLE       lpmmTable;
        LPMMTABLEHEADER lpmmHeader;
        LPMMTABLEENTRY  lpmmEntry;
        LPHANDLE        lphTable;
        LPHANDLE        lphUsageTable;
        UNALIGNED WORD  *lpidxNames = NULL;   // Kill spurious use before set diagnostic
        LPBYTE  lpbUsage;
        MMSETUP mmSetup;
        MMPATCH mmPatch;
        MMAPERR mmaperr;
        UINT    uGarbage;            // size of thing getting deleted
        UINT    uSize;
        UINT    uIdx;                // index of deleted entry in table
        UINT    uUsageFlag = 0;      // SETUP or PATCH => same as uFlag, else 0
        UINT    uNumPos;             // # of positions; setup=16, patch=128
        int     idxEntry;            // table index
        UINT    uIndex;

        // grab the appropriate table, structure size and usage info
        switch (uFlag) {
        case MMAP_SETUP:
                lphTable = &hSetupTable;
                uGarbage = sizeof(MIDIMAP);
                lphUsageTable = &hPatchTable;
                uUsageFlag = MMAP_PATCH;
                uNumPos = 16;
                break;
        case MMAP_PATCH:
                lphTable = &hPatchTable;
                lphUsageTable = &hKeyTable;
                uUsageFlag = MMAP_KEY;
                uGarbage = sizeof(MIDIPATCHMAP);
                uNumPos = MIDIPATCHSIZE;
                break;
        case MMAP_KEY:
                lphTable = &hKeyTable;
                uGarbage = sizeof(MIDIKEYMAP);
                uNumPos = 0;     // Kill spurious diagnostic
                lphUsageTable = NULL; // Kill spurious diagnostic
                break;
        default:uGarbage = 0;
                lphTable = NULL; // kill spurious use before set diagnostic
                lphUsageTable = NULL; // Kill spurious diagnostic
                uNumPos = 0;     // kill spurious use before set diagnostic
        }
        // open file, read in appropriate table(s)
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READWRITE);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(uFlag |uUsageFlag);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        lpmmEntry = LpGetTableEntry(*lphTable, lpName);
        if (lpmmEntry == NULL) {
                switch (uFlag) {
                case MMAP_SETUP:
                        mmaperr = MMAPERR_INVALIDSETUP;
                        break;
                case MMAP_PATCH:
                        mmaperr = MMAPERR_INVALIDPATCH;
                        break;
                case MMAP_KEY:
                        mmaperr = MMAPERR_INVALIDKEY;
                        break;
                }
exit01:         VFreeTable(uFlag | uUsageFlag);
                goto exit00;
        }
        // save the table index
        idxEntry = lpmmEntry->idxEntry;
        if (uUsageFlag) {
                // obtain the number of entrys in the usage table
                lpmmTable = (LPMMTABLE)GlobalLock(*lphUsageTable);
                uSize = lpmmTable->mmHeader.wEntrys;
                GlobalUnlock(*lphUsageTable);
                // read in setup or patchmap, set optimization pointer
                if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                        mmaperr = MMAPERR_READ;
                        goto exit01;
                }
                if (uUsageFlag == MMAP_PATCH) {
                        if (_lread(iFile, (LPSTR)&mmSetup,
                                sizeof(MMSETUP)) != sizeof(MMSETUP)) {
                                mmaperr = MMAPERR_READ;
                                goto exit01;
                        }
                        lpmmChan =(LPMMCHANNEL)(mmSetup.chMap); // MIPS silliness
                } else {
                        if (_lread(iFile, (LPSTR)&mmPatch,
                                sizeof(MMPATCH)) != sizeof(MMPATCH)) {
                                mmaperr = MMAPERR_READ;
                                goto exit01;
                        }
                        lpidxNames = mmPatch.idxKMapNames;
                }
                // create table which is 'number-of-maps' in length
                if (uSize) {
                    hUsage = GlobalAlloc(GHND, (LONG)uSize);
                    if (hUsage == NULL) {
                            mmaperr = MMAPERR_MEMORY;
                            goto exit01;
                    }
                    lpbUsage = (LPBYTE)GlobalLock(hUsage);
                    // set flags in table saying which patchmaps or
                    // keymaps this setup or patchmap references,
                    // respectively
                    for (uIndex = 0; uIndex < uNumPos; uIndex++) {
                        if (uUsageFlag == MMAP_PATCH) {
                            uIdx = lpmmChan->idxPMapName;
                            lpmmChan++;
                        } else {
                            uIdx = *lpidxNames;
                            lpidxNames++;
                        }
                        if (uIdx)
                            lpbUsage[uIdx - 1] = 1;
                    }
                    // go through table and decrement usage count
                    // of any entrys that are non zero
                    for (uIndex = 0; uIndex < uSize; uIndex++, lpbUsage++) {
                        if (!*lpbUsage)
                            continue;
                        mmaperr = MmaperrChangeUsing(uUsageFlag,uIndex + 1, -1);
//                                                              uIndex + -1, 1)
                        if (mmaperr != MMAPERR_SUCCESS) {
                            GlobalUnlock(hUsage);
                            GlobalFree(hUsage);
                            goto exit01;
                        }
                    }
                    GlobalUnlock(hUsage);
                    GlobalFree(hUsage);
                }
        }
        _fmemset((LPSTR)(lpmmEntry->szName), 0L, MMAP_MAXNAME);
        _fmemset((LPSTR)(lpmmEntry->szDesc), 0L, MMAP_MAXDESC);
        lpmmEntry->doData = 0L;
        lpmmEntry->idxEntry = 0L;
        mmaperr = MmaperrWriteTabEntry(uFlag, idxEntry, lpmmEntry);
        if (mmaperr != MMAPERR_SUCCESS)
                goto exit01;                    // Yes, 01, not 02.
        lpmmTable = (LPMMTABLE)GlobalLock(*lphTable);
        lpmmHeader = (LPMMTABLEHEADER)(&lpmmTable->mmHeader);
        lpmmHeader->wUsed--;
        mmaperr = MmaperrWriteTabHeader(uFlag,lpmmHeader);
        if (mmaperr != MMAPERR_SUCCESS) {
                GlobalUnlock(*lphTable);
                goto exit01;                    // Yes, 01, not 02.
        }
        GlobalUnlock(*lphTable);
        mmaperr = MmaperrGarbage(uGarbage);
        if (mmaperr != MMAPERR_SUCCESS)
                goto exit01;                    // Yes, 01, not 02.
        VFreeTable(uFlag | uUsageFlag);
        mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        return MMAPERR_SUCCESS;
} /* mapDelete */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api DWORD | mapEnumerate | This function enumerates the names and
 *      descriptions of all the maps of the specified type found in the
 *      midi data file, or the names and device id's of the ports a setup
 *      accesses.
 *
 * @parm UINT | uFlag | Specifies the type of enumerate to be performed.  It
 *       may be any one of the following values:
 *
 * @flag MMAP_SETUP | Enumerate setup names.
 * @flag MMAP_PATCH | Enumerate patchmap names.
 * @flag MMAP_KEY   | Enumerate keymap names.
 * @flag MMAP_PORTS | Enumerate ports in a setup.
 *
 * @parm ENUMPROC | lpfnCallback | Specifies the procedure-instance address of
 *      the callback function to be called with the name of each map.
 *
 * @parm DWORD | dwUser | If <p uFlag> is <f MMAP_SETUP>, <f MMAP_PATCH>, or
 *      <f MMAP_KEY>, this parameter specifies a user variable that will be
 *      passed to the callback function along with the name of each map.  If
 *      <p uFlag> is <f MMAP_PORTS>, this parameter specifies a far pointer
 *      to a setup name.
 *
 * @rdesc Returns a MMAP error code or zero on success.
 *
 * @comm <p lpfnCallBack> must be obtained using the <f MakeProcInstance>
 *      call, must use the pascal calling convention, must be declared FAR,
 *      and must be under the EXPORTS section in your applications .DEF file.
 *
 * @cb BOOL FAR PASCAL | EnumerateCallback | The callback function for
 *      <f mapEnumerate>.
 *
 * @parm LPSTR | lpName | If <p uFlag> is <f MMAP_SETUP>, <f MMAP_PATCH>, or
 *      <f MMAP_KEY>, this parameter specifies the name of a map of that type.
 *      If <p uFlag> is <f MMAP_PORTS>, this parameter is a number in the
 *      range of 1 to 16 which specifies the channel on which the supplied
 *      port <p lpDesc> is mapped.
 *
 * @parm LPSTR | lpDesc | If <p uFlag> is <f MMAP_SETUP>, <f MMAP_PATCH>, or
 *      <f MMAP_KEY>, this parameter specifies the description of the map in
 *      the <p lpName> parameter.  If <p uFlag> is <f MMAP_PORTS>, this
 *      parameter specifies the name of a port accessed by the setup.
 *
 * @parm DWORD | dwUser | If <p uFlag> is <f MMAP_SETUP>, <f MMAP_PATCH>, or
 *      <f MMAP_KEY>, this parameter specifies a user variable.  If <p uFlag>
 *      is <f MMAP_PORTS>, this parameter specifies the device ID of the
 *      supplied port <p lpDesc>.  In this case, if the port is not available
 *      in the current environment, the device ID is equal to the constant
 *      MMAP_ID_NOPORT.
 *
 * @rdesc It should return non-zero as long as it wants to continue being
 *      called with successive map or port names.  <f mapEnumerate> will stop
 *      enumerating when the callback function returns zero.
 */

//      Issues:
//
//      1.      It regards elements with a zero first byte as "not counting"
//              in the count of elements in a table.  If this is a bad
//              assertion, this routine has a bug.

MMAPERR FAR PASCAL mapEnumerate ( UINT uFlag
                                , ENUMPROC lpfnCallback
                                , UINT uCase        // passed to lpfnCallback
                                , HWND hCombo       // passed to lpfnCallback
                                , LPSTR lpSetupName // passed to lpfnCallback
                                )
{
        LPHANDLE        lphTable;
        LPMMTABLE       lpmmTable;
        LPMMTABLEENTRY  lpmmEntry;
        UINT            wEnum;
        UINT            wUsed;
        MMAPERR mmaperr;

        switch (uFlag) {
        case MMAP_SETUP :
                lphTable = &hSetupTable;
                break;
        case MMAP_PATCH :
                lphTable = &hPatchTable;
                break;
        case MMAP_KEY :
                lphTable = &hKeyTable;
                break;
        case MMAP_PORTS :
                return MmaperrEnumPorts(lpfnCallback, uCase, hCombo, lpSetupName);
        default: lphTable = NULL;  // kill spurious use before set diagnostic
        }
        mmaperr = MmaperrReadTable(uFlag);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        lpmmTable = (LPMMTABLE)GlobalLock(*lphTable);
        lpmmEntry = (LPMMTABLEENTRY)GlobalLock(lpmmTable->hEntrys);
        wUsed = lpmmTable->mmHeader.wUsed;
        for (wEnum = 0; wEnum < wUsed; lpmmEntry++) {
                if (!*lpmmEntry->szName)
                        continue;
                if (!(*lpfnCallback)( (LPSTR)lpmmEntry->szName
                                    , (LPSTR)lpmmEntry->szDesc
                                    , uCase
                                    , hCombo
                                    , lpSetupName
                                    )
                   )
                        break;
                wEnum++;
        }
        GlobalUnlock(lpmmTable->hEntrys);
        GlobalUnlock(*lphTable);
        VFreeTable(uFlag);
        return MMAPERR_SUCCESS;
} /* mapEnumerate */

//      -       -       -       -       -       -       -       -       -

//      MmaperrEnumPorts
//
//      Enumerate the ports in a setup.
//
//      Side-effects:
//
//      1.      None.
//
//      Remarks:
//
//      1.      If the enumeration function fails, this function won't
//              let you know.

STATIC  MMAPERR NEAR PASCAL MmaperrEnumPorts(
        ENUMPROC    lpfnCallback,
        UINT        uCase,          // unused
        HWND        hCombo,         // unused
        LPSTR       lpSetupName)
{
        LPMMTABLEENTRY  lpmmEntry;
        LPMMCHANNEL     lpmmChan;
        MIDIOUTCAPS     moCaps;
        MMSETUP mmSetup;
        MMAPERR mmaperr;
        UINT    wNumDevs;
        UINT    uDeviceID;
        int     i;

        // open file, read in setup table
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        mmaperr = MmaperrReadTable(MMAP_SETUP);
        if (mmaperr != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        // assuming setup actually exists
        lpmmEntry = LpGetTableEntry(hSetupTable, lpSetupName);
        if (lpmmEntry == NULL) {
                mmaperr = MMAPERR_INVALIDSETUP;
exit01:         VFreeTable(MMAP_SETUP);
                goto exit00;
        }
        // read in setup data from file
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        if (_lread(iFile, (LPSTR)&mmSetup, sizeof(MMSETUP)) != sizeof(MMSETUP)) {
                mmaperr = MMAPERR_READ;
                goto exit01;
        }
        // grab the number of devices in the current environment
        wNumDevs = midiOutGetNumDevs();
        // set an optimization-pointer
        lpmmChan = (LPMMCHANNEL)(mmSetup.chMap); // MIPS silliness
        for (i = 0; i < 16; i++, lpmmChan++) {
                // if no device name, then nothing to enumerate
                if (!*lpmmChan->szDevice)
                        continue;
                // convert device name to device ID.  If device name doesn't
                // exist in the current environment, set ID to MMAP_ID_NOPORT
                for (uDeviceID = 0; uDeviceID < wNumDevs; uDeviceID++) {
                        midiOutGetDevCaps(uDeviceID, &moCaps, sizeof(MIDIOUTCAPS));
                        if (!lstrcmpi( moCaps.szPname
                                     , (LPCSTR)(lpmmChan->szDevice)))
                                break;
                }
                if (uDeviceID == wNumDevs)
                        uDeviceID = MMAP_ID_NOPORT;
                if (!(*lpfnCallback)( (LPSTR)(DWORD)i + 1
                                    , (LPSTR)(lpmmChan->szDevice)
                                    , uCase         // garbage parameter
                                    , hCombo        // garbage parameter
                                    , (LPSTR)uDeviceID
                                    )
                   )
                        break;
        }
        // free table, close file and leave
        VFreeTable(MMAP_SETUP);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return MMAPERR_SUCCESS;
} /* MmaperrEnumPorts */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api DWORD | mapGetUsageCount | Get the usage count for a patchmap or
 *      keymap.
 *
 * @parm UINT | uFlag | Specifies the type of map to get the usage count for.
 *      It may be any one of the following values:
 *
 * @flag MMAP_PATCH | Get the usage count for a patchmap.
 * @flag MMAP_KEY | Get the usage count for a keymap.
 *
 * @parm LPSTR | lpName | Specifies the name of the map to get the usage
 *      count for.
 *
 * @rdesc Returns a MMAP error code, or the usage count for the map
 *      specified by <p lpName> in the high-order word.
 *
 * @comm Usage counts are used in order to determine whether it is okay to
 *      delete a patchmap or keymap.  If a setup accesses a patchmap, the
 *      usage count for that patchmap will be 1, and hence it should not be
 *      deleted.  The same is true for patchmaps accessing keymaps.  The
 *      mapDelete function does not look for or care about this number, so
 *      it is up to YOU to determine whether a patchmap or keymap has a zero
 *      usage count before calling mapDelete.
 */

//      Side-effects:
//
//      1.      None.

DWORD   FAR PASCAL mapGetUsageCount(
        UINT    uFlag,
        LPSTR   lpName)
{
        LPMMTABLEENTRY  lpmmEntry;
        MMPATCH mmPatch;
        MMKEY   mmKey;
        MMAPERR mmaperr;
        DWORD   dwRet;

        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return MAKELONG(mmaperr, 0);
        mmaperr = MmaperrReadTable(uFlag);
        if (mmaperr != MMAPERR_SUCCESS) {
                dwRet = MAKELONG(mmaperr, 0);
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return dwRet;
        }
        switch (uFlag) {
        case MMAP_PATCH:
                lpmmEntry = LpGetTableEntry(hPatchTable, lpName);
                if (lpmmEntry != NULL) {
                        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                                dwRet = MAKELONG(MMAPERR_READ, 0);
exit01:                         VFreeTable(uFlag);
                                goto exit00;
                        }
                        if ( _lread(iFile, (LPSTR)&mmPatch, sizeof(MMPATCH))
                           != sizeof(MMPATCH)
                           ) {
                                dwRet = MAKELONG(MMAPERR_READ, 0);
                                goto exit01;
                        }
                        dwRet = MAKELONG(MMAPERR_SUCCESS, mmPatch.wUsing);
exit02:                 VFreeTable(uFlag);
                        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                        return dwRet;
                } else {
                        dwRet = MAKELONG(MMAPERR_INVALIDPATCH, 0);
                        goto exit01;
                }
                break;          // This "break" is actually unreachable.
        case MMAP_KEY:
                lpmmEntry = LpGetTableEntry(hKeyTable, lpName);
                if (lpmmEntry != NULL) {
                        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                                dwRet = MAKELONG(MMAPERR_READ, 0);
                                goto exit01;
                        }
                        if (_lread(iFile, (LPSTR)&mmKey, sizeof(MMKEY))
                           != sizeof(MMKEY)
                           ) {
                                dwRet = MAKELONG(MMAPERR_READ, 0);
                                goto exit01;
                        }
                        dwRet = MAKELONG (MMAPERR_SUCCESS, mmKey.wUsing);
                        goto exit02;
                } else {
                        dwRet = MAKELONG(MMAPERR_INVALIDPATCH, 0);
                        goto exit01;
                }
                break;          // This "break" is actually unreachable.
        }
} /* mapGetUsageCount */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api DWORD | mapPatchMapInSetup | Determine if a patchmap is used within a
 *      setup.
 *
 * @parm LPSTR | lpPatchName | Specifies the name of the patchmap.
 *
 * @parm LPSTR | lpSetupName | Specifies the name of the setup.
 *
 * @rdesc Returns a MMAP error code, or non-zero in the high-order word if the
 *      given patchmap is used within the given setup.
 *
 * @xref mapKeyMapInSetup
 */

//      Side-effects:
//
//      1.      None.

DWORD   FAR PASCAL mapPatchMapInSetup(
        LPSTR   lpPatchName,
        LPSTR   lpSetupName)
{
        LPMMTABLEENTRY  lpmmEntry;
        LPMMCHANNEL     lpmmChan;
        MMSETUP mmSetup;
        DWORD   dwRet;
        MMAPERR mmaperr;
        int     i;

        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return MAKELONG(mmaperr, 0);
        mmaperr = MmaperrReadTable(MMAP_SETUP | MMAP_PATCH);
        if (mmaperr != MMAPERR_SUCCESS) {
                dwRet = MAKELONG(mmaperr, 0);
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return dwRet;
        }
        lpmmEntry = LpGetTableEntry(hSetupTable, lpSetupName);
        if (lpmmEntry == NULL) {
                dwRet = MAKELONG(MMAPERR_INVALIDSETUP, 0);
exit01:         VFreeTable(MMAP_SETUP | MMAP_PATCH);
                goto exit00;
        }
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                dwRet = MAKELONG(MMAPERR_READ, 0);
                goto exit01;
        }
        if (_lread(iFile, (LPSTR)&mmSetup, sizeof(MMSETUP)) != sizeof(MMSETUP)){
                dwRet = MAKELONG(MMAPERR_READ, 0);
                goto exit01;
        }
        lpmmChan = (LPMMCHANNEL)(mmSetup.chMap); // MIPS silliness
        for (i = 0; i < 16; i++, lpmmChan++) {
                if (!lpmmChan->idxPMapName)
                        continue;
                lpmmEntry = LpGetTableEntry(hPatchTable,
                        MAKEID(lpmmChan->idxPMapName));
                if (!lstrcmpi(lpPatchName, (LPCSTR)(lpmmEntry->szName)))
                        break;
        }
        VFreeTable(MMAP_SETUP | MMAP_PATCH);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return MAKELONG(MMAPERR_SUCCESS, (i < 16) ? 1 : 0);
} /* mapPatchMapInSetup */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api DWORD | mapKeyMapInSetup | Determine if a keymap is used within a
 *      setup.
 *
 * @parm LPSTR | lpKeyName | Specifies the name of the keymap.
 *
 * @parm LPSTR | lpSetupName | Specifies the name of the setup.
 *
 * @rdesc Returns a MMAP error code, or non-zero in the high-order word
 *      if the given keymap is used within the given patchmap.
 *
 * @xref mapPatchMapInSetup
 */

DWORD   FAR PASCAL mapKeyMapInSetup(
        LPSTR   lpKeyName,
        LPSTR   lpSetupName)
{
        LPMMTABLEENTRY  lpmmEntry;
        LPMMCHANNEL     lpmmChan;
        MMSETUP mmSetup;
        MMPATCH mmPatch;
        UNALIGNED WORD  *lpidxKMap;
        DWORD   dwRet;
        MMAPERR mmaperr;
        int     i;
        int     j;

        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return MAKELONG(mmaperr, 0);
        mmaperr = MmaperrReadTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
        if (mmaperr != MMAPERR_SUCCESS) {
                dwRet = MAKELONG(mmaperr, 0);
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return dwRet;
        }
        if ((lpmmEntry = LpGetTableEntry(hSetupTable, lpSetupName)) == NULL) {
                dwRet = MAKELONG(MMAPERR_INVALIDSETUP, 0);
exit01:         VFreeTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
                goto exit00;
        }
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                dwRet = MAKELONG(MMAPERR_READ, 0);
                goto exit01;
        }
        if (_lread(iFile, (LPSTR)&mmSetup, sizeof(MMSETUP)) != sizeof(MMSETUP)){
                dwRet = MAKELONG(MMAPERR_READ, 0);
                goto exit01;
        }
        lpmmChan = (LPMMCHANNEL)(mmSetup.chMap);  // MIPS silliness
        dwRet = MAKELONG(MMAPERR_SUCCESS, 0);
        for (i = 0; i < 16; i++, lpmmChan++) {
                DWORD   dwUniqueRet;

                if (!lpmmChan->idxPMapName)
                        continue;
                lpmmEntry = LpGetTableEntry(hPatchTable,
                        MAKEID(lpmmChan->idxPMapName));
                dwUniqueRet = LiNotUnique( (LPSTR)(lpmmEntry->szName)
                                         , &hPatchList
                                         , 0L);
                        //  0L in the "dwOffset" field  because it's never used.
                if (dwUniqueRet == -1L) {
                        dwRet = MAKELONG(MMAPERR_MEMORY, 0);
exit02:                 VFreeUniqueList(&hPatchList);
                        goto exit01;
                } else if (dwUniqueRet)
                        continue;
                if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                        dwRet = MAKELONG(MMAPERR_READ, 0);
                        goto exit02;
                }
                if ( _lread(iFile, (LPSTR)&mmPatch, sizeof(MMPATCH))
                   != sizeof(MMPATCH)
                   ) {
                        dwRet = MAKELONG(MMAPERR_READ, 0);
                        goto exit02;
                }
                lpidxKMap = mmPatch.idxKMapNames;
                for (j = 0; j < MIDIPATCHSIZE; j++, lpidxKMap++) {
                        if (!*lpidxKMap)
                                continue;
                        lpmmEntry = LpGetTableEntry(hKeyTable,
                                MAKEID(*lpidxKMap));
                        if (!lstrcmpi(lpKeyName, (LPCSTR)(lpmmEntry->szName))) {
                                dwRet = MAKELONG(MMAPERR_SUCCESS, 1);
                                break;
                        }
                }
                if (j < MIDIPATCHSIZE)
                        break;
        }
        VFreeUniqueList(&hPatchList);
        // free tables, close file and leave
        VFreeTable(MMAP_SETUP | MMAP_PATCH | MMAP_KEY);
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return dwRet;
} /* mapKeyMapInSetup */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api DWORD | mapExists | Deterine if a map exists by the given name.
 *
 * @parm UINT | uFlag | Specifies the type of map of which existence is
 *      to be determined. It may be any of the following:
 *
 *      @flag MMAP_SETUP | Determine if a setup exists.
 *      @flag MMAP_PATCH | Determine if a patchmap exists.
 *      @flag MMAP_KEY |  Determine if a keymap exists.
 *
 * @parm LPSTR | lpName | Specifies the name of the map.
 *
 * @rdesc Returns a MMAP error code in the low word.  The the call succeeds,
 *      returns non-zero in the high-order word if a map exists by the given
 *      name.
 */

//      Side-effects:
//
//      1.      None.

DWORD FAR PASCAL mapExists(
        UINT    uFlag,
        LPSTR   lpName)
{
        LPHANDLE        lphTable;
        LPMMTABLEENTRY  lpmmEntry;
        MMAPERR mmaperr;

        switch (uFlag) {
        case MMAP_SETUP:
                lphTable = &hSetupTable;
                break;
        case MMAP_PATCH:
                lphTable = &hPatchTable;
                break;
        case MMAP_KEY:
                lphTable = &hKeyTable;
                break;
        default:lphTable = NULL; // kill spurious use before sete diagnostic
        }
        if ((mmaperr = MmaperrReadTable(uFlag)) != MMAPERR_SUCCESS)
                return MAKELONG(mmaperr, 0);
        lpmmEntry = LpGetTableEntry(*lphTable, lpName);
        VFreeTable(uFlag);
        return MAKELONG(MMAPERR_SUCCESS, (lpmmEntry != NULL) ? 1 : 0);
} /* mapExists */

//      -       -       -       -       -       -       -       -       -

//      LiNotUnique
//
//      Return 0L if the supplied name is unique, and in this case add it to
//      the end of the list with the offset given in parm 3.
//
//      Return -1 if the GlobalAlloc fails so that the unique name
//      cannot be added.
//
//      Otherwise return an offset from the base of the current setup
//      or patchmap where the map exists, or the channel number which
//      accesses the invalid port.
//
//      Uniqueness is determined by the presence of the name on its respective
//      list; hPatchList for patchmap names; hKeyList for keymap names;
//      hPortList for port names.  It's up to the caller to give us the
//      right list to search.
//

STATIC  DWORD NEAR PASCAL LiNotUnique(
        LPSTR   lpName,
        LPHANDLE lphList,
        DWORD   dwOffset)
{
        LPUNIQUEMAP  lpumEntry = NULL; //; kill spurious use before set diag
        HANDLE  hCur;
        HANDLE  hPrev = NULL;

        for (hCur = *lphList; hCur;) {
                lpumEntry = (LPUNIQUEMAP)GlobalLock(hCur);
                if (!lstrcmpi((LPCSTR)(lpumEntry->szName), lpName)) {
                        DWORD   liRet;

                        liRet = lpumEntry->dwOffset;
                        GlobalUnlock(hCur);
                        return liRet;
                }
                hPrev = hCur;
                hCur = lpumEntry->hNext;
                if (hCur != NULL) {
                        GlobalUnlock(hPrev);
                }

        }
        hCur = GlobalAlloc(GHND, (DWORD)sizeof(UNIQUEMAP));
        if (hCur  == NULL) {
                if (hPrev != NULL) {
                        GlobalUnlock(hPrev);
                }
                return (DWORD)-1L;
        }
        if (hPrev != NULL) {
                lpumEntry->hNext = hCur;
                GlobalUnlock(hPrev);
        } else
                *lphList = hCur;
        lpumEntry = (LPUNIQUEMAP)GlobalLock(hCur);
        lstrcpy((LPSTR)(lpumEntry->szName), lpName);
        lpumEntry->dwOffset = dwOffset;
        lpumEntry->hNext = NULL;
        GlobalUnlock(hCur);
        return 0L;
} /* LiNotUnique */

//      -       -       -       -       -       -       -       -       -

//      LszGetUniqueAtOffset
//
//      Return a unique name given an offset.
//
//      Remarks:
//
//      1.      I don't think this can fail, although there's a weird case
//              if you run out of list.  In this case you get an empty string.
//              I'm not sure if this is bad or not.

STATIC  LPSTR NEAR PASCAL LszGetUniqueAtOffset(
        DWORD   dwOffset,
        HANDLE  hList)
{
        static char szUnique[MAX_UNIQUENAME];

        *szUnique = 0;
        while (hList) {
                HANDLE  hNext;
                LPUNIQUEMAP lpumEntry;

                lpumEntry = (LPUNIQUEMAP)GlobalLock(hList);
                if (dwOffset == lpumEntry->dwOffset) {
                        lstrcpy(szUnique, (LPCSTR)(lpumEntry->szName));
                        GlobalUnlock(hList);
                        break;
                }
                hNext = lpumEntry->hNext;
                GlobalUnlock(hList);
                hList = hNext;
        }
        return (LPSTR)szUnique;
} /* LszGetUniqueAtOffset */

//      -       -       -       -       -       -       -       -       -

//
//      VFreeUniqueList
//
//      Free up the memory associated with a unique name list.
//
//      Side-effects:
//
//      1.      None.

STATIC  VOID NEAR PASCAL VFreeUniqueList(
        LPHANDLE        lphList)
{
        // there was a code generation compiler bug
        // it thought that hPrev and *lphList were synonyms.  Worrying!!
        HANDLE          hPrev;
        HANDLE          hList;
        hList = *lphList;
        *lphList = NULL;
        for (; hList != NULL; ) {
                LPUNIQUEMAP     lpumEntry;

                hPrev = hList;
                lpumEntry = (LPUNIQUEMAP)GlobalLock(hPrev);
                hList = lpumEntry->hNext;
                GlobalUnlock(hPrev);
                GlobalFree(hPrev);
        }
} /* VFreeUniqueList */

//      -       -       -       -       -       -       -       -       -

//      MmaperrChangeUsing
//
//      Change the usage count of a patchmap or keymap.
//
//      Side-effects:
//
//      1.      None.

STATIC  MMAPERR NEAR PASCAL MmaperrChangeUsing(
        UINT    uFlag,
        UINT    uIdx,
        int     iVal)
{
        HANDLE  hTable;
        LPMMTABLEENTRY  lpmmEntry;
        MMPATCH mmPatch;
        MMAPERR mmaperr;
        MMKEY   mmKey;

        if ((mmaperr = MmaperrFileAccess(MAP_FOPEN,
                OF_READWRITE)) != MMAPERR_SUCCESS)
                return mmaperr;
        if ((mmaperr = MmaperrReadTable(uFlag)) != MMAPERR_SUCCESS) {
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        hTable = (uFlag == MMAP_PATCH) ? hPatchTable : hKeyTable;
        lpmmEntry = LpGetTableEntry(hTable,             // No error return
                MAKEID(uIdx));                          //  is possible.
        if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit01:         VFreeTable(uFlag);
                goto exit00;
        }
        switch (uFlag) {
        case MMAP_PATCH:
                if ( _lread(iFile, (LPSTR)&mmPatch, sizeof(MMPATCH))
                   != sizeof(MMPATCH)
                   ) {
                        mmaperr = MMAPERR_READ;
                        goto exit01;
                }
                mmPatch.wUsing += (WORD)iVal;
                if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                        mmaperr = MMAPERR_WRITE;
                        goto exit01;
                }
                if ( _lwrite(iFile, (LPSTR)&mmPatch, sizeof(MMPATCH))
                   != sizeof(MMPATCH)
                   ) {
                        mmaperr = MMAPERR_WRITE;
                        goto exit01;
                }
                break;
        case MMAP_KEY:
                if (_lread(iFile, (LPSTR)&mmKey, sizeof(MMKEY)) != sizeof(MMKEY)) {
                        mmaperr = MMAPERR_READ;
                        goto exit01;
                }
                mmKey.wUsing += (WORD)iVal;
                if (_llseek(iFile, lpmmEntry->doData, 0) == -1L) {
                        mmaperr = MMAPERR_WRITE;
                        goto exit01;
                }
                if (_lwrite(iFile, (LPSTR)&mmKey, sizeof(MMKEY)) != sizeof(MMKEY)) {
                        mmaperr = MMAPERR_WRITE;
                        goto exit01;
                }
                break;
        }
        VFreeTable(uFlag);
        if ((mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0)) != MMAPERR_SUCCESS)
                return mmaperr;
        return MMAPERR_SUCCESS;
} /* MmaperrChangeUsing */

//      -       -       -       -       -       -       -       -       -

//
//      MmaperrReadTable
//
//      Read in table(s) of names/descriptions from the midimap file.
//
//      Returns boolean.
//
//      Side-effects:
//
//      1.      None anymore.

STATIC  MMAPERR NEAR PASCAL MmaperrReadTable(
        UINT    APIn)
{
        MMHEADER        mmHeader;
        LPMMTABLE       lpmmTable;
        LPMMTABLEENTRY  lpmmEntry;
        LPHANDLE        lpTable;
        MMAPERR mmaperr;
        UINT    oPos;
        UINT    uTableSize;
        UINT    mmap;
        UINT    mmapCompleted;

        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READ);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        if (_lread(iFile, (LPSTR)&mmHeader, sizeof(MMHEADER)) != sizeof(MMHEADER)) {
                mmaperr = MMAPERR_READ;
                goto exit00;
        }
        mmapCompleted = 0;
        for (mmap = 1; mmap < 5; mmap <<= 1) {
                if (!(APIn & mmap))
                        continue;
                switch (mmap) {
                default:
                        continue;
                case MMAP_SETUP:
                        lpTable = &hSetupTable;
                        oPos = mmHeader.oSetup;
                        break;
                case MMAP_PATCH:
                        lpTable = &hPatchTable;
                        oPos = mmHeader.oPatch;
                        break;
                case MMAP_KEY:
                        lpTable = &hKeyTable;
                        oPos = mmHeader.oKey;
                        break;
                }
                if (*lpTable)
                        lpmmTable = (LPMMTABLE)GlobalLock(*lpTable);
                else {
                        *lpTable = GlobalAlloc(GHND, (DWORD)sizeof(MMTABLE));
                        if (*lpTable == NULL) {
                                mmaperr = MMAPERR_MEMORY;
exit01:                         VFreeTable(mmapCompleted);
                                goto exit00;
                        }
                        lpmmTable = (LPMMTABLE)GlobalLock(*lpTable);
                        if (_llseek(iFile, (LONG)oPos, 0) == -1L) {
                                mmaperr = MMAPERR_READ;
exit02:                         GlobalUnlock(*lpTable);
                                GlobalFree(*lpTable);
                                *lpTable = NULL;
                                goto exit01;
                        }
                        if ( _lread(iFile, (LPSTR)&lpmmTable->mmHeader,sizeof(MMTABLEHEADER))
                           != sizeof(MMTABLEHEADER)
                           ) {
                                mmaperr = MMAPERR_READ;
                                goto exit02;
                        }
                        uTableSize = lpmmTable->mmHeader.wEntrys *
                                sizeof(MMTABLEENTRY);
                        lpmmTable->hEntrys = GlobalAlloc( GHND
                                                        , (DWORD)uTableSize
                                                        );
                        if (lpmmTable->hEntrys == NULL) {
                                mmaperr = MMAPERR_MEMORY;
                                goto exit02;
                        }
                        lpmmEntry = (LPMMTABLEENTRY)GlobalLock(
                                                          lpmmTable->hEntrys);
                        if ( _lread(iFile, (LPSTR)lpmmEntry, uTableSize)
                           != uTableSize
                           ) {
                                mmaperr = MMAPERR_READ;
/*exit03:*/                     GlobalUnlock(lpmmTable->hEntrys);
                                GlobalFree(lpmmTable->hEntrys);
                                goto exit02;
                        }
                        GlobalUnlock(lpmmTable->hEntrys);
                }
                lpmmTable->wCount++;
                GlobalUnlock(*lpTable);
                mmapCompleted |= mmap;
        }
        (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
        return MMAPERR_SUCCESS;
} /* MmaperrReadTable */

//      -       -       -       -       -       -       -       -       -

//
//      VFreeTable
//
//      Free a table (or tables) from memory.
//
//      Side-effects:
//
//      1.      None.

STATIC  VOID NEAR PASCAL VFreeTable(
        UINT    APIn)
{
        LPMMTABLE       lpmmTable;
        LPHANDLE        lpTable;
        UINT    mmap;

        for (mmap = 1; mmap < 5; mmap <<= 1) {
                if (!(APIn & mmap))
                        continue;
                switch (mmap) {
                default:
                        continue;
                case MMAP_SETUP:
                        lpTable = &hSetupTable;
                        break;
                case MMAP_PATCH:
                        lpTable = &hPatchTable;
                        break;
                case MMAP_KEY:
                        lpTable = &hKeyTable;
                        break;
                }
                if (!*lpTable)
                        continue;
                lpmmTable = (LPMMTABLE)GlobalLock(*lpTable);
                if (--lpmmTable->wCount) {
                        GlobalUnlock(*lpTable);
                        continue;
                }
                GlobalFree(lpmmTable->hEntrys);
                GlobalUnlock(*lpTable);
                GlobalFree(*lpTable);
                *lpTable = NULL;
        }
} /* VFreeTable */

//      -       -       -       -       -       -       -       -       -

//
//      LpGetTableEntry
//
//      Given a name or index, return a table entry.  If lpName is NULL, it
//      assumes you have already called this and simply returns whatever
//      happens to be in the local static mmEntry.
//
//      Side-effects:
//
//      1.      None.
//
//      Remarks:
//
//      1.      This can't return an error if "lpName" is NULL, or if the
//              HIWORD of it is zero.

STATIC  LPMMTABLEENTRY NEAR PASCAL LpGetTableEntry(
        HANDLE  hTable,
        LPSTR   lpName         // Name or index
        )
{
        static MMTABLEENTRY mmEntry;
        LPMMTABLE       lpmmTable;
        LPMMTABLEENTRY  lpmmEntry;
        UINT    wNum        ;
        UINT    wUsed;

        if (lpName == NULL) {
                return &mmEntry;
        }
        lpmmTable = (LPMMTABLE)GlobalLock(hTable);
        lpmmEntry = (LPMMTABLEENTRY)GlobalLock(lpmmTable->hEntrys);
        wNum  = 0;
        if (!HIWORD(lpName)) {
                lpmmEntry += LOWORD ((LONG_PTR)lpName) - 1;
                wUsed = 1;
        } else {
                /* search through storage handled by lpmmTable->hEntrys for lpName */
                /* set wNum to its number and lpmmEntry to the entry               */
                wUsed = lpmmTable->mmHeader.wUsed;
                for (; wNum         < wUsed; lpmmEntry++) {
                        if (!*lpmmEntry->szName) {
                                continue;
                        }

                        if (!lstrcmpi((LPCSTR)(lpmmEntry->szName), lpName))
                                break;
                        wNum++;
                }
        }
        if (wNum < wUsed)
                mmEntry = *lpmmEntry;
        GlobalUnlock(lpmmTable->hEntrys);
        GlobalUnlock(hTable);
        if (wNum == wUsed)
                return NULL;
        return (LPMMTABLEENTRY)&mmEntry;
} /* LpGetTableEntry */

//      -       -       -       -       -       -       -       -       -

/*
 * @doc INTERNAL
 *
 * @api MMAPERR | MmaperrWriteTabEntry | Write a table entry to disk.
 *
 * uIdx may be a table entry index (1 ... n) or it may be zero if
 * you want to use lpmmEntry->idxEntry as the index.
 */

//      Side-effects of failure:
//
//      1.      If the "close" fails something bad has happened to the file.
//
//      Remarks:
//
//      1.      This function used to modify the in-memory table first, then
//              go on to tackle the on-disk entry.  It now does this in
//              reverse order in order to avoid a situation where the
//              in-memory table is changed but the disk representation isn't.

STATIC  MMAPERR NEAR PASCAL MmaperrWriteTabEntry(
        UINT    uFlag,
        UINT    uIdx,
        LPMMTABLEENTRY  lpmmEntry)
{
        HANDLE          hTable;
        LPMMTABLE       lpmmTable;
        LPMMTABLEENTRY  lpmmOldEntry;
        MMHEADER        mmHeader;
        UNALIGNED WORD  *lpoTable;
        MMAPERR mmaperr;

        switch (uFlag) {
        case MMAP_SETUP:
                lpoTable = &mmHeader.oSetup;
                hTable = hSetupTable;
                break;
        case MMAP_PATCH:
                lpoTable = &mmHeader.oPatch;
                hTable = hPatchTable;
                break;
        case MMAP_KEY:
                lpoTable = &mmHeader.oKey;
                hTable = hKeyTable;
                break;
        default:hTable = NULL;   // Kill spurious use before set diag
                lpoTable = NULL; // Kill spurious use before set diag
        }
        if (!uIdx)
                uIdx = lpmmEntry->idxEntry;
        uIdx--;
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READWRITE);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
exit01:         return mmaperr;
        }
        if ( _lread(iFile, (LPSTR)&mmHeader, sizeof(MMHEADER))
           != sizeof(MMHEADER)
           ) {
                mmaperr = MMAPERR_READ;
                goto exit00;                            // 00, not 01.
        }
        if (_llseek( iFile
                   , (LONG) ( *lpoTable
                            + sizeof(MMTABLEHEADER)
                            + uIdx * sizeof(MMTABLEENTRY)
                            )
                   , 0
                   ) == -1L
           ) {
                mmaperr = MMAPERR_WRITE;
                goto exit00;                            // 00, not 01.
        }
        if ( _lwrite(iFile, (LPSTR)lpmmEntry, sizeof(MMTABLEENTRY))
           != sizeof(MMTABLEENTRY)
           ) {
                mmaperr = MMAPERR_WRITE;
                goto exit00;                            // 00, not 01.
        }
        mmaperr = MmaperrFileAccess(MAP_FCLOSE, 0);
        if (mmaperr != MMAPERR_SUCCESS)
                goto exit01;
        // if the table is in memory, modify the in-memory table entry
        if (hTable) {
                lpmmTable = (LPMMTABLE)GlobalLock(hTable);
                lpmmOldEntry = (LPMMTABLEENTRY)GlobalLock(lpmmTable->hEntrys);
                lpmmOldEntry += uIdx;
                *lpmmOldEntry = *lpmmEntry;
                GlobalUnlock(lpmmTable->hEntrys);
                GlobalUnlock(hTable);
        }
        return MMAPERR_SUCCESS;
} /* MmaperrWriteTabEntry */

//      -       -       -       -       -       -       -       -       -

//      MmaperrWriteTabHeader
//
//      Write a table header.
//
//      Side-effects of failure:
//
//      1.      If the "close" fails something bad has happened to the file.

STATIC  MMAPERR NEAR PASCAL MmaperrWriteTabHeader(
        UINT    uFlag,
        LPMMTABLEHEADER lpmmHeader)
{
        MMHEADER        mmHeader;
        UNALIGNED WORD  *lpoTable;
        MMAPERR mmaperr;

        switch (uFlag) {
        case MMAP_SETUP:
                lpoTable = &mmHeader.oSetup;
                break;
        case MMAP_PATCH:
                lpoTable = &mmHeader.oPatch;
                break;
        case MMAP_KEY:
                lpoTable = &mmHeader.oKey;
                break;
        default:lpoTable = NULL; // Kill spurious use before set diagnostic
        }
        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READWRITE);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        if (_lread(iFile, (LPSTR)&mmHeader, sizeof(MMHEADER)) != sizeof(MMHEADER))
        {
                mmaperr = MMAPERR_READ;
                goto exit00;
        }
        if (_llseek(iFile, (LONG)*lpoTable, 0) == -1L) {
                mmaperr = MMAPERR_WRITE;
                goto exit00;
        }
        if ( _lwrite(iFile, (LPSTR)lpmmHeader, sizeof(MMTABLEHEADER))
           != sizeof(MMTABLEHEADER)
           ) {
                mmaperr = MMAPERR_WRITE;
                goto exit00;
        }
        return MmaperrFileAccess(MAP_FCLOSE, 0);
} /* MmaperrWriteTabHeader */

//      MmaperrGarbage
//
//      Increase the garbage-byte count.
//
//      Side-effects:
//
//      1.      None.

STATIC  MMAPERR NEAR PASCAL MmaperrGarbage(
        UINT    uBytes)
{
        MMHEADER mmHeader;
        MMAPERR mmaperr;

        mmaperr = MmaperrFileAccess(MAP_FOPEN, OF_READWRITE);
        if (mmaperr != MMAPERR_SUCCESS)
                return mmaperr;
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_READ;
exit00:         (VOID)MmaperrFileAccess(MAP_FCLOSE, 0);
                return mmaperr;
        }
        if (_lread(iFile, (LPSTR)&mmHeader, sizeof(MMHEADER)) != sizeof(MMHEADER)) {
                mmaperr = MMAPERR_READ;
                goto exit00;
        }
        mmHeader.dwGarbage += uBytes;
        if (_llseek(iFile, 0L, 0) == -1L) {
                mmaperr = MMAPERR_WRITE;
                goto exit00;
        }
        if (_lwrite(iFile, (LPSTR)&mmHeader, sizeof(MMHEADER)) != sizeof(MMHEADER)) {
                mmaperr = MMAPERR_WRITE;
                goto exit00;
        }
        return MmaperrFileAccess(MAP_FCLOSE, 0);
} /* MmaperrGarbage */

//      -       -       -       -       -       -       -       -       -

//
//      MmaperrFileAccess
//
//      Control the global file descriptor.
//
//      Returns TRUE on success, FALSE on error.
//
//      Remarks:
//
//      1.      Opens/closes file potentially.
//      2.      Modifies "iFile".
//      3.      Modifies "ucFileOpen".

MMAPERR NEAR PASCAL MmaperrFileAccess(
        int     iFunc,    /* MAP_FOPEN, MAP_FCREATE or MAP_FCLOSE */
        int     iMode)
{
    OFSTRUCT of;

    if (!fEditing)
    {
        char    szMapCfg[MMAP_MAXCFGNAME];

        GetSystemDirectory(aszMapperPath, sizeof(aszMapperPath));
        LoadString(hLibInst,IDS_MIDIMAPCFG,szMapCfg,MMAP_MAXCFGNAME);
        lstrcat(aszMapperPath, szMapCfg);
    }

    switch (iFunc) {
    case MAP_FOPEN :
        if (iFile != HFILE_ERROR) {
                ucFileOpen++;
                break;
        }
        iFile = OpenFile(aszMapperPath, &of, iMode);
        if (iFile == HFILE_ERROR) {
                if (of.nErrCode == 0x05)
                        return MMAPERR_OPEN_READONLY;
                else
                        return MMAPERR_OPEN;
        }
        ucFileOpen++;
        break;
    case MAP_FCREATE :
        iFile = OpenFile(aszMapperPath, &of,iMode|OF_CREATE|OF_READWRITE);
        if (iFile == HFILE_ERROR)
                return MMAPERR_CREATE;
        ucFileOpen++;
        break;
    case MAP_FCLOSE :
        if (!--ucFileOpen) {
                _lclose(iFile);
                iFile = HFILE_ERROR;
        }
        break;
    }
    return MMAPERR_SUCCESS;
} /* MmaperrFileAccess */

//      -       -       -       -       -       -       -       -       -
