windows-nt/Source/XPSP1/NT/shell/osshell/control/midi/midimap.c

3261 lines
120 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*
* 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 */
// - - - - - - - - -