|
|
/*
*************************************************************** * sprop.c * * Copyright (C) Microsoft, 1990, All Rights Reserved. * * Displays the Simple media properties * * History: * * July 1994 -by- VijR (Created) * *************************************************************** */
#include "mmcpl.h"
#include <windowsx.h>
#ifdef DEBUG
#undef DEBUG
#include <mmsystem.h>
#define DEBUG
#else
#include <mmsystem.h>
#endif
#include <mmddk.h>
#include <mmreg.h>
#include <commctrl.h>
#include <prsht.h>
#include <regstr.h>
#include "utils.h"
#include "medhelp.h"
#include "mmcpl.h"
#include <winerror.h>
//for digital cd audio
#include "devguid.h"
#include "setupapi.h"
#include "cfgmgr32.h"
#include "winioctl.h"
#include "tchar.h"
const TCHAR gszCreateCDFile[] = TEXT("\\\\.\\%c:");
#define MYREGSTR_PATH_MEDIA TEXT("SYSTEM\\CurrentControlSet\\Control\\MediaResources")
const TCHAR gszRegstrCDAPath[] = MYREGSTR_PATH_MEDIA TEXT("\\mci\\cdaudio"); const TCHAR gszUnitEnum[] = TEXT("%s\\unit %d"); const TCHAR gszSettingsKey[] = TEXT("Volume Settings"); const TCHAR gszDefaultCDA[] = TEXT("Default Drive");
const TCHAR gszRegstrCDROMPath[] = TEXT("System\\CurrentControlSet\\Services\\Class\\CDROM\\"); const TCHAR gszDigitalPlay[] = TEXT("DigitalAudioPlay");
const TCHAR gszRegstrDrivePath[] = TEXT("Enum\\SCSI");
#define CDA_VT_UNSET 0
#define CDA_VT_AUX 1
#define CDA_VT_MIX 2
#define CDROM_DIGITAL_PLAY_ENABLED 0x01
#define CDROM_DIGITAL_PLAY_CAPABLE 0x02
#define CDROM_DIGITAL_DEVICE_KNOWN 0x04
typedef struct { // This struct is used in other places DO NOT CHANGE
DWORD unit; DWORD dwVol; } CDAREG, *PCDAREG;
typedef struct { // This is now the local version with addition data, this can change, but has to keep the
CDAREG cdar; BOOLEAN fDigFlags; BOOLEAN DigitalEnabled; BOOLEAN DigitalKnownDevice; BOOLEAN oldDigEnabled; DWORD dwOldVol; TCHAR chDrive; HDEVINFO hDevInfo; PSP_DEVINFO_DATA pDevInfoData; } CDSTATE, *PCDSTATE;
BOOL g_fWDMEnabled = FALSE; HMODULE g_hModStorProp = NULL;
typedef LONG (WINAPI *CDROMISDIGITALPLAYBACKENABLEDPROC)(HDEVINFO,PSP_DEVINFO_DATA,BOOLEAN*); typedef BOOL (WINAPI *CDROMKNOWNGOODDIGITALPLAYBACKPROC)(HDEVINFO,PSP_DEVINFO_DATA); typedef LONG (WINAPI *CDROMENABLEDIGITALPLAYBACKPROC)(HDEVINFO,PSP_DEVINFO_DATA,BOOLEAN); typedef LONG (WINAPI *CDROMDISABLEDIGITALPLAYBACKPROC)(HDEVINFO,PSP_DEVINFO_DATA);
CDROMISDIGITALPLAYBACKENABLEDPROC _gCdromIsDigitalPlaybackEnabled = NULL; CDROMKNOWNGOODDIGITALPLAYBACKPROC _gCdromKnownGoodDigitalPlayback = NULL; CDROMENABLEDIGITALPLAYBACKPROC _gCdromEnableDigitalPlayback = NULL; CDROMDISABLEDIGITALPLAYBACKPROC _gCdromDisableDigitalPlayback = NULL;
void GetPrefInfo(PAUDIODLGINFO pai, HWND hDlg );
HANDLE GetHandleForDevice(LPCTSTR DeviceName) { int i = 0; TCHAR fakeDeviceName[MAX_PATH]; HANDLE h = INVALID_HANDLE_VALUE; BOOL success = FALSE; TCHAR buf[MAX_PATH]; while (!success && i < 10) { wsprintf(buf, TEXT("DISK_FAKE_DEVICE_%d_"), i++); success = DefineDosDevice(DDD_RAW_TARGET_PATH, buf, DeviceName); if (success) { _tcscpy(fakeDeviceName, TEXT("\\\\.\\")); _tcscat(fakeDeviceName, buf); h = CreateFile(fakeDeviceName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); DefineDosDevice(DDD_REMOVE_DEFINITION, buf, NULL); } } //end while
return h; }
HANDLE GetHandleForDeviceInst(DEVINST DevInst) { TCHAR DeviceName[MAX_PATH]; CONFIGRET cr; DWORD len = MAX_PATH;
cr = CM_Get_DevNode_Registry_Property(DevInst, CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME, NULL, DeviceName, &len, 0);
if (cr != CR_SUCCESS) { return 0; } return GetHandleForDevice(DeviceName); }
BOOL EnableCdromFunctions(HMODULE* pMod) { BOOL fRet = FALSE; if (!*pMod) { *pMod = LoadLibrary(TEXT("STORPROP.DLL")); }
if (*pMod) { _gCdromIsDigitalPlaybackEnabled = (CDROMISDIGITALPLAYBACKENABLEDPROC)GetProcAddress(*pMod,"CdromIsDigitalPlaybackEnabled"); _gCdromKnownGoodDigitalPlayback = (CDROMKNOWNGOODDIGITALPLAYBACKPROC)GetProcAddress(*pMod,"CdromKnownGoodDigitalPlayback"); _gCdromEnableDigitalPlayback = (CDROMENABLEDIGITALPLAYBACKPROC)GetProcAddress(*pMod,"CdromEnableDigitalPlayback"); _gCdromDisableDigitalPlayback = (CDROMDISABLEDIGITALPLAYBACKPROC)GetProcAddress(*pMod,"CdromDisableDigitalPlayback");
if ( (_gCdromIsDigitalPlaybackEnabled) && (_gCdromKnownGoodDigitalPlayback) && (_gCdromEnableDigitalPlayback) && (_gCdromDisableDigitalPlayback) ) { fRet = TRUE; } }
return fRet; }
BYTE CDAudio_GetSetDigitalFlags(PCDSTATE pcds, BYTE fSetFlags, BOOL fSet) { BYTE bFlags = 0; BOOLEAN bEnabled = FALSE;
if (EnableCdromFunctions(&g_hModStorProp)) { bFlags = 0;
if (fSet) { BOOLEAN bEnable = fSetFlags & CDROM_DIGITAL_PLAY_ENABLED; BOOLEAN bAlready = FALSE; _gCdromIsDigitalPlaybackEnabled(pcds->hDevInfo,pcds->pDevInfoData,&bAlready);
if (bEnable != bAlready) { if (bEnable) { _gCdromEnableDigitalPlayback(pcds->hDevInfo,pcds->pDevInfoData,FALSE); } else { _gCdromDisableDigitalPlayback(pcds->hDevInfo,pcds->pDevInfoData); } } }
//always do a get after a set
_gCdromIsDigitalPlaybackEnabled(pcds->hDevInfo,pcds->pDevInfoData,&bEnabled);
if (bEnabled) { bFlags |= CDROM_DIGITAL_PLAY_ENABLED; }
if (_gCdromKnownGoodDigitalPlayback(pcds->hDevInfo,pcds->pDevInfoData)) { bFlags |= CDROM_DIGITAL_DEVICE_KNOWN; } }
return bFlags; }
BYTE CDAudio_SetDigitalFlags(PCDSTATE pcds, BYTE fDigFlags) { return CDAudio_GetSetDigitalFlags(pcds,fDigFlags,TRUE); }
BYTE CDAudio_GetDigitalFlags(PCDSTATE pcds) { return CDAudio_GetSetDigitalFlags(pcds,0,FALSE); }
/*
* */ void CDAudio_GetRegData(PCDSTATE pcds,ULONG uDrive) { TCHAR szRegstrCDAudio[_MAX_PATH]; HKEY hkTmp; if (!pcds) return; wsprintf(szRegstrCDAudio, gszUnitEnum, gszRegstrCDAPath, uDrive);
pcds->cdar.dwVol = 0xFF;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE , szRegstrCDAudio , 0 , KEY_READ , &hkTmp ) == ERROR_SUCCESS) { DWORD cbCDA = sizeof(CDAREG); RegQueryValueEx(hkTmp , gszSettingsKey , NULL , NULL , (LPBYTE)&pcds->cdar , &cbCDA); RegCloseKey(hkTmp); } pcds->cdar.unit = uDrive;
pcds->fDigFlags = CDAudio_GetDigitalFlags(pcds);
pcds->DigitalEnabled = pcds->fDigFlags & CDROM_DIGITAL_PLAY_ENABLED; pcds->DigitalKnownDevice = pcds->fDigFlags & CDROM_DIGITAL_DEVICE_KNOWN;
pcds->oldDigEnabled = pcds->DigitalEnabled; pcds->dwOldVol = pcds->cdar.dwVol; }
/*
* */ void CDAudio_SetRegData( PCDSTATE pcds, HWND hwnd) { TCHAR szRegstrCDAudio[_MAX_PATH]; HKEY hkTmp; BYTE bFlags = 0;
wsprintf(szRegstrCDAudio, gszUnitEnum, gszRegstrCDAPath, pcds->cdar.unit); if (RegCreateKeyEx(HKEY_LOCAL_MACHINE , szRegstrCDAudio , 0 , NULL , 0 , KEY_WRITE , NULL , &hkTmp , NULL ) == ERROR_SUCCESS) { RegSetValueEx(hkTmp , gszSettingsKey , 0L , REG_BINARY , (LPBYTE)&pcds->cdar , sizeof(CDAREG)); RegCloseKey(hkTmp); }
if (pcds->DigitalEnabled) { pcds->fDigFlags |= CDROM_DIGITAL_PLAY_ENABLED; } else { pcds->fDigFlags &= ~CDROM_DIGITAL_PLAY_ENABLED; }
bFlags = CDAudio_SetDigitalFlags(pcds,pcds->fDigFlags);
//check for success
if ((bFlags & CDROM_DIGITAL_PLAY_ENABLED) != (pcds->DigitalEnabled)) { pcds->DigitalEnabled = bFlags & CDROM_DIGITAL_PLAY_ENABLED; Button_SetCheck(GetDlgItem(hwnd, IDC_CDEN_DIGAUDIO), pcds->DigitalEnabled); } }
void ChangeCDVolume(PCDSTATE pcds) { MCI_OPEN_PARMS mciOpen; TCHAR szElementName[4]; TCHAR szAliasName[32]; DWORD dwFlags; DWORD dwAliasCount = GetCurrentTime(); DWORD dwRet; CDAREG cdarCache; HKEY hkTmp; TCHAR szRegstrCDAudio[_MAX_PATH]; ASSERT(pcds);
if (pcds != NULL) { wsprintf(szRegstrCDAudio, gszUnitEnum, gszRegstrCDAPath, pcds->cdar.unit);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE , szRegstrCDAudio , 0 , KEY_READ , &hkTmp ) == ERROR_SUCCESS) { DWORD cbCDA = sizeof(CDAREG); RegQueryValueEx(hkTmp , gszSettingsKey , NULL , NULL , (LPBYTE)&cdarCache , &cbCDA); RegCloseKey(hkTmp); } else { cdarCache = pcds->cdar; }
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegstrCDAudio, 0, NULL, 0, KEY_WRITE, NULL, &hkTmp, NULL ) == ERROR_SUCCESS) { CDAREG cdar; cdar = pcds->cdar; RegSetValueEx(hkTmp , gszSettingsKey , 0L , REG_BINARY , (LPBYTE)&cdar , sizeof(CDAREG)); RegCloseKey(hkTmp); }
ZeroMemory( &mciOpen, sizeof(mciOpen) );
mciOpen.lpstrDeviceType = (LPTSTR)MCI_DEVTYPE_CD_AUDIO; wsprintf( szElementName, TEXT("%c:"), pcds->chDrive ); wsprintf( szAliasName, TEXT("SJE%lu:"), dwAliasCount );
mciOpen.lpstrElementName = szElementName; mciOpen.lpstrAlias = szAliasName;
dwFlags = MCI_OPEN_ELEMENT | MCI_OPEN_ALIAS | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | MCI_WAIT;
dwRet = mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD_PTR)(LPVOID)&mciOpen);
if ( dwRet == MMSYSERR_NOERROR ) { mciSendCommand(mciOpen.wDeviceID, MCI_CLOSE, 0L, 0L ); }
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE , szRegstrCDAudio , 0 , KEY_WRITE , &hkTmp ) == ERROR_SUCCESS) { RegSetValueEx(hkTmp , gszSettingsKey , 0L , REG_BINARY , (LPBYTE)&cdarCache , sizeof(CDAREG)); RegCloseKey(hkTmp); } } }
/*
* */ void CDAudio_SaveState( HWND hwnd) { PCDSTATE pcds; pcds = (PCDSTATE)GetWindowLongPtr(hwnd,DWLP_USER);
if (pcds) { CDAudio_SetRegData(pcds,hwnd); pcds->oldDigEnabled = pcds->DigitalEnabled; pcds->dwOldVol = pcds->cdar.dwVol;
ChangeCDVolume(pcds); } }
void CDAudio_DigitalPlaybackEnable(HWND hDlg) { PCDSTATE pcds; pcds = (PCDSTATE)GetWindowLongPtr(hDlg,DWLP_USER);
if (pcds) { pcds->DigitalEnabled = (BOOLEAN)Button_GetCheck(GetDlgItem(hDlg, IDC_CDEN_DIGAUDIO)); } }
ULONG MatchDriveToDevInst(DWORD DevInst) { HANDLE hMatch = INVALID_HANDLE_VALUE; STORAGE_DEVICE_NUMBER sDevice, sMatch; DWORD bytesReturned; ULONG uRet = 0;
if (INVALID_HANDLE_VALUE != (hMatch = GetHandleForDeviceInst(DevInst))) { if (DeviceIoControl(hMatch, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sMatch, sizeof(STORAGE_DEVICE_NUMBER), &bytesReturned, NULL)) { uRet = sMatch.DeviceNumber; }
CloseHandle(hMatch); }
return uRet; }
/*
* */ DWORD CDAudio_InitDrives(HWND hwnd, PCDSTATE pcds) { DWORD cch; DWORD cCDs = 0; ULONG uDrive = 0;
uDrive = MatchDriveToDevInst(pcds->pDevInfoData->DevInst); CDAudio_GetRegData(pcds,uDrive);
if (cch = GetLogicalDriveStrings(0, NULL)) { LPTSTR lpDrives,lp; lp = lpDrives = GlobalAllocPtr(GHND, cch * sizeof(TCHAR)); cch = GetLogicalDriveStrings(cch, lpDrives); if (lpDrives && cch) { // upon the last drive enumerated, there will be a double
// null termination
while (*lpDrives) { if (GetDriveType(lpDrives) == DRIVE_CDROM) { int i; LPTSTR lp; lp = CharUpper(lpDrives); while (*lp != TEXT('\\')) lp = CharNext(lp); while (*lp) { *lp = TEXT(' '); lp = CharNext(lp); } if (cCDs == uDrive) { pcds->chDrive = lpDrives[0]; }
cCDs++; } for ( ; *lpDrives ; lpDrives++ ); lpDrives++; } } if (lp) GlobalFreePtr(lp);
} return cCDs; }
//
// Determines what device is currently being used by the mapper to play audio
//
MMRESULT GetWaveID(UINT *puWaveID)
{ PAUDIODLGINFO pInfo = (PAUDIODLGINFO)LocalAlloc(LPTR, sizeof(AUDIODLGINFO));;
if (!pInfo) return MMSYSERR_NOMEM;
GetPrefInfo(pInfo, NULL); if(-1 == pInfo->uPrefOut) { LocalFree((HLOCAL)pInfo); return MMSYSERR_BADDEVICEID; }
*puWaveID = pInfo->uPrefOut; LocalFree((HLOCAL)pInfo);
return MMSYSERR_NOERROR; }
//
// Checks to see if the current output audio device is a WDM Device or not
//
BOOL WDMAudioEnabled(void) { BOOL fResult = FALSE;
UINT uWaveID;
if (GetWaveID(&uWaveID) == MMSYSERR_NOERROR) { WAVEOUTCAPS woc;
if (waveOutGetDevCaps(uWaveID, &woc, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR) { if ((woc.wMid == MM_MICROSOFT) && (woc.wPid == MM_MSFT_WDMAUDIO_WAVEOUT)) { fResult = TRUE; } } }
return(fResult); }
BOOL CDAudio_OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lParam) { HWND hwndTB1 = GetDlgItem(hwnd, IDC_CD_TB_VOLUME); HWND hwndCK3 = GetDlgItem(hwnd, IDC_CDEN_DIGAUDIO); HWND hwndTX1 = GetDlgItem(hwnd, IDC_TEXT_24); UINT uDrive; int i; PCDSTATE pcds = NULL; PALLDEVINFO pDevInfo = NULL;
pcds = (PCDSTATE)GlobalAllocPtr(GHND, sizeof(CDSTATE));
if (!pcds) return FALSE;
SetWindowLongPtr(hwnd,DWLP_USER,(LONG_PTR)pcds);
pDevInfo = (ALLDEVINFO *) ((LPPROPSHEETPAGE) lParam)->lParam;
if (pDevInfo) { pcds->hDevInfo = pDevInfo->hDevInfo; pcds->pDevInfoData = pDevInfo->pDevInfoData; GlobalFreePtr(pDevInfo); ((LPPROPSHEETPAGE) lParam)->lParam = (LPARAM) NULL; }
SendMessage(hwndTB1, TBM_SETTICFREQ, 10, 0); SendMessage(hwndTB1, TBM_SETRANGE, FALSE, MAKELONG(0,100));
i = CDAudio_InitDrives(hwnd,pcds); if (i) { if (pcds) { SendMessage(hwndTB1, TBM_SETPOS, TRUE, (pcds->cdar.dwVol * 100L)/255L ); Button_SetCheck(hwndCK3, pcds->DigitalEnabled); }
g_fWDMEnabled = WDMAudioEnabled();
// if (!g_fWDMEnabled) // Per bug 20964, never disable "Enable Digital CD" checkbox
// {
// EnableWindow(hwndCK3, FALSE);
// EnableWindow(hwndTX1, FALSE);
// }
} // else
// {
// EnableWindow(hwndCK3, FALSE);
// EnableWindow(hwndTB1, FALSE);
// EnableWindow(hwndTX1, FALSE);
// }
return FALSE; }
void CDToggleApply(HWND hDlg) { PCDSTATE pcds; BOOL fChanged = FALSE;
pcds = (PCDSTATE)GetWindowLongPtr(hDlg,DWLP_USER);
if (pcds) { if (pcds->DigitalEnabled != pcds->oldDigEnabled) { fChanged = TRUE; }
if (pcds->dwOldVol != pcds->cdar.dwVol) { fChanged = TRUE; } }
if (fChanged) { PropSheet_Changed(GetParent(hDlg),hDlg); } else { PropSheet_UnChanged(GetParent(hDlg),hDlg); } }
void CDAudio_OnDestroy( HWND hwnd) { PCDSTATE lp = (PCDSTATE)GetWindowLongPtr(hwnd,DWLP_USER); if (lp) GlobalFreePtr(lp);
if (g_hModStorProp) { FreeLibrary(g_hModStorProp); g_hModStorProp = NULL; } }
void CDAudio_OnHScroll( HWND hwnd, HWND hwndCtl, UINT code, int pos) { if (code == TB_ENDTRACK || code == SB_THUMBTRACK) { HWND hwndTB1 = GetDlgItem(hwnd, IDC_CD_TB_VOLUME); int i; PCDSTATE pcds; DWORD dwVol;
pcds = (PCDSTATE)GetWindowLongPtr(hwnd,DWLP_USER);
if (CB_ERR != (UINT_PTR) pcds && pcds) { dwVol = (((DWORD)SendMessage(hwndTB1, TBM_GETPOS, 0, 0)) * 255L) / 100L;
if (dwVol != pcds->cdar.dwVol) { pcds->cdar.dwVol = dwVol; ChangeCDVolume(pcds); CDToggleApply(hwnd); } } } } void CDAudio_OnCancel( HWND hwnd) { PCDSTATE pcds;
pcds = (PCDSTATE)GetWindowLongPtr(hwnd,DWLP_USER); pcds->cdar.dwVol = pcds->dwOldVol; ChangeCDVolume(pcds); }
BOOL PASCAL CDAudio_OnCommand( HWND hDlg, int id, HWND hwndCtl, UINT codeNotify) { BOOL fResult = FALSE;
switch (id) { case ID_APPLY: { CDAudio_SaveState(hDlg); fResult = TRUE; } break;
case IDCANCEL: { CDAudio_OnCancel(hDlg); fResult = TRUE; } break;
case IDC_CDEN_DIGAUDIO: { CDAudio_DigitalPlaybackEnable(hDlg); CDToggleApply(hDlg); } break; }
return fResult; }
const static DWORD aCDHelpIds[] = { // Context Help IDs
IDI_CDAUDIO, IDH_COMM_GROUPBOX, IDC_ICON_5, IDH_COMM_GROUPBOX, IDC_TEXT_25, IDH_COMM_GROUPBOX, IDC_GROUPBOX, IDH_COMM_GROUPBOX, IDC_GROUPBOX_2, IDH_COMM_GROUPBOX, IDC_TEXT_29, IDH_CD_VOL_HEADPHONE, IDC_CD_TB_VOLUME, IDH_CD_VOL_HEADPHONE, IDC_TEXT_30, IDH_CD_VOL_HEADPHONE, IDC_TEXT_24, IDH_CDROM_PROPERTIES_DIGITAL, IDC_CDEN_DIGAUDIO, IDH_CDROM_PROPERTIES_DIGITAL,
0, 0 };
BOOL CALLBACK CDDlg( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { NMHDR FAR *lpnm;
switch (uMsg) { case WM_NOTIFY: lpnm = (NMHDR FAR *)lParam; switch(lpnm->code) { case PSN_KILLACTIVE: FORWARD_WM_COMMAND(hDlg, IDOK, 0, 0, SendMessage); break;
case PSN_APPLY: FORWARD_WM_COMMAND(hDlg, ID_APPLY, 0, 0, SendMessage); break;
case PSN_SETACTIVE: FORWARD_WM_COMMAND(hDlg, ID_INIT, 0, 0, SendMessage); break;
case PSN_RESET: FORWARD_WM_COMMAND(hDlg, IDCANCEL, 0, 0, SendMessage); break; } break;
case WM_INITDIALOG: HANDLE_WM_INITDIALOG(hDlg, wParam, lParam, CDAudio_OnInitDialog); break;
case WM_DESTROY: HANDLE_WM_DESTROY(hDlg, wParam, lParam, CDAudio_OnDestroy); break;
case WM_DROPFILES: break;
case WM_CONTEXTMENU: WinHelp((HWND)wParam, gszWindowsHlp, HELP_CONTEXTMENU, (UINT_PTR)(LPTSTR)aCDHelpIds); break;
case WM_HELP: WinHelp(((LPHELPINFO)lParam)->hItemHandle, gszWindowsHlp, HELP_WM_HELP, (UINT_PTR)(LPTSTR)aCDHelpIds); break;
case WM_COMMAND: HANDLE_WM_COMMAND(hDlg, wParam, lParam, CDAudio_OnCommand); break;
case WM_HSCROLL: HANDLE_WM_HSCROLL(hDlg, wParam, lParam, CDAudio_OnHScroll); break;
#if 0
default: if (uMsg == wHelpMessage) { WinHelp(hDlg, gszWindowsHlp, HELP_CONTEXT, ID_SND_HELP); return TRUE; } break; #endif
} return FALSE; }
|