|
|
/*
* 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 */
// - - - - - - - - -
|