You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3385 lines
92 KiB
3385 lines
92 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: dplshare.c
|
|
* Content: Methods for shared buffer management
|
|
*
|
|
* History:
|
|
* Date By Reason
|
|
* ======= ======= ======
|
|
* 5/18/96 myronth Created it
|
|
* 12/12/96 myronth Fixed DPLCONNECTION validation & bug #4692
|
|
* 12/13/96 myronth Fixed bugs #4697 and #4607
|
|
* 2/12/97 myronth Mass DX5 changes
|
|
* 2/20/97 myronth Changed buffer R/W to be circular
|
|
* 3/12/97 myronth Kill thread timeout, DPF error levels
|
|
* 4/1/97 myronth Fixed handle leak -- bug #7054
|
|
* 5/8/97 myronth Added bHeader parameter to packing function
|
|
* 5/21/97 ajayj DPL_SendLobbyMessage - allow DPLMSG_STANDARD flag #8929
|
|
* 5/30/97 myronth Fixed SetConnectionSettings for invalid AppID (#9110)
|
|
* Fixed SetLobbyMessageEvent for invalid handle (#9111)
|
|
* 6/19/97 myronth Fixed handle leak (#10063)
|
|
* 7/30/97 myronth Added support for standard lobby messaging and
|
|
* fixed receive loop race condition (#10843)
|
|
* 8/11/97 myronth Added guidInstance handling in standard lobby requests
|
|
* 8/19/97 myronth Support for DPLMSG_NEWSESSIONHOST
|
|
* 8/19/97 myronth Removed dead PRV_SendStandardSystemMessageByObject
|
|
* 8/20/97 myronth Added DPLMSG_STANDARD to all standard messages
|
|
* 11/13/97 myronth Added guidInstance to lobby system message (#10944)
|
|
* 12/2/97 myronth Fixed swallowed error code, moved structure
|
|
* validation for DPLCONNECTION (#15527, 15529)
|
|
* 1/20/98 myronth Added WaitForConnectionSettings
|
|
* 7/9/99 aarono Cleaning up GetLastError misuse, must call right away,
|
|
* before calling anything else, including DPF.
|
|
* 10/31/99 aarono add node lock when to SetLobbyMessageEvent
|
|
* NTB#411892
|
|
* 02/08/00 aarono added monitoring for lobby client crash/exit, notify
|
|
* lobbied application, Mill B#131938
|
|
* 7/12/00 aarono fix GUIDs for IPC to be fully significant, otherwise won't IPC.
|
|
***************************************************************************/
|
|
#include "dplobpr.h"
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Debug Functions
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
#ifdef DEBUG
|
|
|
|
DPF_DUMPWSTR(int level, LPWSTR lpwStr)
|
|
{
|
|
char lpszStr[200];
|
|
WideToAnsi(lpszStr,lpwStr,200);
|
|
DPF(level, lpszStr);
|
|
}
|
|
#else
|
|
#define DPF_DUMPWSTR(a,b)
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Functions
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT PRV_ReadCommandLineIPCGuid(GUID *lpguidIPC)
|
|
{
|
|
LPWSTR pwszCommandLine;
|
|
LPWSTR pwszAlloc=NULL;
|
|
LPWSTR pwszSwitch=NULL;
|
|
HRESULT hr=DP_OK;
|
|
|
|
if(!OS_IsPlatformUnicode())
|
|
{
|
|
// if we get a command line in ANSI, convert to UNICODE, this allows
|
|
// us to avoid the DBCS issues in ANSI while scanning for the IPC GUID
|
|
LPSTR pszCommandLine;
|
|
|
|
pszCommandLine=(LPSTR)GetCommandLineA();
|
|
pwszAlloc=DPMEM_ALLOC(MAX_PATH*sizeof(WCHAR));
|
|
if (pwszAlloc == NULL)
|
|
{
|
|
goto exit;
|
|
}
|
|
hr=AnsiToWide(pwszAlloc,pszCommandLine,MAX_PATH);
|
|
if(FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
pwszCommandLine=pwszAlloc;
|
|
}
|
|
else
|
|
{
|
|
pwszCommandLine=(LPWSTR)GetCommandLine();
|
|
}
|
|
|
|
// pwszCommandLine now points to the UNICODE command line.
|
|
if(pwszSwitch=OS_StrStr(pwszCommandLine,SZ_DP_IPC_GUID)){
|
|
// found the GUID on the command line
|
|
if (OS_StrLen(pwszSwitch) >= (sizeof(SZ_DP_IPC_GUID)+sizeof(SZ_GUID_PROTOTYPE)-sizeof(WCHAR))/sizeof(WCHAR)){
|
|
// skip past the switch description to the actual GUID and extract
|
|
hr=GUIDFromString(pwszSwitch+(sizeof(SZ_DP_IPC_GUID)/sizeof(WCHAR))-1, lpguidIPC);
|
|
} else {
|
|
hr=DPERR_GENERIC;
|
|
}
|
|
} else {
|
|
hr=DPERR_GENERIC;
|
|
}
|
|
|
|
|
|
exit:
|
|
|
|
if(pwszAlloc){
|
|
DPMEM_FREE(pwszAlloc);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_GetInternalName"
|
|
HRESULT PRV_GetInternalName(LPDPLOBBYI_GAMENODE lpgn, DWORD dwType, LPWSTR lpName)
|
|
{
|
|
DWORD pid;
|
|
LPWSTR lpFileName;
|
|
LPSTR lpstr1, lpstr2, lpstr3;
|
|
char szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)];
|
|
BOOL bUseGuid=FALSE;
|
|
|
|
|
|
DPF(7, "Entering PRV_GetInternalName");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", lpgn, dwType, lpName);
|
|
|
|
|
|
if(lpgn->dwFlags & GN_IPCGUID_SET){
|
|
bUseGuid=TRUE;
|
|
}
|
|
// Get the current process ID if we are a game, otherwise, we need to
|
|
// get the process ID of the game that we spawned
|
|
else if(lpgn->dwFlags & GN_LOBBY_CLIENT)
|
|
{
|
|
if(lpgn->dwGameProcessID)
|
|
pid = lpgn->dwGameProcessID;
|
|
else
|
|
return DPERR_APPNOTSTARTED;
|
|
}
|
|
else
|
|
{
|
|
pid = GetCurrentProcessId();
|
|
}
|
|
|
|
switch(dwType)
|
|
{
|
|
case TYPE_CONNECT_DATA_FILE:
|
|
lpFileName = SZ_CONNECT_DATA_FILE;
|
|
break;
|
|
|
|
case TYPE_CONNECT_DATA_MUTEX:
|
|
lpFileName = SZ_CONNECT_DATA_MUTEX;
|
|
break;
|
|
|
|
case TYPE_GAME_WRITE_FILE:
|
|
lpFileName = SZ_GAME_WRITE_FILE;
|
|
break;
|
|
|
|
case TYPE_LOBBY_WRITE_FILE:
|
|
lpFileName = SZ_LOBBY_WRITE_FILE;
|
|
break;
|
|
|
|
case TYPE_LOBBY_WRITE_EVENT:
|
|
lpFileName = SZ_LOBBY_WRITE_EVENT;
|
|
break;
|
|
|
|
case TYPE_GAME_WRITE_EVENT:
|
|
lpFileName = SZ_GAME_WRITE_EVENT;
|
|
break;
|
|
|
|
case TYPE_LOBBY_WRITE_MUTEX:
|
|
lpFileName = SZ_LOBBY_WRITE_MUTEX;
|
|
break;
|
|
|
|
case TYPE_GAME_WRITE_MUTEX:
|
|
lpFileName = SZ_GAME_WRITE_MUTEX;
|
|
break;
|
|
|
|
default:
|
|
DPF(2, "We got an Internal Name Type that we didn't expect!");
|
|
return DPERR_GENERIC;
|
|
}
|
|
|
|
GetAnsiString(&lpstr2, SZ_FILENAME_BASE);
|
|
GetAnsiString(&lpstr3, lpFileName);
|
|
|
|
if(!bUseGuid){
|
|
// REVIEW!!!! -- I can't get the Unicode version of wsprintf to work, so
|
|
// for now, use the ANSI version and convert
|
|
// wsprintf(lpName, SZ_NAME_TEMPLATE, SZ_FILENAME_BASE, lpFileName, pid);
|
|
GetAnsiString(&lpstr1, SZ_NAME_TEMPLATE);
|
|
wsprintfA((LPSTR)szName, lpstr1, lpstr2, lpstr3, pid);
|
|
} else {
|
|
GetAnsiString(&lpstr1, SZ_GUID_NAME_TEMPLATE);
|
|
wsprintfA((LPSTR)szName, lpstr1, lpstr2, lpstr3);
|
|
}
|
|
|
|
AnsiToWide(lpName, szName, (strlen(szName) + 1));
|
|
|
|
if(bUseGuid){
|
|
// concatenate the guid to the name if we are using the guid.
|
|
WCHAR *pGuid;
|
|
pGuid = lpName + WSTRLEN(lpName) - 1;
|
|
StringFromGUID(&lpgn->guidIPC, pGuid, GUID_STRING_SIZE);
|
|
}
|
|
|
|
DPF(9, "Made internal Name...");
|
|
DPF_DUMPWSTR(9,lpName);
|
|
|
|
if(lpstr1)
|
|
DPMEM_FREE(lpstr1);
|
|
if(lpstr2)
|
|
DPMEM_FREE(lpstr2);
|
|
if(lpstr3)
|
|
DPMEM_FREE(lpstr3);
|
|
|
|
return DP_OK;
|
|
|
|
} // PRV_GetInternalName
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_AddNewGameNode"
|
|
HRESULT PRV_AddNewGameNode(LPDPLOBBYI_DPLOBJECT this,
|
|
LPDPLOBBYI_GAMENODE * lplpgn, DWORD dwGameID,
|
|
HANDLE hGameProcess, BOOL bLobbyClient, GUID *lpguidIPC)
|
|
{
|
|
LPDPLOBBYI_GAMENODE lpgn;
|
|
|
|
|
|
DPF(7, "Entering PRV_AddNewGameNode");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu",
|
|
this, lplpgn, dwGameID, hGameProcess, bLobbyClient);
|
|
|
|
lpgn = DPMEM_ALLOC(sizeof(DPLOBBYI_GAMENODE));
|
|
if(!lpgn)
|
|
{
|
|
DPF(2, "Unable to allocate memory for GameNode structure!");
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
// Initialize the GameNode
|
|
lpgn->dwSize = sizeof(DPLOBBYI_GAMENODE);
|
|
lpgn->dwGameProcessID = dwGameID;
|
|
lpgn->hGameProcess = hGameProcess;
|
|
lpgn->this = this;
|
|
lpgn->MessageHead.lpPrev = &lpgn->MessageHead;
|
|
lpgn->MessageHead.lpNext = &lpgn->MessageHead;
|
|
|
|
if(lpguidIPC){
|
|
// provided during launch by lobby client
|
|
lpgn->guidIPC=*lpguidIPC;
|
|
lpgn->dwFlags |= GN_IPCGUID_SET;
|
|
} else {
|
|
// need to extract the GUID from the command line if present.
|
|
if(DP_OK==PRV_ReadCommandLineIPCGuid(&lpgn->guidIPC)){
|
|
lpgn->dwFlags |= GN_IPCGUID_SET;
|
|
}
|
|
}
|
|
|
|
// If we are a lobby client, set the flag
|
|
if(bLobbyClient)
|
|
lpgn->dwFlags |= GN_LOBBY_CLIENT;
|
|
|
|
// Add the GameNode to the list
|
|
lpgn->lpgnNext = this->lpgnHead;
|
|
this->lpgnHead = lpgn;
|
|
|
|
// Set the output pointer
|
|
*lplpgn = lpgn;
|
|
|
|
return DP_OK;
|
|
|
|
} // PRV_AddNewGameNode
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_GetGameNode"
|
|
LPDPLOBBYI_GAMENODE PRV_GetGameNode(LPDPLOBBYI_GAMENODE lpgnHead, DWORD dwGameID)
|
|
{
|
|
LPDPLOBBYI_GAMENODE lpgnTemp = lpgnHead;
|
|
GUID guidIPC=GUID_NULL;
|
|
BOOL bFoundGUID;
|
|
|
|
DPF(7, "Entering PRV_GetGameNode");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x", lpgnHead, dwGameID);
|
|
|
|
if(DP_OK==PRV_ReadCommandLineIPCGuid(&guidIPC)){
|
|
bFoundGUID=TRUE;
|
|
} else {
|
|
bFoundGUID=FALSE;
|
|
}
|
|
|
|
while(lpgnTemp)
|
|
{
|
|
if((lpgnTemp->dwGameProcessID == dwGameID) ||
|
|
((bFoundGUID) && (lpgnTemp->dwFlags & GN_IPCGUID_SET) && (IsEqualGUID(&lpgnTemp->guidIPC,&guidIPC))))
|
|
return lpgnTemp;
|
|
else
|
|
lpgnTemp = lpgnTemp->lpgnNext;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
} // PRV_GetGameNode
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_SetupClientDataAccess"
|
|
BOOL PRV_SetupClientDataAccess(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
SECURITY_ATTRIBUTES sa;
|
|
HANDLE hConnDataMutex = NULL;
|
|
HANDLE hLobbyWrite = NULL;
|
|
HANDLE hLobbyWriteMutex = NULL;
|
|
HANDLE hGameWrite = NULL;
|
|
HANDLE hGameWriteMutex = NULL;
|
|
WCHAR szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)];
|
|
|
|
|
|
DPF(7, "Entering PRV_SetupClientDataAccess");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
// Set up the security attributes (so that our objects can
|
|
// be inheritable)
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.lpSecurityDescriptor = NULL;
|
|
sa.bInheritHandle = TRUE;
|
|
|
|
// Create the ConnectionData Mutex
|
|
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_CONNECT_DATA_MUTEX,
|
|
(LPWSTR)szName)))
|
|
{
|
|
hConnDataMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName);
|
|
}
|
|
|
|
// Create the GameWrite Event
|
|
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_GAME_WRITE_EVENT, (LPWSTR)szName)))
|
|
{
|
|
hGameWrite = OS_CreateEvent(&sa, FALSE, FALSE, (LPWSTR)szName);
|
|
}
|
|
|
|
// Create the GameWrite Mutex
|
|
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_GAME_WRITE_MUTEX,
|
|
(LPWSTR)szName)))
|
|
{
|
|
hGameWriteMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName);
|
|
}
|
|
|
|
// Create the LobbyWrite Event
|
|
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_LOBBY_WRITE_EVENT, (LPWSTR)szName)))
|
|
{
|
|
hLobbyWrite = OS_CreateEvent(&sa, FALSE, FALSE, (LPWSTR)szName);
|
|
}
|
|
|
|
// Create the LobbyWrite Mutex
|
|
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_LOBBY_WRITE_MUTEX,
|
|
(LPWSTR)szName)))
|
|
{
|
|
hLobbyWriteMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName);
|
|
}
|
|
|
|
|
|
// Check for errors
|
|
if(!hConnDataMutex || !hGameWrite || !hGameWriteMutex
|
|
|| !hLobbyWrite || !hLobbyWriteMutex)
|
|
{
|
|
if(hConnDataMutex)
|
|
CloseHandle(hConnDataMutex);
|
|
if(hGameWrite)
|
|
CloseHandle(hGameWrite);
|
|
if(hGameWriteMutex)
|
|
CloseHandle(hGameWriteMutex);
|
|
if(hLobbyWrite)
|
|
CloseHandle(hLobbyWrite);
|
|
if(hLobbyWriteMutex)
|
|
CloseHandle(hLobbyWriteMutex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Save the handles
|
|
lpgn->hConnectDataMutex = hConnDataMutex;
|
|
lpgn->hGameWriteEvent = hGameWrite;
|
|
lpgn->hGameWriteMutex = hGameWriteMutex;
|
|
lpgn->hLobbyWriteEvent = hLobbyWrite;
|
|
lpgn->hLobbyWriteMutex = hLobbyWriteMutex;
|
|
|
|
return TRUE;
|
|
|
|
} // PRV_SetupClientDataAccess
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_GetDataBuffer"
|
|
HRESULT PRV_GetDataBuffer(LPDPLOBBYI_GAMENODE lpgn, DWORD dwType,
|
|
DWORD dwSize, LPHANDLE lphFile, LPVOID * lplpMemory)
|
|
{
|
|
HRESULT hr;
|
|
SECURITY_ATTRIBUTES sa;
|
|
WCHAR szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)];
|
|
LPVOID lpMemory = NULL;
|
|
HANDLE hFile = NULL;
|
|
DWORD dwError = 0;
|
|
|
|
|
|
DPF(7, "Entering PRV_GetDataBuffer");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, 0x%08x, 0x%08x",
|
|
lpgn, dwType, dwSize, lphFile, lplpMemory);
|
|
|
|
// Get the data buffer filename
|
|
hr = PRV_GetInternalName(lpgn, dwType, (LPWSTR)szName);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
|
|
// If we are a Lobby Client, we need to create the file. If we
|
|
// are a game, we need to open the already created file for
|
|
// connection data, or we can create the file for game data (if
|
|
// it doesn't already exist).
|
|
if(lpgn->dwFlags & GN_LOBBY_CLIENT)
|
|
{
|
|
// Set up the security attributes (so that our mapping can
|
|
// be inheritable
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.lpSecurityDescriptor = NULL;
|
|
sa.bInheritHandle = TRUE;
|
|
|
|
// Create the file mapping
|
|
hFile = OS_CreateFileMapping(INVALID_HANDLE_VALUE, &sa,
|
|
PAGE_READWRITE, 0, dwSize,
|
|
(LPWSTR)szName);
|
|
}
|
|
else
|
|
{
|
|
hFile = OS_OpenFileMapping(FILE_MAP_ALL_ACCESS, TRUE, (LPWSTR)szName);
|
|
}
|
|
|
|
if(!hFile)
|
|
{
|
|
dwError = GetLastError();
|
|
// WARNING: error may not be correct since calls we are trying to get last error from may have called out
|
|
// to another function before returning.
|
|
DPF(5, "Couldn't get a handle to the shared local memory, dwError = %lu (error may not be correct)", dwError);
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
// Map a View of the file
|
|
lpMemory = MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
|
|
|
if(!lpMemory)
|
|
{
|
|
dwError = GetLastError();
|
|
DPF(5, "Unable to get pointer to shared local memory, dwError = %lu", dwError);
|
|
CloseHandle(hFile);
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
// Setup the control structure based on the buffer type
|
|
switch(dwType)
|
|
{
|
|
case TYPE_CONNECT_DATA_FILE:
|
|
{
|
|
LPDPLOBBYI_CONNCONTROL lpControl = NULL;
|
|
|
|
|
|
lpControl = (LPDPLOBBYI_CONNCONTROL)lpMemory;
|
|
|
|
// If the buffer has been initialized, then don't worry
|
|
// about it. If the token is wrong (uninitialized), then do it
|
|
if(lpControl->dwToken != BC_TOKEN)
|
|
{
|
|
lpControl->dwToken = BC_TOKEN;
|
|
lpControl->dwFlags = 0;
|
|
if(lpgn->dwFlags & GN_LOBBY_CLIENT){
|
|
lpControl->CliProcId = GetCurrentProcessId();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TYPE_GAME_WRITE_FILE:
|
|
case TYPE_LOBBY_WRITE_FILE:
|
|
{
|
|
LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
|
|
|
|
|
|
lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpMemory;
|
|
if(lpgn->dwFlags & GN_LOBBY_CLIENT)
|
|
{
|
|
// Since we're the lobby client, we know we create the buffer, so
|
|
// initialize the entire structure
|
|
lpControl->dwToken = BC_TOKEN;
|
|
lpControl->dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
lpControl->dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
lpControl->dwFlags = BC_LOBBY_ACTIVE;
|
|
lpControl->dwMessages = 0;
|
|
lpControl->dwBufferSize = dwSize;
|
|
lpControl->dwBufferLeft = dwSize - sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
}
|
|
else
|
|
{
|
|
// We're the game, but we don't know for sure if we just created
|
|
// the buffer or if a lobby client did. So check the token. If
|
|
// it is incorrect, we will assume we just created it and we need
|
|
// to initialize the buffer control struct. Otherwise, we will
|
|
// assume a lobby client created it and we just need to add
|
|
// our flag.
|
|
if(lpControl->dwToken != BC_TOKEN)
|
|
{
|
|
// We don't see the token, so initialize the structure
|
|
lpControl->dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
lpControl->dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
lpControl->dwFlags = BC_GAME_ACTIVE;
|
|
lpControl->dwMessages = 0;
|
|
lpControl->dwBufferSize = dwSize;
|
|
lpControl->dwBufferLeft = dwSize - sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
}
|
|
else
|
|
{
|
|
// We assume the lobby created this buffer, so just set our flag
|
|
lpControl->dwFlags |= BC_GAME_ACTIVE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fill in the output parameters
|
|
*lphFile = hFile;
|
|
*lplpMemory = lpMemory;
|
|
|
|
return DP_OK;
|
|
|
|
} // PRV_GetDataBuffer
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_StartReceiveThread"
|
|
HRESULT PRV_StartReceiveThread(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
HANDLE hReceiveThread = NULL;
|
|
HANDLE hKillEvent = NULL;
|
|
DWORD dwThreadID;
|
|
|
|
|
|
DPF(7, "Entering PRV_StartReceiveThread");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
ASSERT(lpgn);
|
|
|
|
// Create the kill event if one doesn't exists
|
|
if(!(lpgn->hKillReceiveThreadEvent))
|
|
{
|
|
hKillEvent = OS_CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if(!hKillEvent)
|
|
{
|
|
DPF(2, "Unable to create Kill Receive Thread Event");
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
// If the Receive Thread isn't going, start it
|
|
if(!(lpgn->hReceiveThread))
|
|
{
|
|
// Spawn off a receive notification thread for the cross-proc communication
|
|
hReceiveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
|
|
PRV_ReceiveClientNotification, lpgn, 0, &dwThreadID);
|
|
|
|
if(!hReceiveThread)
|
|
{
|
|
DPF(2, "Unable to create Receive Thread!");
|
|
if(hKillEvent)
|
|
CloseHandle(hKillEvent);
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
lpgn->hReceiveThread = hReceiveThread;
|
|
if(hKillEvent)
|
|
lpgn->hKillReceiveThreadEvent = hKillEvent;
|
|
|
|
}
|
|
|
|
return DP_OK;
|
|
|
|
} // PRV_StartReceiveThread
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_SetupAllSharedMemory"
|
|
HRESULT PRV_SetupAllSharedMemory(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
HRESULT hr;
|
|
LPVOID lpConnDataMemory = NULL;
|
|
LPVOID lpGameMemory = NULL;
|
|
LPVOID lpLobbyMemory = NULL;
|
|
HANDLE hFileConnData = NULL;
|
|
HANDLE hFileGameWrite = NULL;
|
|
HANDLE hFileLobbyWrite = NULL;
|
|
DWORD dwError = 0;
|
|
|
|
|
|
DPF(7, "Entering PRV_SetupAllSharedMemory");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
// Get access to the Connection Data File
|
|
hr = PRV_GetDataBuffer(lpgn, TYPE_CONNECT_DATA_FILE,
|
|
MAX_APPDATABUFFERSIZE,
|
|
&hFileConnData, &lpConnDataMemory);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF(5, "Couldn't get access to Connection Data buffer");
|
|
goto ERROR_SETUP_SHARED_MEMORY;
|
|
}
|
|
|
|
// Do the same for the Game Write File...
|
|
hr = PRV_GetDataBuffer(lpgn, TYPE_GAME_WRITE_FILE,
|
|
MAX_APPDATABUFFERSIZE,
|
|
&hFileGameWrite, &lpGameMemory);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF(5, "Couldn't get access to Game Write buffer");
|
|
goto ERROR_SETUP_SHARED_MEMORY;
|
|
}
|
|
|
|
|
|
// Do the same for the Lobby Write File...
|
|
hr = PRV_GetDataBuffer(lpgn, TYPE_LOBBY_WRITE_FILE,
|
|
MAX_APPDATABUFFERSIZE,
|
|
&hFileLobbyWrite, &lpLobbyMemory);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF(5, "Couldn't get access to Lobby Write buffer");
|
|
goto ERROR_SETUP_SHARED_MEMORY;
|
|
}
|
|
|
|
|
|
// Setup the signalling objects
|
|
if(!PRV_SetupClientDataAccess(lpgn))
|
|
{
|
|
DPF(5, "Unable to create synchronization objects for shared memory!");
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
// Save the file handles
|
|
lpgn->hConnectDataFile = hFileConnData;
|
|
lpgn->lpConnectDataBuffer = lpConnDataMemory;
|
|
lpgn->hGameWriteFile = hFileGameWrite;
|
|
lpgn->lpGameWriteBuffer = lpGameMemory;
|
|
lpgn->hLobbyWriteFile = hFileLobbyWrite;
|
|
lpgn->lpLobbyWriteBuffer = lpLobbyMemory;
|
|
|
|
// Set the flag that tells us the shared memory files are valid
|
|
lpgn->dwFlags |= GN_SHARED_MEMORY_AVAILABLE;
|
|
|
|
// Start the Receive Thread
|
|
hr = PRV_StartReceiveThread(lpgn);
|
|
if(FAILED(hr))
|
|
{
|
|
// In this case, we will keep our shared buffers around. Don't
|
|
// worry about cleaning them up here -- we'll probably still need
|
|
// them later, and they will get cleaned up later.
|
|
DPF(5, "Unable to start receive thread");
|
|
return hr;
|
|
}
|
|
|
|
return DP_OK;
|
|
|
|
|
|
ERROR_SETUP_SHARED_MEMORY:
|
|
|
|
if(hFileConnData)
|
|
CloseHandle(hFileConnData);
|
|
if(lpConnDataMemory)
|
|
UnmapViewOfFile(lpConnDataMemory);
|
|
if(hFileGameWrite)
|
|
CloseHandle(hFileGameWrite);
|
|
if(lpGameMemory)
|
|
UnmapViewOfFile(lpGameMemory);
|
|
if(hFileLobbyWrite)
|
|
CloseHandle(hFileLobbyWrite);
|
|
if(lpLobbyMemory)
|
|
UnmapViewOfFile(lpLobbyMemory);
|
|
|
|
return hr;
|
|
|
|
} // PRV_SetupAllSharedMemory
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_EnterConnSettingsWaitMode"
|
|
void PRV_EnterConnSettingsWaitMode(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
|
|
LPDPLOBBYI_BUFFERCONTROL lpBufferControl = NULL;
|
|
|
|
|
|
DPF(7, "Entering PRV_EnterConnSettingsWaitMode");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
ASSERT(lpgn);
|
|
|
|
// Set the flag in the ConnSettings buffer
|
|
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
|
|
lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
|
|
lpConnControl->dwFlags |= BC_WAIT_MODE;
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
|
|
// Set the flag in the GameWrite buffer
|
|
WaitForSingleObject(lpgn->hGameWriteMutex, INFINITE);
|
|
lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer;
|
|
lpBufferControl->dwFlags |= BC_WAIT_MODE;
|
|
ReleaseMutex(lpgn->hGameWriteMutex);
|
|
|
|
// Set the flag in the LobbyWrite buffer
|
|
WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE);
|
|
lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
|
|
lpBufferControl->dwFlags |= BC_WAIT_MODE;
|
|
ReleaseMutex(lpgn->hLobbyWriteMutex);
|
|
|
|
} // PRV_EnterConnSettingsWaitMode
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_LeaveConnSettingsWaitMode"
|
|
void PRV_LeaveConnSettingsWaitMode(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
|
|
LPDPLOBBYI_BUFFERCONTROL lpBufferControl = NULL;
|
|
|
|
|
|
DPF(7, "Entering PRV_LeaveConnSettingsWaitMode");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
ASSERT(lpgn);
|
|
|
|
// Clear the flag in the ConnSettings buffer
|
|
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
|
|
lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
|
|
lpConnControl->dwFlags &= ~(BC_WAIT_MODE | BC_PENDING_CONNECT);
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
|
|
// Clear the flag in the GameWrite buffer
|
|
WaitForSingleObject(lpgn->hGameWriteMutex, INFINITE);
|
|
lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer;
|
|
lpBufferControl->dwFlags &= ~BC_WAIT_MODE;
|
|
ReleaseMutex(lpgn->hGameWriteMutex);
|
|
|
|
// Clear the flag in the LobbyWrite buffer
|
|
WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE);
|
|
lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
|
|
lpBufferControl->dwFlags &= ~BC_WAIT_MODE;
|
|
ReleaseMutex(lpgn->hLobbyWriteMutex);
|
|
|
|
} // PRV_LeaveConnSettingsWaitMode
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_WriteClientData"
|
|
HRESULT PRV_WriteClientData(LPDPLOBBYI_GAMENODE lpgn, DWORD dwFlags,
|
|
LPVOID lpData, DWORD dwSize)
|
|
{
|
|
LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
|
|
LPDPLOBBYI_MESSAGEHEADER lpHeader = NULL;
|
|
HANDLE hMutex = NULL;
|
|
DWORD dwSizeToEnd = 0;
|
|
LPBYTE lpTemp = NULL;
|
|
HRESULT hr = DP_OK;
|
|
|
|
DWORD dwReadOffset;
|
|
DWORD dwWriteOffset;
|
|
DWORD dwBufferSize;
|
|
|
|
|
|
DPF(7, "Entering PRV_WriteClientData");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu",
|
|
lpgn, dwFlags, lpData, dwSize);
|
|
|
|
// Make sure we have a valid shared memory buffer
|
|
// Note: Take the GameNode lock so that nobody changes the flags
|
|
// for the buffers, or the buffers themselves out from under us.
|
|
ENTER_DPLGAMENODE();
|
|
if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
|
|
{
|
|
hr = PRV_SetupAllSharedMemory(lpgn);
|
|
if(FAILED(hr))
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
DPF(2, "Unable to access App Data memory");
|
|
return hr;
|
|
}
|
|
}
|
|
LEAVE_DPLGAMENODE();
|
|
|
|
|
|
// Grab the mutex
|
|
hMutex = (lpgn->dwFlags & GN_LOBBY_CLIENT) ?
|
|
(lpgn->hLobbyWriteMutex) : (lpgn->hGameWriteMutex);
|
|
WaitForSingleObject(hMutex, INFINITE);
|
|
|
|
// Get a pointer to our control structure
|
|
lpControl = (LPDPLOBBYI_BUFFERCONTROL)((lpgn->dwFlags &
|
|
GN_LOBBY_CLIENT) ? (lpgn->lpLobbyWriteBuffer)
|
|
: (lpgn->lpGameWriteBuffer));
|
|
|
|
// If we're in wait mode, bail
|
|
if(lpControl->dwFlags & BC_WAIT_MODE)
|
|
{
|
|
DPF_ERR("Cannot send lobby message while in Wait Mode for new ConnectionSettings");
|
|
hr = DPERR_UNAVAILABLE;
|
|
goto EXIT_WRITE_CLIENT_DATA;
|
|
}
|
|
|
|
// If we are the game, check to see if the lobby client is even there. In
|
|
// the self-lobbied case, it won't be. If it is not there, don't even
|
|
// bother sending anything.
|
|
if((!(lpgn->dwFlags & GN_LOBBY_CLIENT)) && (!(lpControl->dwFlags
|
|
& BC_LOBBY_ACTIVE)))
|
|
{
|
|
DPF(5, "There is not active lobby client; Not sending message");
|
|
hr = DPERR_UNAVAILABLE;
|
|
goto EXIT_WRITE_CLIENT_DATA;
|
|
}
|
|
|
|
// Make sure there is enough space left for the message and two dwords
|
|
if(lpControl->dwBufferLeft < (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)))
|
|
{
|
|
DPF(5, "Not enough space left in the message buffer");
|
|
hr = DPERR_BUFFERTOOSMALL;
|
|
goto EXIT_WRITE_CLIENT_DATA;
|
|
}
|
|
|
|
if(lpControl->dwBufferLeft > MAX_APPDATABUFFERSIZE-(sizeof(DPLOBBYI_BUFFERCONTROL)))
|
|
{
|
|
DPF(4,"SECURITY WARN: invalid amount of buffer left in write buffer");
|
|
hr=DPERR_UNAVAILABLE;
|
|
goto EXIT_WRITE_CLIENT_DATA;
|
|
}
|
|
|
|
// SECURITY: need to snapshot these so they aren't
|
|
// altered by attacking code during processing
|
|
dwReadOffset = lpControl->dwReadOffset;
|
|
dwWriteOffset = lpControl->dwWriteOffset;
|
|
dwBufferSize = lpControl->dwBufferSize;
|
|
|
|
if(dwReadOffset >= MAX_APPDATABUFFERSIZE || dwWriteOffset >= MAX_APPDATABUFFERSIZE)
|
|
{
|
|
DPF(4,"SECURITY WARN: invalid offsets found in shared memory control block, bailing");
|
|
hr=DPERR_UNAVAILABLE;
|
|
goto EXIT_WRITE_CLIENT_DATA;
|
|
}
|
|
|
|
if(dwBufferSize != MAX_APPDATABUFFERSIZE)
|
|
{
|
|
DPF(4,"SECURITY WARN: shared memory control block buffer size tampered with, bailing");
|
|
hr=DPERR_UNAVAILABLE;
|
|
goto EXIT_WRITE_CLIENT_DATA;
|
|
}
|
|
|
|
// Copy in the data. First make sure we can write from the cursor
|
|
// forward without having to wrap around to the beginning of the buffer,
|
|
// but make sure we don't write past the read cursor
|
|
if(dwWriteOffset >= dwReadOffset)
|
|
{
|
|
// Our write pointer is ahead of our read pointer (cool). Figure
|
|
// out if we have enough room between our write pointer and the
|
|
// end of the buffer. If we do, then just write it. If we don't
|
|
// we need to wrap it.
|
|
dwSizeToEnd = dwBufferSize - dwWriteOffset;
|
|
if(dwSizeToEnd >= (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)))
|
|
{
|
|
// We have enough room
|
|
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl
|
|
+ dwWriteOffset);
|
|
lpHeader->dwSize = dwSize;
|
|
lpHeader->dwFlags = dwFlags;
|
|
lpTemp = (LPBYTE)(++lpHeader);
|
|
|
|
memcpySecureD(lpTemp, lpData, dwSize,
|
|
lpControl, MAX_APPDATABUFFERSIZE,
|
|
"SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
|
|
hr=DPERR_UNAVAILABLE,
|
|
EXIT_WRITE_CLIENT_DATA);
|
|
|
|
|
|
// Move the write cursor, and check to see if we have enough
|
|
// room for the header on the next message. If the move causes
|
|
// us to wrap, or if we are within one header's size,
|
|
// we need to move the write cursor back to the beginning
|
|
// of the buffer
|
|
dwWriteOffset += dwSize + sizeof(DPLOBBYI_MESSAGEHEADER);
|
|
if(dwWriteOffset > (dwBufferSize -
|
|
sizeof(DPLOBBYI_MESSAGEHEADER)))
|
|
{
|
|
// Increment the amount of free buffer by the amount we
|
|
// are about to skip over to wrap
|
|
lpControl->dwBufferLeft -= (lpControl->dwBufferSize -
|
|
dwWriteOffset);
|
|
|
|
// We're closer than one header's size
|
|
dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// We don't have enough room before the end, so we need to
|
|
// wrap the message (ugh). Here's the rules:
|
|
// 1. If we don't have enough bytes for the header, start
|
|
// the whole thing at the beginning of the buffer
|
|
// 2. If we have enough bytes, write as much
|
|
// as we can and wrap the rest.
|
|
if(dwSizeToEnd < sizeof(DPLOBBYI_MESSAGEHEADER))
|
|
{
|
|
// We don't even have room for our two dwords, so wrap
|
|
// the whole thing. So first decrement the amount of
|
|
// free memory left and make sure we will still fit
|
|
lpControl->dwBufferLeft -= dwSizeToEnd;
|
|
if(lpControl->dwBufferLeft < (dwSize +
|
|
sizeof(DPLOBBYI_MESSAGEHEADER)))
|
|
{
|
|
DPF(5, "Not enough space left in the message buffer");
|
|
hr = DPERR_BUFFERTOOSMALL;
|
|
goto EXIT_WRITE_CLIENT_DATA;
|
|
}
|
|
|
|
// Reset the write pointer and copy
|
|
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl +
|
|
sizeof(DPLOBBYI_BUFFERCONTROL));
|
|
lpHeader->dwSize = dwSize;
|
|
lpHeader->dwFlags = dwFlags;
|
|
lpTemp = (LPBYTE)(++lpHeader);
|
|
|
|
memcpySecureD(lpTemp, lpData, dwSize,
|
|
lpControl, MAX_APPDATABUFFERSIZE,
|
|
"SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
|
|
hr=DPERR_UNAVAILABLE,
|
|
EXIT_WRITE_CLIENT_DATA);
|
|
|
|
// Move the write cursor
|
|
dwWriteOffset += sizeof(DPLOBBYI_BUFFERCONTROL) +
|
|
(dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
|
|
}
|
|
else
|
|
{
|
|
// We at least have enough room for the two dwords
|
|
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl
|
|
+ dwWriteOffset);
|
|
lpHeader->dwSize = dwSize;
|
|
lpHeader->dwFlags = dwFlags;
|
|
|
|
// Now figure out how much we can write
|
|
lpTemp = (LPBYTE)(++lpHeader);
|
|
dwSizeToEnd -= sizeof(DPLOBBYI_MESSAGEHEADER);
|
|
if(!dwSizeToEnd)
|
|
{
|
|
// We need to wrap to write the whole message
|
|
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
memcpySecureD(lpTemp, lpData, dwSize,
|
|
lpControl, MAX_APPDATABUFFERSIZE,
|
|
"SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
|
|
hr=DPERR_UNAVAILABLE,
|
|
EXIT_WRITE_CLIENT_DATA);
|
|
|
|
// Move the write cursor
|
|
dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL)
|
|
+ dwSize;
|
|
}
|
|
else
|
|
{
|
|
memcpySecureD(lpTemp, lpData, dwSizeToEnd,
|
|
lpControl, MAX_APPDATABUFFERSIZE,
|
|
"SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
|
|
hr=DPERR_UNAVAILABLE,
|
|
EXIT_WRITE_CLIENT_DATA);
|
|
|
|
// Move both pointers and finish the job
|
|
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
|
|
memcpySecureD(lpTemp, ((LPBYTE)lpData+dwSizeToEnd), (dwSize-dwSizeToEnd),
|
|
lpControl, MAX_APPDATABUFFERSIZE,
|
|
"SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
|
|
hr=DPERR_UNAVAILABLE,
|
|
EXIT_WRITE_CLIENT_DATA);
|
|
|
|
|
|
// Move the write cursor
|
|
dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL)
|
|
+ (dwSize - dwSizeToEnd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Our read pointer is ahead of our write pointer. Since we checked
|
|
// and found there is enough room to write, we should just be able
|
|
// to just slam this guy in.
|
|
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl +
|
|
dwWriteOffset);
|
|
lpHeader->dwSize = dwSize;
|
|
lpHeader->dwFlags = dwFlags;
|
|
lpTemp = (LPBYTE)(++lpHeader);
|
|
memcpySecureD(lpTemp, lpData, dwSize,
|
|
lpControl, MAX_APPDATABUFFERSIZE,
|
|
"SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
|
|
hr=DPERR_UNAVAILABLE,
|
|
EXIT_WRITE_CLIENT_DATA);
|
|
|
|
// Move the write cursor
|
|
dwWriteOffset += dwSize + sizeof(DPLOBBYI_MESSAGEHEADER);
|
|
}
|
|
|
|
lpControl->dwWriteOffset = dwWriteOffset;
|
|
|
|
// Decrement the amount of free space left and increment the message count
|
|
lpControl->dwBufferLeft -= (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
|
|
lpControl->dwMessages++;
|
|
|
|
// Signal the other user that we have written something
|
|
SetEvent((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
|
|
(lpgn->hLobbyWriteEvent) : (lpgn->hGameWriteEvent));
|
|
|
|
// Fall through
|
|
|
|
EXIT_WRITE_CLIENT_DATA:
|
|
|
|
// Release the mutex
|
|
ReleaseMutex(hMutex);
|
|
return hr;
|
|
|
|
} // PRV_WriteClientData
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_SendStandardSystemMessage"
|
|
HRESULT PRV_SendStandardSystemMessage(LPDIRECTPLAYLOBBY lpDPL,
|
|
DWORD dwMessage, DWORD dwGameID)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this = NULL;
|
|
LPDPLOBBYI_GAMENODE lpgn = NULL;
|
|
HRESULT hr;
|
|
DWORD dwMessageSize;
|
|
LPVOID lpmsg = NULL;
|
|
DWORD dwFlags;
|
|
|
|
|
|
DPF(7, "Entering PRV_SendStandardSystemMessage");
|
|
DPF(9, "Parameters: 0x%08x, %lu, %lu",
|
|
lpDPL, dwMessage, dwGameID);
|
|
|
|
ENTER_DPLOBBY();
|
|
|
|
TRY
|
|
{
|
|
if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDINTERFACE;
|
|
}
|
|
|
|
this = DPLOBJECT_FROM_INTERFACE(lpDPL);
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
}
|
|
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// If dwGameID is zero it means that we are the game, so
|
|
// we need to get the current process ID. Otherwise, it
|
|
// means we are the lobby client
|
|
if(!dwGameID)
|
|
dwGameID = GetCurrentProcessId();
|
|
|
|
// Now find the correct game node
|
|
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
|
|
if(!lpgn)
|
|
{
|
|
if(FAILED(PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, FALSE,NULL)))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
// Get the size of the message
|
|
switch(dwMessage)
|
|
{
|
|
case DPLSYS_NEWSESSIONHOST:
|
|
dwMessageSize = sizeof(DPLMSG_NEWSESSIONHOST);
|
|
break;
|
|
|
|
default:
|
|
dwMessageSize = sizeof(DPLMSG_SYSTEMMESSAGE);
|
|
break;
|
|
}
|
|
|
|
// Allocate a buffer for the message
|
|
lpmsg = DPMEM_ALLOC(dwMessageSize);
|
|
if(!lpmsg)
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERRVAL("Unable to allocate memory for lobby system message, dwMessage = %lu", dwMessage);
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
// Setup the message
|
|
((LPDPLMSG_SYSTEMMESSAGE)lpmsg)->dwType = dwMessage;
|
|
((LPDPLMSG_SYSTEMMESSAGE)lpmsg)->guidInstance = lpgn->guidInstance;
|
|
|
|
// Write into the shared buffer
|
|
dwFlags = DPLMSG_SYSTEM | DPLMSG_STANDARD;
|
|
hr = PRV_WriteClientData(lpgn, dwFlags, lpmsg, dwMessageSize);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF(8, "Couldn't send system message");
|
|
}
|
|
|
|
// Free our buffer
|
|
DPMEM_FREE(lpmsg);
|
|
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
|
|
} // PRV_SendStandardSystemMessage
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_AddNewRequestNode"
|
|
HRESULT PRV_AddNewRequestNode(LPDPLOBBYI_DPLOBJECT this,
|
|
LPDPLOBBYI_GAMENODE lpgn, LPDPLMSG_GENERIC lpmsg, BOOL bSlamGuid)
|
|
{
|
|
LPDPLOBBYI_REQUESTNODE lprn = NULL;
|
|
|
|
|
|
// Allocate memory for a Request Node
|
|
lprn = DPMEM_ALLOC(sizeof(DPLOBBYI_REQUESTNODE));
|
|
if(!lprn)
|
|
{
|
|
DPF_ERR("Unable to allocate memory for request node, system message not sent");
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
// Setup the request node
|
|
lprn->dwFlags = lpgn->dwFlags;
|
|
lprn->dwRequestID = this->dwCurrentRequest;
|
|
lprn->dwAppRequestID = ((LPDPLMSG_GETPROPERTY)lpmsg)->dwRequestID;
|
|
lprn->lpgn = lpgn;
|
|
|
|
// Add the slammed guid flag if needed
|
|
if(bSlamGuid)
|
|
lprn->dwFlags |= GN_SLAMMED_GUID;
|
|
|
|
// Change the request ID in the message to our internal one (we'll
|
|
// change it back on Receive
|
|
((LPDPLMSG_GETPROPERTY)lpmsg)->dwRequestID = this->dwCurrentRequest++;
|
|
|
|
// Add the node to the list
|
|
if(this->lprnHead)
|
|
this->lprnHead->lpPrev = lprn;
|
|
lprn->lpNext = this->lprnHead;
|
|
this->lprnHead = lprn;
|
|
|
|
return DP_OK;
|
|
|
|
} // PRV_AddNewRequestNode
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_RemoveRequestNode"
|
|
void PRV_RemoveRequestNode(LPDPLOBBYI_DPLOBJECT this,
|
|
LPDPLOBBYI_REQUESTNODE lprn)
|
|
{
|
|
// If we're the head, move it
|
|
if(lprn == this->lprnHead)
|
|
this->lprnHead = lprn->lpNext;
|
|
|
|
// Fixup the previous & next pointers
|
|
if(lprn->lpPrev)
|
|
lprn->lpPrev->lpNext = lprn->lpNext;
|
|
if(lprn->lpNext)
|
|
lprn->lpNext->lpPrev = lprn->lpPrev;
|
|
|
|
// Free the node
|
|
DPMEM_FREE(lprn);
|
|
|
|
} // PRV_RemoveRequestNode
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_ForwardMessageToLobbyServer"
|
|
HRESULT PRV_ForwardMessageToLobbyServer(LPDPLOBBYI_GAMENODE lpgn,
|
|
LPVOID lpBuffer, DWORD dwSize, BOOL bStandard)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this;
|
|
LPDPLMSG_GENERIC lpmsg = NULL;
|
|
HRESULT hr;
|
|
BOOL bSlamGuid = FALSE;
|
|
|
|
|
|
DPF(7, "Entering PRV_ForwardMessageToLobbyServer");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, %lu",
|
|
lpgn, lpBuffer, dwSize, bStandard);
|
|
|
|
|
|
TRY
|
|
{
|
|
// Validate the dplay object
|
|
hr = VALID_DPLAY_PTR( lpgn->lpDPlayObject );
|
|
if (FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("bad dplay ptr - hr = 0x%08lx\n",hr);
|
|
return hr;
|
|
}
|
|
|
|
// Validate the lobby object
|
|
this = lpgn->lpDPlayObject->lpLobbyObject;
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
DPF_ERR("Invalid lobby object");
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
}
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// If this is a property request, we need to create a request node
|
|
lpmsg = (LPDPLMSG_GENERIC)lpBuffer;
|
|
if(bStandard)
|
|
{
|
|
// If it's a property message, we need a request node
|
|
switch(lpmsg->dwType)
|
|
{
|
|
case DPLSYS_GETPROPERTY:
|
|
{
|
|
LPDPLMSG_GETPROPERTY lpgp = lpBuffer;
|
|
|
|
// If it's a GETPROPERTY message, we need to check to see if
|
|
// the player guid is NULL. If it is, we need to
|
|
// stuff the game's Instance guid in that field
|
|
if(IsEqualGUID(&lpgp->guidPlayer, &GUID_NULL))
|
|
{
|
|
// Stuff the instance guid of the game
|
|
lpgp->guidPlayer = lpgn->guidInstance;
|
|
bSlamGuid = TRUE;
|
|
}
|
|
|
|
// Add a request node to the pending requests list
|
|
hr = PRV_AddNewRequestNode(this, lpgn, lpmsg, bSlamGuid);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Unable to add request node to list, hr = 0x%08x", hr);
|
|
return hr;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DPLSYS_SETPROPERTY:
|
|
{
|
|
LPDPLMSG_SETPROPERTY lpsp = lpBuffer;
|
|
|
|
// If it's a SETPROPERTY message, we need to check to see if
|
|
// the player guid is NULL. If it is, we need to
|
|
// stuff the game's Instance guid in that field
|
|
if(IsEqualGUID(&lpsp->guidPlayer, &GUID_NULL))
|
|
{
|
|
// Stuff the instance guid of the game
|
|
lpsp->guidPlayer = lpgn->guidInstance;
|
|
bSlamGuid = TRUE;
|
|
}
|
|
|
|
// If the request ID is zero, we don't need to swap
|
|
// the ID's or add a pending request
|
|
if(lpsp->dwRequestID != 0)
|
|
{
|
|
// Add a request node to the pending requests list
|
|
hr = PRV_AddNewRequestNode(this, lpgn, lpmsg, bSlamGuid);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Unable to add request node to list, hr = 0x%08x", hr);
|
|
return hr;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DPLSYS_NEWSESSIONHOST:
|
|
((LPDPLMSG_NEWSESSIONHOST)lpBuffer)->guidInstance = lpgn->guidInstance;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Call Send on the lobby object
|
|
hr = PRV_Send(this, lpgn->dpidPlayer, DPID_SERVERPLAYER,
|
|
DPSEND_LOBBYSYSTEMMESSAGE, lpBuffer, dwSize);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Failed sending lobby message, hr = 0x%08x", hr);
|
|
}
|
|
|
|
return hr;
|
|
|
|
} // PRV_ForwardMessageToLobbyServer
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_InjectMessageInQueue"
|
|
HRESULT PRV_InjectMessageInQueue(LPDPLOBBYI_GAMENODE lpgn, DWORD dwFlags,
|
|
LPVOID lpData, DWORD dwSize, BOOL bForward)
|
|
{
|
|
LPDPLOBBYI_MESSAGE lpm = NULL;
|
|
LPVOID lpBuffer = NULL;
|
|
HRESULT hr;
|
|
|
|
|
|
DPF(7, "Entering PRV_InjectMessageInQueue");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu, %lu",
|
|
lpgn, dwFlags, lpData, dwSize, bForward);
|
|
|
|
ASSERT(lpData);
|
|
|
|
// Allocate memory for the node and the data buffer
|
|
lpm = DPMEM_ALLOC(sizeof(DPLOBBYI_MESSAGE));
|
|
lpBuffer = DPMEM_ALLOC(dwSize);
|
|
if((!lpm) || (!lpBuffer))
|
|
{
|
|
DPF_ERR("Unable to allocate memory for system message");
|
|
if(lpm)
|
|
DPMEM_FREE(lpm);
|
|
if(lpBuffer)
|
|
DPMEM_FREE(lpBuffer);
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
// Copy the data
|
|
memcpy(lpBuffer, lpData, dwSize);
|
|
|
|
// Before we put it in our own queue, forward it onto the lobby server
|
|
// if there is one.
|
|
if(bForward && (lpgn->dwFlags & GN_CLIENT_LAUNCHED))
|
|
{
|
|
hr = PRV_ForwardMessageToLobbyServer(lpgn, lpData, dwSize, FALSE);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Failed forwarding system message to lobby server, hr = 0x%08x", hr);
|
|
}
|
|
}
|
|
|
|
// Save the data pointer & the external flags
|
|
// Note: If we're injecting this, it has to be a system message,
|
|
// so set the flag just in case we forgot elsewhere.
|
|
lpm->dwFlags = (dwFlags | DPLAD_SYSTEM);
|
|
lpm->dwSize = dwSize;
|
|
lpm->lpData = lpBuffer;
|
|
|
|
// Add the message to the end of the queue & increment the count
|
|
ENTER_DPLQUEUE();
|
|
lpm->lpPrev = lpgn->MessageHead.lpPrev;
|
|
lpgn->MessageHead.lpPrev->lpNext = lpm;
|
|
lpgn->MessageHead.lpPrev = lpm;
|
|
lpm->lpNext = &lpgn->MessageHead;
|
|
|
|
lpgn->dwMessageCount++;
|
|
LEAVE_DPLQUEUE();
|
|
|
|
// Kick the event handle
|
|
if(lpgn->hDupReceiveEvent)
|
|
{
|
|
SetEvent(lpgn->hDupReceiveEvent);
|
|
}
|
|
|
|
return DP_OK;
|
|
|
|
} // PRV_InjectMessageInQueue
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_ReadClientData"
|
|
HRESULT PRV_ReadClientData(LPDPLOBBYI_GAMENODE lpgn, LPDWORD lpdwFlags,
|
|
LPVOID lpData, LPDWORD lpdwDataSize)
|
|
{
|
|
LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
|
|
LPDPLOBBYI_MESSAGEHEADER lpHeader = NULL;
|
|
DWORD dwSize = 0;
|
|
DWORD_PTR dwSizeToEnd = 0;
|
|
HANDLE hMutex = NULL;
|
|
LPBYTE lpTemp = NULL;
|
|
LPBYTE lpEnd = NULL;
|
|
HRESULT hr = DP_OK;
|
|
|
|
DWORD dwReadOffset;
|
|
DWORD dwBufferSize;
|
|
|
|
DPF(7, "Entering PRV_ReadClientData");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
|
lpgn, lpdwFlags, lpData, lpdwDataSize);
|
|
|
|
// Make sure we have a valid shared memory buffer
|
|
// Note: Take the GameNode lock so that nobody changes the flags
|
|
// for the buffers, or the buffers themselves out from under us.
|
|
ENTER_DPLGAMENODE();
|
|
if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
|
|
{
|
|
hr = PRV_SetupAllSharedMemory(lpgn);
|
|
if(FAILED(hr))
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
DPF(2, "Unable to access App Data memory");
|
|
return hr;
|
|
}
|
|
}
|
|
LEAVE_DPLGAMENODE();
|
|
|
|
// Grab the mutex
|
|
// REVIEW!!!! -- Is there anything that might cause this wait to hang????
|
|
hMutex = (lpgn->dwFlags & GN_LOBBY_CLIENT) ?
|
|
(lpgn->hGameWriteMutex) : (lpgn->hLobbyWriteMutex);
|
|
WaitForSingleObject(hMutex, INFINITE);
|
|
|
|
// Get a pointer to our control structure
|
|
lpControl = (LPDPLOBBYI_BUFFERCONTROL)((lpgn->dwFlags &
|
|
GN_LOBBY_CLIENT) ? (lpgn->lpGameWriteBuffer)
|
|
: (lpgn->lpLobbyWriteBuffer));
|
|
|
|
// Make sure there are any messages in the buffer
|
|
if(!lpControl->dwMessages)
|
|
{
|
|
DPF(8, "No messages in shared buffer");
|
|
hr = DPERR_NOMESSAGES;
|
|
goto EXIT_READ_CLIENT_DATA;
|
|
}
|
|
|
|
// SECURITY need to snapshot these values so hackers
|
|
// can't change in the middle of accessing shared object.
|
|
dwReadOffset=lpControl->dwReadOffset;
|
|
dwBufferSize=lpControl->dwBufferSize;
|
|
|
|
// Make sure there is enough space for the message
|
|
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl
|
|
+ dwReadOffset);
|
|
|
|
if(lpControl->dwReadOffset >= MAX_APPDATABUFFERSIZE)
|
|
{
|
|
DPF(4,"SECURITY WARN: local application hacking shared memory, lobby connection broken");
|
|
hr=DPERR_NOMESSAGES;
|
|
goto EXIT_READ_CLIENT_DATA;
|
|
}
|
|
|
|
dwSize = lpHeader->dwSize;
|
|
|
|
if(dwSize > MAX_APPDATABUFFERSIZE-(sizeof(DPLOBBYI_BUFFERCONTROL)+sizeof(DPLOBBYI_MESSAGEHEADER))){
|
|
DPF(4,"SECURITY WARN: lobby message size %d is too large",lpHeader->dwSize);
|
|
hr=DPERR_NOMESSAGES;
|
|
goto EXIT_READ_CLIENT_DATA;
|
|
}
|
|
|
|
// Set the output data size (even if we fail, we want to return it)
|
|
if(lpdwDataSize)
|
|
*lpdwDataSize = dwSize;
|
|
|
|
if((!lpData) || (dwSize > *lpdwDataSize))
|
|
{
|
|
DPF(8, "Message buffer is too small, must be at least %d bytes", dwSize);
|
|
hr = DPERR_BUFFERTOOSMALL;
|
|
goto EXIT_READ_CLIENT_DATA;
|
|
}
|
|
|
|
// Set the output flags
|
|
if(lpdwFlags)
|
|
*lpdwFlags = lpHeader->dwFlags;
|
|
|
|
if(dwBufferSize != MAX_APPDATABUFFERSIZE) // note, makes storing this value in header redundant.
|
|
{
|
|
DPF(4, "SECURITY WARN: shared app memory has been tampered with");
|
|
hr = DPERR_NOMESSAGES;
|
|
goto EXIT_READ_CLIENT_DATA;
|
|
}
|
|
|
|
// Now check and see if we are going to wrap. If we are, some of the message
|
|
// will be at the end of the buffer, some will be at the beginning.
|
|
lpTemp = (LPBYTE)(++lpHeader) + dwSize;
|
|
if(lpTemp > ((LPBYTE)lpControl + dwBufferSize))
|
|
{
|
|
// Figure out where we need to wrap
|
|
dwSizeToEnd = ((LPBYTE)lpControl + dwBufferSize)
|
|
- (LPBYTE)(lpHeader);
|
|
|
|
if(!dwSizeToEnd)
|
|
{
|
|
// We are at the end, so the whole message must be at the
|
|
// beginning of the buffer
|
|
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
memcpy(lpData, lpTemp, dwSize);
|
|
|
|
// Move the read cursor
|
|
dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL) + dwSize;
|
|
}
|
|
else
|
|
{
|
|
// Copy the first part of the data
|
|
lpTemp = (LPBYTE)lpHeader;
|
|
memcpy(lpData, lpTemp, (DWORD)dwSizeToEnd);
|
|
|
|
// Move the read cursor and copy the rest
|
|
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
memcpy(((LPBYTE)lpData + dwSizeToEnd), lpTemp,
|
|
(DWORD)(dwSize - dwSizeToEnd));
|
|
|
|
// Move the read pointer
|
|
dwReadOffset = (DWORD)(sizeof(DPLOBBYI_BUFFERCONTROL)
|
|
+ (dwSize - dwSizeToEnd));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We don't have to wrap (cool).
|
|
lpTemp = (LPBYTE)lpHeader;
|
|
memcpy(lpData, lpTemp, dwSize);
|
|
|
|
// Move the read pointer. If there are less than 8 bytes left in the
|
|
// buffer, we should move the read pointer to the beginning. We need
|
|
// to add however many bytes we skip (at the end) back into our free
|
|
// buffer memory counter.
|
|
lpTemp += dwSize;
|
|
lpEnd = (LPBYTE)lpControl + dwBufferSize;
|
|
if(lpTemp > (lpEnd - sizeof(DPLOBBYI_MESSAGEHEADER)))
|
|
{
|
|
// Move the read cursor to the beginning
|
|
dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
|
|
|
|
// Add the number of bytes to the free buffer total
|
|
lpControl->dwBufferLeft += (DWORD)(lpEnd - lpTemp);
|
|
}
|
|
else
|
|
dwReadOffset += (DWORD)(dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
|
|
}
|
|
|
|
lpControl->dwReadOffset=dwReadOffset;
|
|
|
|
// Increment the amount of free space left and decrement the message count
|
|
lpControl->dwBufferLeft += (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
|
|
lpControl->dwMessages--;
|
|
|
|
// Fall through
|
|
|
|
EXIT_READ_CLIENT_DATA:
|
|
|
|
// Release the mutex
|
|
ReleaseMutex(hMutex);
|
|
|
|
return hr;
|
|
|
|
} // PRV_ReadClientData
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_ReceiveClientNotification"
|
|
DWORD WINAPI PRV_ReceiveClientNotification(LPVOID lpParam)
|
|
{
|
|
LPDPLOBBYI_GAMENODE lpgn = (LPDPLOBBYI_GAMENODE)lpParam;
|
|
LPDPLOBBYI_MESSAGE lpm = NULL;
|
|
LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
|
|
LPDPLMSG_GENERIC lpmsg = NULL;
|
|
HRESULT hr;
|
|
HANDLE hEvents[4];
|
|
LPVOID lpBuffer = NULL;
|
|
DWORD dwFlags;
|
|
DWORD dwSize;
|
|
DWORD dwReturn;
|
|
BOOL bForward;
|
|
|
|
DWORD dwWait=INFINITE;
|
|
DWORD nWait=2;
|
|
|
|
DPF(7, "Entering PRV_ReceiveClientNotification");
|
|
DPF(9, "Parameters: 0x%08x", lpParam);
|
|
|
|
// Make sure we have a valid shared memory buffer
|
|
// Note: Take the GameNode lock so that nobody changes the flags
|
|
// for the buffers, or the buffers themselves out from under us.
|
|
ENTER_DPLGAMENODE();
|
|
if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
|
|
{
|
|
BOOL bGameCreate=FALSE;
|
|
|
|
|
|
DPF(2, "NOTE: ReceiveClientNotification thread starting without shared memory set up. Setting up now.");
|
|
|
|
// HACK!!!! -- SetLobbyMessageReceiveEvent may get called from
|
|
// the game without having been lobbied yet. If that is the case,
|
|
// we need to create the shared memory buffer. If we don't do
|
|
// that, we may miss messages.
|
|
|
|
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
|
|
{
|
|
// Fake the setup routine by setting the lobby client flag
|
|
lpgn->dwFlags |= GN_LOBBY_CLIENT;
|
|
|
|
// Set our flag
|
|
bGameCreate = TRUE;
|
|
}
|
|
|
|
hr = PRV_SetupAllSharedMemory(lpgn);
|
|
|
|
// HACK!!!! -- Reset the settings we changed to fake the setup routines
|
|
if(bGameCreate)
|
|
{
|
|
lpgn->dwFlags &= (~GN_LOBBY_CLIENT);
|
|
}
|
|
|
|
|
|
//hr = PRV_SetupAllSharedMemory(lpgn);
|
|
if(FAILED(hr))
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
DPF(2, "Unable to access App Data memory");
|
|
return 0L;
|
|
}
|
|
}
|
|
LEAVE_DPLGAMENODE();
|
|
|
|
// Setup the two events -- one receive event, one kill event
|
|
hEvents[0] = ((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
|
|
(lpgn->hGameWriteEvent) : (lpgn->hLobbyWriteEvent));
|
|
hEvents[1] = lpgn->hKillReceiveThreadEvent;
|
|
|
|
if(lpgn->hLobbyClientProcess){
|
|
nWait=3;
|
|
hEvents[2] = lpgn->hLobbyClientProcess;
|
|
} else {
|
|
hEvents[2] = INVALID_HANDLE_VALUE;
|
|
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)){
|
|
dwWait = 5000;
|
|
}
|
|
}
|
|
// This extra handle is here because of a Windows 95 bug. Windows
|
|
// will occasionally miss when it walks the handle table, causing
|
|
// my thread to wait on the wrong handles. By putting a guaranteed
|
|
// invalid handle at the end of our array, the kernel will do a
|
|
// forced re-walk of the handle table and find the correct handles.
|
|
hEvents[3] = INVALID_HANDLE_VALUE;
|
|
|
|
// Make sure we have a valid event
|
|
if(!hEvents[0] || !hEvents[1])
|
|
{
|
|
DPF(2, "Either the Write Event or the Kill Event is NULL and it shouldn't be!");
|
|
ExitThread(0L);
|
|
return 0;
|
|
}
|
|
|
|
// If we are the game, we should check the buffer to see if any messages
|
|
// already exist in the shared buffer.
|
|
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
|
|
{
|
|
lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
|
|
// If there are any messages, kick our event so that our receive
|
|
// loop will immediately put the messages in the queue
|
|
if(lpControl->dwMessages)
|
|
SetEvent(hEvents[0]);
|
|
}
|
|
|
|
// Wait for the event notification
|
|
while(1)
|
|
{
|
|
// Sleep until something shows up
|
|
dwReturn = WaitForMultipleObjects(nWait, (HANDLE *)hEvents,
|
|
FALSE, dwWait);
|
|
|
|
if(dwReturn == WAIT_TIMEOUT){
|
|
ASSERT(!(lpgn->dwFlags & GN_LOBBY_CLIENT));
|
|
if(lpgn->hLobbyClientProcess){
|
|
DPF(9,"Got the lobby client process handle, adding to wait list\n");
|
|
hEvents[2] = lpgn->hLobbyClientProcess;
|
|
nWait = 3;
|
|
dwWait=INFINITE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if(nWait==3 && dwReturn == WAIT_OBJECT_0 + 2){
|
|
// send the dead lobby client message
|
|
DPLMSG_SYSTEMMESSAGE msg;
|
|
|
|
if(lpgn->dwLobbyClientProcessID){
|
|
memset(&msg, 0, sizeof(DPLMSG_SYSTEMMESSAGE));
|
|
msg.dwType = DPLSYS_LOBBYCLIENTRELEASE;
|
|
msg.guidInstance = lpgn->guidInstance;
|
|
lpgn->dwLobbyClientProcessID = 0;
|
|
hr = PRV_InjectMessageInQueue(lpgn, DPLMSG_SYSTEM | DPLMSG_STANDARD, &msg,
|
|
sizeof(DPLMSG_SYSTEMMESSAGE), FALSE);
|
|
|
|
}
|
|
nWait=2;
|
|
hEvents[2]=INVALID_HANDLE_VALUE;
|
|
continue;
|
|
}
|
|
|
|
// If the return value was anything bug the receive event,
|
|
// kill the thread
|
|
if(dwReturn != WAIT_OBJECT_0)
|
|
{
|
|
if(dwReturn == WAIT_FAILED)
|
|
{
|
|
// This is a Windows 95 bug -- We may have gotten
|
|
// kicked for no reason. If that was the case, we
|
|
// still have valid handles (we think), the OS
|
|
// just goofed up. So, validate the handle and if
|
|
// they are valid, just return to waiting. See
|
|
// bug #3340 for a better explanation.
|
|
if(ERROR_INVALID_HANDLE == GetLastError())
|
|
{
|
|
if(!OS_IsValidHandle(hEvents[0]))
|
|
break;
|
|
if(!OS_IsValidHandle(hEvents[1]))
|
|
break;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// It is either our kill event, or something we don't
|
|
// understand or expect. In this case, let's exit.
|
|
break;
|
|
}
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
// First, call PRV_ReadClientData to get the size of the data
|
|
hr = PRV_ReadClientData(lpgn, NULL, NULL, &dwSize);
|
|
|
|
// If there are no messages, end the while loop
|
|
if(hr == DPERR_NOMESSAGES)
|
|
break;
|
|
|
|
// Otherwise, we should get the BUFFERTOOSMALL case
|
|
if(hr != DPERR_BUFFERTOOSMALL)
|
|
{
|
|
// We should never have a problem here
|
|
DPF_ERRVAL("Recieved an unexpected error reading from shared buffer, hr = 0x%08x", hr);
|
|
ASSERT(FALSE);
|
|
// Might as well keep trying
|
|
break;
|
|
}
|
|
|
|
// Allocate memory for the node and the data buffer
|
|
lpm = DPMEM_ALLOC(sizeof(DPLOBBYI_MESSAGE));
|
|
lpBuffer = DPMEM_ALLOC(dwSize);
|
|
if((!lpm) || (!lpBuffer))
|
|
{
|
|
DPF_ERR("Unable to allocate memory for message");
|
|
ASSERT(FALSE);
|
|
// Might as well keep trying
|
|
break;
|
|
}
|
|
|
|
// Copy the data into our buffer
|
|
hr = PRV_ReadClientData(lpgn, &dwFlags, lpBuffer, &dwSize);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Error reading shared buffer, message not read, hr = 0x%08x", hr);
|
|
ASSERT(FALSE);
|
|
DPMEM_FREE(lpm);
|
|
DPMEM_FREE(lpBuffer);
|
|
// Might as well keep trying
|
|
break;
|
|
}
|
|
|
|
// Clear our foward flag
|
|
bForward = FALSE;
|
|
|
|
// If we are a dplay lobby client, we need to forward the message
|
|
// onto the lobby server using the IDP3 interface. If we're not,
|
|
// then just put the message in the receive queue.
|
|
if(lpgn->dwFlags & GN_CLIENT_LAUNCHED)
|
|
{
|
|
// Foward the message
|
|
hr = PRV_ForwardMessageToLobbyServer(lpgn, lpBuffer, dwSize,
|
|
((dwFlags & DPLMSG_STANDARD) ? TRUE : FALSE));
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Unable to send lobby system message, hr = 0x%08x", hr);
|
|
}
|
|
|
|
// Set the forwarded flag
|
|
bForward = TRUE;
|
|
}
|
|
|
|
// Check for an App Terminated message. If we get one off the wire,
|
|
// we need to shut down our ClientTerminateMonitor thread, signal
|
|
// this thread (the receive thread to shut down, and mark the game
|
|
// node as dead. This will keep us from sending or receiving any
|
|
// more messages from the now dead game. (This message will only
|
|
// ever be received by a lobby client).
|
|
lpmsg = (LPDPLMSG_GENERIC)lpBuffer;
|
|
if(lpmsg->dwType == DPLSYS_APPTERMINATED)
|
|
{
|
|
// Kick the TerminateMonitor thread with it's kill event
|
|
SetEvent(lpgn->hKillTermThreadEvent);
|
|
|
|
// Set this thread's kill event (so that when we get done
|
|
// reading messages out of the shared buffer, we go away)
|
|
SetEvent(lpgn->hKillReceiveThreadEvent);
|
|
|
|
// Mark the GAMENODE as dead, but don't remove it since we know
|
|
// there will still messages in the queue.
|
|
lpgn->dwFlags |= GN_DEAD_GAME_NODE;
|
|
}
|
|
|
|
// If it's one of our DX3 messages, we need to put it in the queue
|
|
// otherwise if we already forwarded it, we can free it. NOTE: All
|
|
// DX3 lobby system messages had a value between 0 and
|
|
// DPLSYS_APPTERMINATED (0x04).
|
|
if( (!bForward) || (lpmsg->dwType <= DPLSYS_APPTERMINATED))
|
|
{
|
|
|
|
if (lpmsg->dwType == DPLSYS_LOBBYCLIENTRELEASE) {
|
|
if(lpgn->dwLobbyClientProcessID){
|
|
lpgn->dwLobbyClientProcessID = 0;
|
|
} else {
|
|
goto no_queue;
|
|
}
|
|
}
|
|
|
|
|
|
// Save the data pointer & the external flags
|
|
lpm->dwFlags = dwFlags & (~DPLOBBYPR_INTERNALMESSAGEFLAGS);
|
|
lpm->dwSize = dwSize;
|
|
lpm->lpData = lpBuffer;
|
|
|
|
// Add the message to the end of the queue & increment the count
|
|
ENTER_DPLQUEUE();
|
|
lpm->lpPrev = lpgn->MessageHead.lpPrev;
|
|
lpgn->MessageHead.lpPrev->lpNext = lpm;
|
|
lpgn->MessageHead.lpPrev = lpm;
|
|
lpm->lpNext = &lpgn->MessageHead;
|
|
|
|
lpgn->dwMessageCount++;
|
|
LEAVE_DPLQUEUE();
|
|
|
|
// NOTE: There is a potential thread problem here, but we are going
|
|
// to ignore it for now. It is possible for another thread to be
|
|
// going through the SetAppData code which changes this event handle.
|
|
// The problem is if they change it after this IF statement, but
|
|
// before we call SetEvent. However, the SetEvent call will either
|
|
// succeed on the new handle, or return an error if the handle is
|
|
// changed to NULL. In either case, no harm, no foul -- we don't care.
|
|
if(!lpgn->hDupReceiveEvent)
|
|
{
|
|
DPF(8, "The Receive Event handle is NULL!");
|
|
continue;
|
|
}
|
|
|
|
SetEvent(lpgn->hDupReceiveEvent);
|
|
}
|
|
else
|
|
{
|
|
no_queue:
|
|
// Free the buffers
|
|
DPMEM_FREE(lpm);
|
|
DPMEM_FREE(lpBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
DPF(8, "Lobby Receive Thread is going away!!!!!");
|
|
ExitThread(0L);
|
|
|
|
return 0L; // avoid warning.
|
|
} // PRV_ReceiveClientNotification
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_RemoveNodeFromQueue"
|
|
void PRV_RemoveNodeFromQueue(LPDPLOBBYI_GAMENODE lpgn, LPDPLOBBYI_MESSAGE lpm)
|
|
{
|
|
DPF(7, "Entering PRV_RemoveNodeFromQueue");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x", lpgn, lpm);
|
|
|
|
ASSERT(lpgn);
|
|
ASSERT(lpm);
|
|
|
|
// Delete the message from the queue & decrement the count
|
|
lpm->lpPrev->lpNext = lpm->lpNext;
|
|
lpm->lpNext->lpPrev = lpm->lpPrev;
|
|
|
|
lpgn->dwMessageCount--;
|
|
|
|
// Free the memory for the message node
|
|
DPMEM_FREE(lpm->lpData);
|
|
DPMEM_FREE(lpm);
|
|
|
|
} // PRV_RemoveNodeFromQueue
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_CleanUpQueue"
|
|
void PRV_CleanUpQueue(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
LPDPLOBBYI_MESSAGE lpm, lpmNext;
|
|
|
|
|
|
DPF(7, "Entering PRV_CleanUpQueue");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
ASSERT(lpgn);
|
|
|
|
lpm = lpgn->MessageHead.lpNext;
|
|
while(lpm != &lpgn->MessageHead)
|
|
{
|
|
// Save the next pointer
|
|
lpmNext = lpm->lpNext;
|
|
|
|
// Remove the node
|
|
PRV_RemoveNodeFromQueue(lpgn, lpm);
|
|
|
|
// Move to the next node
|
|
lpm = lpmNext;
|
|
}
|
|
|
|
|
|
} // PRV_CleanUpQueue
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_KillThread"
|
|
void PRV_KillThread(HANDLE hThread, HANDLE hEvent)
|
|
{
|
|
|
|
DPF(7, "Entering PRV_KillThread");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x", hThread, hEvent);
|
|
|
|
ASSERT(hThread);
|
|
ASSERT(hEvent);
|
|
|
|
// Signal the thread to die.
|
|
SetEvent(hEvent);
|
|
|
|
// Wait until the thread terminates, if it doesn't something is
|
|
// wrong, so we better fix it.
|
|
DPF(8, "Starting to wait for a thread to exit -- hThread = 0x%08x, hEvent = 0x%08x", hThread, hEvent);
|
|
WaitForSingleObject(hThread, INFINITE);
|
|
|
|
// Now close both handles
|
|
CloseHandle(hThread);
|
|
CloseHandle(hEvent);
|
|
|
|
} // PRV_KillThread
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_FreeGameNode"
|
|
HRESULT PRV_FreeGameNode(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
|
|
|
|
|
|
DPF(7, "Entering PRV_FreeGameNode");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
// FIRST: Take care of the connection settings data buffer
|
|
// Unmap & release the shared memory
|
|
if(lpgn->lpConnectDataBuffer)
|
|
UnmapViewOfFile(lpgn->lpConnectDataBuffer);
|
|
|
|
if(lpgn->hConnectDataFile)
|
|
CloseHandle(lpgn->hConnectDataFile);
|
|
|
|
if(lpgn->hConnectDataMutex)
|
|
CloseHandle(lpgn->hConnectDataMutex);
|
|
|
|
// NEXT: Take care of the App Data Events & Buffers
|
|
// Kill the Receive Thread
|
|
if(lpgn->hReceiveThread)
|
|
{
|
|
PRV_KillThread(lpgn->hReceiveThread, lpgn->hKillReceiveThreadEvent);
|
|
CloseHandle(lpgn->hDupReceiveEvent);
|
|
}
|
|
|
|
// Close the event handles
|
|
if(lpgn->hLobbyWriteEvent)
|
|
CloseHandle(lpgn->hLobbyWriteEvent);
|
|
|
|
if(lpgn->hGameWriteEvent)
|
|
CloseHandle(lpgn->hGameWriteEvent);
|
|
|
|
// Kill the Terminate Monitor Thread
|
|
if(lpgn->hTerminateThread)
|
|
{
|
|
PRV_KillThread(lpgn->hTerminateThread, lpgn->hKillTermThreadEvent);
|
|
}
|
|
|
|
// Clear the flags since we are no longer going to be active
|
|
if(lpgn->lpGameWriteBuffer)
|
|
{
|
|
lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer;
|
|
lpControl->dwFlags &= ~((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
|
|
BC_LOBBY_ACTIVE : BC_GAME_ACTIVE);
|
|
}
|
|
|
|
if(lpgn->lpLobbyWriteBuffer)
|
|
{
|
|
lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
|
|
lpControl->dwFlags &= ~((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
|
|
BC_LOBBY_ACTIVE : BC_GAME_ACTIVE);
|
|
}
|
|
|
|
// Unmap & release the Game Write memory
|
|
if(lpgn->lpGameWriteBuffer)
|
|
UnmapViewOfFile(lpgn->lpGameWriteBuffer);
|
|
|
|
if(lpgn->hGameWriteFile)
|
|
CloseHandle(lpgn->hGameWriteFile);
|
|
|
|
if(lpgn->hGameWriteMutex)
|
|
CloseHandle(lpgn->hGameWriteMutex);
|
|
|
|
// Unmap & release the Lobby Write memory
|
|
if(lpgn->lpLobbyWriteBuffer)
|
|
UnmapViewOfFile(lpgn->lpLobbyWriteBuffer);
|
|
|
|
if(lpgn->hLobbyWriteFile)
|
|
CloseHandle(lpgn->hLobbyWriteFile);
|
|
|
|
if(lpgn->hLobbyWriteMutex)
|
|
CloseHandle(lpgn->hLobbyWriteMutex);
|
|
|
|
// Clean up the message queue
|
|
PRV_CleanUpQueue(lpgn);
|
|
|
|
// Close the process handle we have for the game
|
|
if(lpgn->hGameProcess)
|
|
CloseHandle(lpgn->hGameProcess);
|
|
|
|
if(lpgn->hLobbyClientProcess)
|
|
CloseHandle(lpgn->hLobbyClientProcess);
|
|
|
|
// Free the game node structure
|
|
DPMEM_FREE(lpgn);
|
|
|
|
return DP_OK;
|
|
|
|
} // PRV_FreeGameNode
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_DuplicateHandle"
|
|
HANDLE PRV_DuplicateHandle(HANDLE hSource)
|
|
{
|
|
HANDLE hProcess = NULL;
|
|
HANDLE hTarget = NULL;
|
|
DWORD dwProcessID;
|
|
DWORD dwError;
|
|
|
|
|
|
DPF(7, "Entering PRV_DuplicateHandle");
|
|
DPF(9, "Parameters: 0x%08x", hSource);
|
|
|
|
dwProcessID = GetCurrentProcessId();
|
|
hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessID);
|
|
if(!DuplicateHandle(hProcess, hSource, hProcess, &hTarget,
|
|
0L, FALSE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
dwError = GetLastError();
|
|
CloseHandle(hProcess);
|
|
return NULL;
|
|
}
|
|
|
|
CloseHandle(hProcess);
|
|
return hTarget;
|
|
|
|
} // PRV_DuplicateHandle
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DPL_SetLobbyMessageEvent"
|
|
HRESULT DPLAPI DPL_SetLobbyMessageEvent(LPDIRECTPLAYLOBBY lpDPL,
|
|
DWORD dwFlags, DWORD dwGameID,
|
|
HANDLE hReceiveEvent)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this;
|
|
LPDPLOBBYI_GAMENODE lpgn = NULL;
|
|
LPVOID lpBuffer = NULL;
|
|
HANDLE hReceiveThread = NULL;
|
|
HANDLE hDupReceiveEvent = NULL;
|
|
HRESULT hr;
|
|
BOOL bCreated = FALSE;
|
|
BOOL bLobbyClient = TRUE;
|
|
BOOL bNewEvent = FALSE;
|
|
|
|
|
|
DPF(7, "Entering DPL_SetLobbyMessageEvent");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
|
lpDPL, dwFlags, dwGameID, hReceiveEvent);
|
|
|
|
ENTER_DPLOBBY();
|
|
|
|
TRY
|
|
{
|
|
if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDINTERFACE;
|
|
}
|
|
|
|
this = DPLOBJECT_FROM_INTERFACE(lpDPL);
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
|
|
// Validate the handle
|
|
if(hReceiveEvent)
|
|
{
|
|
if(!OS_IsValidHandle(hReceiveEvent))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR("Invalid hReceiveEvent handle");
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
}
|
|
|
|
// We haven't defined any flags for this release
|
|
if( (dwFlags) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
}
|
|
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
|
|
// If the dwGameID is zero, we assume we are a game. In that case,
|
|
// the GameNode we are looking for should have our own ProcessID.
|
|
if(!dwGameID)
|
|
{
|
|
dwGameID = GetCurrentProcessId();
|
|
bLobbyClient = FALSE;
|
|
}
|
|
|
|
ENTER_DPLGAMENODE();
|
|
|
|
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
|
|
|
|
|
|
// If the event handle is null, kill our duplicate handle
|
|
if(!hReceiveEvent)
|
|
{
|
|
if(!lpgn)
|
|
{
|
|
DPF(5, "Unable to find GameNode -- Invalid dwGameID!");
|
|
LEAVE_DPLGAMENODE();
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_GENERIC;
|
|
}
|
|
|
|
CloseHandle(lpgn->hDupReceiveEvent);
|
|
lpgn->hDupReceiveEvent = NULL;
|
|
LEAVE_DPLGAMENODE();
|
|
LEAVE_DPLOBBY();
|
|
return DP_OK;
|
|
}
|
|
|
|
// If a GameNode structure exists for this process, we must be trying
|
|
// to replace the event handle, so kill the old event handle, OTHERWISE
|
|
// we need to allocate a new GameNode for this process
|
|
if(lpgn)
|
|
{
|
|
if(lpgn->hDupReceiveEvent)
|
|
{
|
|
CloseHandle(lpgn->hDupReceiveEvent);
|
|
lpgn->hDupReceiveEvent = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we are a game, go ahead and create the node
|
|
if(!bLobbyClient)
|
|
{
|
|
hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
}
|
|
|
|
// Duplicate the caller's handle in case they free it without calling
|
|
// us first to remove the Receive thread.
|
|
hDupReceiveEvent = PRV_DuplicateHandle(hReceiveEvent);
|
|
if(!hDupReceiveEvent)
|
|
{
|
|
DPF(2, "Unable to duplicate ReceiveEvent handle");
|
|
LEAVE_DPLGAMENODE();
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_OUTOFMEMORY;
|
|
}
|
|
|
|
if(!lpgn->hDupReceiveEvent)
|
|
bNewEvent = TRUE;
|
|
lpgn->hDupReceiveEvent = hDupReceiveEvent;
|
|
|
|
// Check to see if the Receive thread already exists. If it
|
|
// doesn't, create it. Otherwise, leave it alone.
|
|
if(!(lpgn->hReceiveThread))
|
|
{
|
|
hr = PRV_StartReceiveThread(lpgn);
|
|
if(FAILED(hr))
|
|
{
|
|
if(lpgn->hDupReceiveEvent)
|
|
{
|
|
CloseHandle(lpgn->hDupReceiveEvent);
|
|
lpgn->hDupReceiveEvent = NULL;
|
|
}
|
|
|
|
LEAVE_DPLGAMENODE();
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// If this is a new event, check to see if there are any messages in the
|
|
// queue. If there are, kick the event so the user knows they are there.
|
|
if(bNewEvent && lpgn->dwMessageCount)
|
|
SetEvent(hDupReceiveEvent);
|
|
|
|
LEAVE_DPLGAMENODE();
|
|
LEAVE_DPLOBBY();
|
|
return DP_OK;
|
|
|
|
} // DPL_SetLobbyMessageEvent
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DPL_SendLobbyMessage"
|
|
HRESULT DPLAPI DPL_SendLobbyMessage(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags,
|
|
DWORD dwGameID, LPVOID lpData, DWORD dwSize)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this;
|
|
LPDPLOBBYI_GAMENODE lpgn = NULL;
|
|
LPDPLMSG_GENERIC lpmsg = NULL;
|
|
HRESULT hr = DP_OK;
|
|
BOOL bLobbyClient = TRUE;
|
|
BOOL bStandard = FALSE;
|
|
|
|
|
|
DPF(7, "Entering DPL_SendLobbyMessage");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu",
|
|
lpDPL, dwFlags, dwGameID, lpData, dwSize);
|
|
|
|
ENTER_DPLOBBY();
|
|
|
|
TRY
|
|
{
|
|
if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDINTERFACE;
|
|
}
|
|
|
|
this = DPLOBJECT_FROM_INTERFACE(lpDPL);
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
|
|
if( !VALID_READ_PTR( lpData, dwSize ) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// Check for valid flags
|
|
if( !VALID_SENDLOBBYMESSAGE_FLAGS(dwFlags))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDFLAGS;
|
|
}
|
|
|
|
// If it's of the system message format, validate the dwType
|
|
if( dwFlags & DPLMSG_STANDARD )
|
|
{
|
|
// Mark this as a standard message
|
|
bStandard = TRUE;
|
|
|
|
// Make sure the message is big enough to read
|
|
if(! VALID_READ_PTR( lpData, sizeof(DPLMSG_GENERIC)) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR("Invalid message buffer");
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// Make sure it's one we support
|
|
lpmsg = (LPDPLMSG_GENERIC)lpData;
|
|
switch(lpmsg->dwType)
|
|
{
|
|
case DPLSYS_GETPROPERTY:
|
|
case DPLSYS_SETPROPERTY:
|
|
break;
|
|
default:
|
|
DPF_ERR("The dwType of the message is invalid for a legal standard lobby message");
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// If a GameID was passed in, use it to find the correct GameNode. If
|
|
// one wasn't passed in, assume we are the game and use our ProcessID.
|
|
if(!dwGameID)
|
|
{
|
|
dwGameID = GetCurrentProcessId();
|
|
bLobbyClient = FALSE;
|
|
}
|
|
|
|
// Now find the correct game node. If we don't find it, assume we
|
|
// have an invalid ID and error out.
|
|
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
|
|
if(!lpgn)
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR("Invalid dwGameID");
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// If we are self-lobbied, we need to send the message onto the lobby
|
|
// using the IDP3 interface that we are communicating with the lobby on
|
|
// If not, we need to put it in the shared buffer and let the lobby
|
|
// client deal with it.
|
|
if(lpgn->dwFlags & GN_SELF_LOBBIED)
|
|
{
|
|
// Drop the lobby lock so we can call PRV_Send
|
|
LEAVE_DPLOBBY();
|
|
|
|
// Foward the message
|
|
hr = PRV_ForwardMessageToLobbyServer(lpgn, lpData, dwSize, bStandard);
|
|
|
|
// Take the lock back
|
|
ENTER_DPLOBBY();
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Unable to send lobby system message, hr = 0x%08x", hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Write the data to our shared memory
|
|
hr = PRV_WriteClientData(lpgn, dwFlags, lpData, dwSize);
|
|
}
|
|
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
|
|
} // DPL_SendLobbyMessage
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_GetMessageFromQueue"
|
|
HRESULT PRV_GetMessageFromQueue(LPDPLOBBYI_GAMENODE lpgn, LPDWORD lpdwFlags,
|
|
LPVOID lpData, LPDWORD lpdwSize)
|
|
{
|
|
LPDPLOBBYI_MESSAGE lpm;
|
|
|
|
|
|
DPF(7, "Entering PRV_GetMessageFromQueue");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
|
lpgn, lpdwFlags, lpData, lpdwSize);
|
|
|
|
ENTER_DPLQUEUE();
|
|
|
|
// Get the top message in the queue
|
|
lpm = lpgn->MessageHead.lpNext;
|
|
|
|
// Make sure we have a message
|
|
if((!lpgn->dwMessageCount) || (lpm == &lpgn->MessageHead))
|
|
{
|
|
LEAVE_DPLQUEUE();
|
|
return DPERR_NOMESSAGES;
|
|
}
|
|
|
|
// If the lpData pointer is NULL, just return the size
|
|
if(!lpData)
|
|
{
|
|
*lpdwSize = lpm->dwSize;
|
|
LEAVE_DPLQUEUE();
|
|
return DPERR_BUFFERTOOSMALL;
|
|
}
|
|
|
|
// Otherwise, check the remaining output parameters
|
|
if( !VALIDEX_CODE_PTR( lpData ) )
|
|
{
|
|
LEAVE_DPLQUEUE();
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
if( !VALID_DWORD_PTR( lpdwFlags ) )
|
|
{
|
|
LEAVE_DPLQUEUE();
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// Copy the message
|
|
if(*lpdwSize < lpm->dwSize)
|
|
{
|
|
*lpdwSize = lpm->dwSize;
|
|
LEAVE_DPLQUEUE();
|
|
return DPERR_BUFFERTOOSMALL;
|
|
}
|
|
else
|
|
memcpy(lpData, lpm->lpData, lpm->dwSize);
|
|
|
|
// Set the other output parameters
|
|
*lpdwSize = lpm->dwSize;
|
|
*lpdwFlags = lpm->dwFlags;
|
|
|
|
|
|
// Delete the message from the queue & decrement the count
|
|
PRV_RemoveNodeFromQueue(lpgn, lpm);
|
|
|
|
// Check and see if our GAMENODE is dead. If it is, and if the message
|
|
// count has gone to zero, then free the GAMENODE structure.
|
|
if((!lpgn->dwMessageCount) && IS_GAME_DEAD(lpgn))
|
|
PRV_RemoveGameNodeFromList(lpgn);
|
|
|
|
LEAVE_DPLQUEUE();
|
|
return DP_OK;
|
|
|
|
} // PRV_GetMessageFromQueue
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DPL_ReceiveLobbyMessage"
|
|
HRESULT DPLAPI DPL_ReceiveLobbyMessage(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags,
|
|
DWORD dwGameID, LPDWORD lpdwMessageFlags, LPVOID lpData,
|
|
LPDWORD lpdwDataLength)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this;
|
|
LPDPLOBBYI_GAMENODE lpgn = NULL;
|
|
HRESULT hr = DP_OK;
|
|
BOOL bLobbyClient = TRUE;
|
|
|
|
|
|
DPF(7, "Entering DPL_ReceiveLobbyMessage");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
|
lpDPL, dwFlags, dwGameID, lpdwMessageFlags, lpData, lpdwDataLength);
|
|
|
|
ENTER_DPLOBBY();
|
|
|
|
TRY
|
|
{
|
|
if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDINTERFACE;
|
|
}
|
|
|
|
this = DPLOBJECT_FROM_INTERFACE(lpDPL);
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
|
|
if( !VALID_DWORD_PTR( lpdwDataLength ) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// We haven't defined any flags for this release
|
|
if( (dwFlags) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDFLAGS;
|
|
}
|
|
}
|
|
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// If a GameID was passed in, use it to find the correct GameNode. If
|
|
// one wasn't passed in, assume we are the game and use our ProcessID.
|
|
if(!dwGameID)
|
|
{
|
|
dwGameID = GetCurrentProcessId();
|
|
bLobbyClient = FALSE;
|
|
}
|
|
|
|
// Now find the correct game node. If we don't find it, assume we
|
|
// have an invalid ID and error out.
|
|
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
|
|
if(!lpgn)
|
|
{
|
|
DPF_ERR("Invalid dwGameID");
|
|
hr = DPERR_INVALIDPARAMS;
|
|
goto EXIT_RECEIVE_LOBBY_MESSAGE;
|
|
}
|
|
|
|
// Read the data from shared memory
|
|
hr = PRV_GetMessageFromQueue(lpgn, lpdwMessageFlags, lpData, lpdwDataLength);
|
|
|
|
// REVIEW!!!! -- Do we need to send this to the lobby server as part of this API????
|
|
|
|
EXIT_RECEIVE_LOBBY_MESSAGE:
|
|
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
|
|
} // DPL_ReceiveLobbyMessage
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_WriteConnectionSettings"
|
|
HRESULT PRV_WriteConnectionSettings(LPDPLOBBYI_GAMENODE lpgn,
|
|
LPDPLCONNECTION lpConn, BOOL bOverrideWaitMode)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwSize;
|
|
BOOL bGameCreate = FALSE;
|
|
LPBYTE lpConnBuffer = NULL;
|
|
LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
|
|
|
|
|
|
DPF(7, "Entering PRV_WriteConnectionSettings");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, %lu",
|
|
lpgn, lpConn, bOverrideWaitMode);
|
|
|
|
ENTER_DPLGAMENODE();
|
|
|
|
// Make sure we have a valid shared memory buffer
|
|
// Note: Take the GameNode lock so that nobody changes the flags
|
|
// for the buffers, or the buffers themselves out from under us.
|
|
if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
|
|
{
|
|
// HACK!!!! -- SetConnectionSettings may get called from the game
|
|
// without having been lobbied. If that is the case, we need to
|
|
// create the shared memory with the game's process ID (this process)
|
|
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
|
|
{
|
|
// Fake the setup routine by setting the lobby client flag
|
|
lpgn->dwFlags |= GN_LOBBY_CLIENT;
|
|
|
|
// Set our flag
|
|
bGameCreate = TRUE;
|
|
}
|
|
|
|
hr = PRV_SetupAllSharedMemory(lpgn);
|
|
|
|
// HACK!!!! -- Reset the settings we changed to fake the setup routines
|
|
if(bGameCreate)
|
|
{
|
|
lpgn->dwFlags &= (~GN_LOBBY_CLIENT);
|
|
}
|
|
|
|
// Now handle the failure
|
|
if(FAILED(hr))
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
DPF(2, "Unable to access Connection Settings memory");
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// If the ConnectionSettings come from a StartSession message, we need to
|
|
// pick the dplay object pointer out of the DPLCONNECTION structure's
|
|
// reserved field. This pointer to a dplay object represents the object
|
|
// that has a connection to the lobby server.
|
|
if(lpConn->lpSessionDesc->dwReserved1)
|
|
{
|
|
// Save the pointer and player ID in our gamenode structure
|
|
lpgn->lpDPlayObject = (LPDPLAYI_DPLAY)lpConn->lpSessionDesc->dwReserved1;
|
|
lpgn->dpidPlayer = (DWORD)lpConn->lpSessionDesc->dwReserved2;
|
|
|
|
// Clear the field
|
|
lpConn->lpSessionDesc->dwReserved1 = 0L;
|
|
lpConn->lpSessionDesc->dwReserved2 = 0L;
|
|
}
|
|
|
|
// Save the instance pointer for the system messages
|
|
lpgn->guidInstance = lpConn->lpSessionDesc->guidInstance;
|
|
|
|
// Get the packaged size of the DPLCONNECTION structure
|
|
PRV_GetDPLCONNECTIONPackageSize(lpConn, &dwSize, NULL);
|
|
|
|
// Check data sizes
|
|
if(dwSize > (MAX_APPDATABUFFERSIZE - APPDATA_RESERVEDSIZE))
|
|
{
|
|
DPF(2, "Packaged Connection Settings exceeded max buffer size of %d",
|
|
(MAX_APPDATABUFFERSIZE - APPDATA_RESERVEDSIZE));
|
|
LEAVE_DPLGAMENODE();
|
|
return DPERR_BUFFERTOOLARGE;
|
|
}
|
|
|
|
// Make sure we have the mutex for the shared conn settings buffer
|
|
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
|
|
|
|
// Look at the control block to see if we are in wait mode
|
|
// If we are, and this is not a call from RunApplication, then
|
|
// we don't want to write the connection settings
|
|
hr = DPERR_UNAVAILABLE; // Default set to error
|
|
lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
|
|
if((!(lpConnControl->dwFlags & BC_WAIT_MODE)) || bOverrideWaitMode)
|
|
{
|
|
// Get a pointer to the actual buffer
|
|
lpConnBuffer = (LPBYTE)lpConnControl + sizeof(DPLOBBYI_CONNCONTROL);
|
|
|
|
// Package the connection settings into the buffer
|
|
hr = PRV_PackageDPLCONNECTION(lpConn, lpConnBuffer, TRUE);
|
|
|
|
// If it succeeded, and we were overriding wait mode, we need
|
|
// to take the buffers out of wait mode and send the new connection
|
|
// settings available message
|
|
if(SUCCEEDED(hr) && bOverrideWaitMode)
|
|
{
|
|
// Take the buffers out of wait mode
|
|
PRV_LeaveConnSettingsWaitMode(lpgn);
|
|
}
|
|
}
|
|
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
|
|
LEAVE_DPLGAMENODE();
|
|
return hr;
|
|
|
|
} // PRV_WriteConnectionSettings
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_SetConnectionSettings"
|
|
HRESULT PRV_SetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags,
|
|
DWORD dwGameID, LPDPLCONNECTION lpConn)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this;
|
|
LPDPLOBBYI_GAMENODE lpgn = NULL;
|
|
HRESULT hr;
|
|
BOOL bLobbyClient = TRUE;
|
|
|
|
|
|
DPF(7, "Entering PRV_SetConnectionSettings");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
|
lpDPL, dwFlags, dwGameID, lpConn);
|
|
|
|
TRY
|
|
{
|
|
if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
|
|
{
|
|
return DPERR_INVALIDINTERFACE;
|
|
}
|
|
|
|
this = DPLOBJECT_FROM_INTERFACE(lpDPL);
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
|
|
// Validate the DPLCONNECTION structure
|
|
hr = PRV_ValidateDPLCONNECTION(lpConn, FALSE);
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// We haven't defined any flags for this release
|
|
if( (dwFlags) )
|
|
{
|
|
return DPERR_INVALIDFLAGS;
|
|
}
|
|
}
|
|
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// If dwGameID is zero, we assume we are a game. In that case, the
|
|
// GameNode we are looking for should have our ProcessID.
|
|
if(!dwGameID)
|
|
{
|
|
dwGameID = GetCurrentProcessId();
|
|
bLobbyClient = FALSE;
|
|
}
|
|
|
|
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
|
|
if(!lpgn)
|
|
{
|
|
// If we are a game, go ahead and create the node
|
|
if(!bLobbyClient)
|
|
{
|
|
hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
return DPERR_INVALIDPARAMS;
|
|
|
|
}
|
|
|
|
// If the ConnectionSettings are from a StartSession message (lobby launched),
|
|
// we need to set the flag saying we are self-lobbied
|
|
if(lpConn->lpSessionDesc->dwReserved1)
|
|
{
|
|
// Set the flag that says we were lobby client launched
|
|
lpgn->dwFlags |= GN_SELF_LOBBIED;
|
|
}
|
|
|
|
// Write the connection settings to our shared buffer
|
|
hr = PRV_WriteConnectionSettings(lpgn, lpConn, FALSE);
|
|
|
|
return hr;
|
|
|
|
} // PRV_SetConnectionSettings
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DPL_SetConnectionSettings"
|
|
HRESULT DPLAPI DPL_SetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL,
|
|
DWORD dwFlags, DWORD dwGameID, LPDPLCONNECTION lpConn)
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
DPF(7, "Entering DPL_SetConnectionSettings");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
|
lpDPL, dwFlags, dwGameID, lpConn);
|
|
|
|
ENTER_DPLOBBY();
|
|
|
|
// Set the ANSI flag to TRUE and call the internal function
|
|
hr = PRV_SetConnectionSettings(lpDPL, dwFlags, dwGameID, lpConn);
|
|
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
|
|
} // DPL_SetConnectionSettings
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_ReadConnectionSettings"
|
|
HRESULT PRV_ReadConnectionSettings(LPDPLOBBYI_GAMENODE lpgn, LPVOID lpData,
|
|
LPDWORD lpdwSize, BOOL bAnsi)
|
|
{
|
|
HRESULT hr = DP_OK;
|
|
LPDWORD lpdwBuffer;
|
|
LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
|
|
LPBYTE lpConnBuffer = NULL;
|
|
DWORD dwSize = 0,
|
|
dwSizeAnsi,
|
|
dwSizeUnicode;
|
|
|
|
|
|
DPF(7, "Entering PRV_ReadConnectionSettings");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu",
|
|
lpgn, lpData, lpdwSize, bAnsi);
|
|
|
|
// Make sure we have a valid memory pointer
|
|
// Note: Take the GameNode lock so that nobody changes the flags
|
|
// for the buffers, or the buffers themselves out from under us.
|
|
ENTER_DPLGAMENODE();
|
|
if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
|
|
{
|
|
hr = PRV_SetupAllSharedMemory(lpgn);
|
|
if(FAILED(hr))
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
DPF(5, "Unable to access Connect Data memory");
|
|
return DPERR_NOTLOBBIED;
|
|
}
|
|
}
|
|
|
|
// Grab the shared buffer mutex
|
|
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
|
|
|
|
// Make sure we are not in wait mode without being in pending mode
|
|
lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
|
|
if((lpConnControl->dwFlags & BC_WAIT_MODE) &&
|
|
!(lpConnControl->dwFlags & BC_PENDING_CONNECT))
|
|
{
|
|
hr = DPERR_UNAVAILABLE;
|
|
goto EXIT_READ_CONN_SETTINGS;
|
|
}
|
|
|
|
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)){
|
|
lpgn->dwLobbyClientProcessID = lpConnControl->CliProcId;
|
|
lpgn->hLobbyClientProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE ,FALSE, lpgn->dwLobbyClientProcessID);
|
|
|
|
if(!lpgn->hLobbyClientProcess){
|
|
#ifdef DEBUG
|
|
DWORD err;
|
|
err = GetLastError();
|
|
DPF(0,"Couldn't get lobby client processId %d, extended error %d\n",lpConnControl->CliProcId,err);
|
|
#endif
|
|
// lobby client is already dead, don't allow settings across.
|
|
hr = DPERR_UNAVAILABLE;
|
|
goto EXIT_READ_CONN_SETTINGS;
|
|
}
|
|
}
|
|
|
|
// Take us out of wait mode and pending mode
|
|
PRV_LeaveConnSettingsWaitMode(lpgn);
|
|
|
|
// Verify that the buffer is big enough. If it's not, OR if the lpData
|
|
// buffer pointer is NULL, just set the lpdwSize parameter to the
|
|
// correct size and return an error. Note: In our packed structure, the
|
|
// first DWORD is the size of the packed structure with Unicode strings
|
|
// and the second DWORD is the size of the packed structure with ANSI.
|
|
lpConnBuffer = (LPBYTE)lpConnControl + sizeof(DPLOBBYI_CONNCONTROL);
|
|
lpdwBuffer = (LPDWORD)lpConnBuffer;
|
|
dwSizeUnicode = *lpdwBuffer++;
|
|
dwSizeAnsi = *lpdwBuffer;
|
|
dwSize = (bAnsi) ? dwSizeAnsi : dwSizeUnicode;
|
|
|
|
if(dwSize > MAX_APPDATABUFFERSIZE-sizeof(DPLOBBYI_CONNCONTROL)){
|
|
DPF(4,"SECURITY WARN: illegal size in settings shared memory buffer");
|
|
hr=DPERR_NOTLOBBIED;
|
|
goto EXIT_READ_CONN_SETTINGS;
|
|
}
|
|
|
|
if(((*lpdwSize) < dwSize) || (!lpData))
|
|
{
|
|
if(bAnsi)
|
|
*lpdwSize = dwSizeAnsi;
|
|
else
|
|
*lpdwSize = dwSizeUnicode;
|
|
|
|
hr = DPERR_BUFFERTOOSMALL;
|
|
goto EXIT_READ_CONN_SETTINGS;
|
|
}
|
|
|
|
// Copy the DPLCONNECTION structure, taking the ANSI conversion
|
|
// into account if necessary.
|
|
if(bAnsi)
|
|
hr = PRV_UnpackageDPLCONNECTIONAnsi(lpData, lpConnBuffer);
|
|
else
|
|
hr = PRV_UnpackageDPLCONNECTIONUnicode(lpData, lpConnBuffer);
|
|
|
|
// If we haven't yet saved off the Instance guid for the game, save
|
|
// it now so that we have it for the system messages
|
|
if(IsEqualGUID(&lpgn->guidInstance, &GUID_NULL))
|
|
lpgn->guidInstance = ((LPDPLCONNECTION)lpData)->lpSessionDesc->guidInstance;
|
|
|
|
// Fall through
|
|
|
|
EXIT_READ_CONN_SETTINGS:
|
|
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
LEAVE_DPLGAMENODE();
|
|
return hr;
|
|
|
|
} // PRV_ReadConnectionSettings
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_GetConnectionSettings"
|
|
HRESULT PRV_GetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwGameID,
|
|
LPVOID lpData, LPDWORD lpdwSize, BOOL bAnsi)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this;
|
|
LPDPLOBBYI_GAMENODE lpgn = NULL;
|
|
HRESULT hr;
|
|
BOOL bLobbyClient = TRUE;
|
|
|
|
|
|
DPF(7, "Entering PRV_GetConnectionSettings");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu",
|
|
lpDPL, dwGameID, lpData, lpdwSize, bAnsi);
|
|
|
|
TRY
|
|
{
|
|
if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
|
|
{
|
|
return DPERR_INVALIDINTERFACE;
|
|
}
|
|
|
|
this = DPLOBJECT_FROM_INTERFACE(lpDPL);
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
|
|
if( !VALID_DWORD_PTR( lpdwSize ) )
|
|
{
|
|
DPF_ERR("lpdwSize was not a valid dword pointer!");
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
if(lpData)
|
|
{
|
|
if( !VALID_WRITE_PTR(lpData, *lpdwSize) )
|
|
{
|
|
DPF_ERR("lpData is not a valid output buffer of the size specified in *lpdwSize");
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// If dwGameID is zero, we assume we are a game. In that case, the
|
|
// GameNode we are looking for should have our ProcessID.
|
|
if(!dwGameID)
|
|
{
|
|
dwGameID = GetCurrentProcessId();
|
|
bLobbyClient = FALSE;
|
|
}
|
|
|
|
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
|
|
if(!lpgn)
|
|
{
|
|
// If we are a game, go ahead and create the node
|
|
if(!bLobbyClient)
|
|
{
|
|
hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// Read the data from our shared memory
|
|
hr = PRV_ReadConnectionSettings(lpgn, lpData, lpdwSize, bAnsi);
|
|
|
|
return hr;
|
|
|
|
} // PRV_GetConnectionSettings
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DPL_GetConnectionSettings"
|
|
HRESULT DPLAPI DPL_GetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL,
|
|
DWORD dwGameID, LPVOID lpData, LPDWORD lpdwSize)
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
DPF(7, "Entering DPL_GetConnectionSettings");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
|
lpDPL, dwGameID, lpData, lpdwSize);
|
|
|
|
ENTER_DPLOBBY();
|
|
|
|
// Set the ANSI flag to TRUE and call the internal function
|
|
hr = PRV_GetConnectionSettings(lpDPL, dwGameID, lpData,
|
|
lpdwSize, FALSE);
|
|
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
|
|
} // DPL_GetConnectionSettings
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_RemoveGameNodeFromList"
|
|
void PRV_RemoveGameNodeFromList(LPDPLOBBYI_GAMENODE lpgn)
|
|
{
|
|
LPDPLOBBYI_GAMENODE lpgnTemp;
|
|
BOOL bFound = FALSE;
|
|
|
|
|
|
DPF(7, "Entering PRV_RemoveGameNodeFromList");
|
|
DPF(9, "Parameters: 0x%08x", lpgn);
|
|
|
|
// Get the head pointer
|
|
lpgnTemp = lpgn->this->lpgnHead;
|
|
|
|
// Make sure it's not the first node. If it is, move the head pointer
|
|
if(lpgnTemp == lpgn)
|
|
{
|
|
lpgn->this->lpgnHead = lpgn->lpgnNext;
|
|
PRV_FreeGameNode(lpgn);
|
|
return;
|
|
}
|
|
|
|
// Walk the list looking for the previous node
|
|
while(lpgnTemp)
|
|
{
|
|
if(lpgnTemp->lpgnNext == lpgn)
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
lpgnTemp = lpgnTemp->lpgnNext;
|
|
}
|
|
|
|
if(!bFound)
|
|
{
|
|
DPF_ERR("Unable to remove GameNode from list!");
|
|
return;
|
|
}
|
|
|
|
// We've now got it's previous one, so remove it from the linked list
|
|
// and delete it.
|
|
lpgnTemp->lpgnNext = lpgn->lpgnNext;
|
|
PRV_FreeGameNode(lpgn);
|
|
|
|
return;
|
|
|
|
} // PRV_RemoveGameNodeFromList
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PRV_ClientTerminateNotification"
|
|
DWORD WINAPI PRV_ClientTerminateNotification(LPVOID lpParam)
|
|
{
|
|
LPDPLOBBYI_GAMENODE lpgn = (LPDPLOBBYI_GAMENODE)lpParam;
|
|
DPLMSG_SYSTEMMESSAGE msg;
|
|
HANDLE hObjects[3];
|
|
HRESULT hr;
|
|
DWORD dwResult;
|
|
DWORD dwError;
|
|
|
|
|
|
DPF(7, "Entering PRV_ClientTerminateNotification");
|
|
DPF(9, "Parameters: 0x%08x", lpParam);
|
|
|
|
// Setup the objects to wait on -- one process handle, one kill event
|
|
hObjects[0] = lpgn->hGameProcess;
|
|
hObjects[1] = lpgn->hKillTermThreadEvent;
|
|
// This extra handle is here because of a Windows 95 bug. Windows
|
|
// will occasionally miss when it walks the handle table, causing
|
|
// my thread to wait on the wrong handles. By putting a guaranteed
|
|
// invalid handle at the end of our array, the kernel will do a
|
|
// forced re-walk of the handle table and find the correct handles.
|
|
hObjects[2] = INVALID_HANDLE_VALUE;
|
|
|
|
// Wait for the event notification
|
|
while(1)
|
|
{
|
|
// Wait for the process to go away
|
|
dwResult = WaitForMultipleObjects(2, (HANDLE *)hObjects,
|
|
FALSE, INFINITE);
|
|
|
|
// If we are signalled by anything but the process going away,
|
|
// just kill the thread.
|
|
if(dwResult != WAIT_OBJECT_0)
|
|
{
|
|
if(dwResult == WAIT_FAILED)
|
|
{
|
|
// This is a Windows 95 bug -- We may have gotten
|
|
// kicked for no reason. If that was the case, we
|
|
// still have valid handles (we think), the OS
|
|
// just goofed up. So, validate the handle and if
|
|
// they are valid, just return to waiting. See
|
|
// bug #3340 for a better explanation.
|
|
dwError = GetLastError();
|
|
if(ERROR_INVALID_HANDLE == dwError)
|
|
{
|
|
DPF(1, "Wait for client termination failed due to invalid handle.");
|
|
if(!OS_IsValidHandle(hObjects[0]))
|
|
break;
|
|
if(!OS_IsValidHandle(hObjects[1]))
|
|
break;
|
|
continue;
|
|
}
|
|
DPF(0, "Wait for client termination failed (err = %u)!", dwError);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// This is something we don't understand, so just go away.
|
|
DPF(1, "Exiting thread (result = %u).", dwResult);
|
|
ExitThread(0L);
|
|
return 0L;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is our process handle going away, so bail out of
|
|
// the wait loop and send the system message.
|
|
DPF(2, "Client terminated.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Send the system message which says the app terminated
|
|
memset(&msg, 0, sizeof(DPLMSG_SYSTEMMESSAGE));
|
|
msg.dwType = DPLSYS_APPTERMINATED;
|
|
msg.guidInstance = lpgn->guidInstance;
|
|
hr = PRV_InjectMessageInQueue(lpgn, DPLAD_SYSTEM, &msg,
|
|
sizeof(DPLMSG_SYSTEMMESSAGE), TRUE);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF(0, "Failed to send App Termination message, hr = 0x%08x", hr);
|
|
}
|
|
|
|
// Mark the GAMENODE as dead, but don't remove it since we know
|
|
// there are still messages in the queue.
|
|
lpgn->dwFlags |= GN_DEAD_GAME_NODE;
|
|
|
|
ExitThread(0L);
|
|
|
|
return 0L; // avoid warning.
|
|
} // PRV_ClientTerminateNotification
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DPL_WaitForConnectionSettings"
|
|
HRESULT DPLAPI DPL_WaitForConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags)
|
|
{
|
|
LPDPLOBBYI_DPLOBJECT this;
|
|
LPDPLOBBYI_GAMENODE lpgn = NULL;
|
|
LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
|
|
LPDPLOBBYI_BUFFERCONTROL lpBuffControl = NULL;
|
|
HRESULT hr = DP_OK;
|
|
BOOL bCreated = FALSE;
|
|
DWORD dwProcessID;
|
|
BOOL bGameCreate = FALSE;
|
|
BOOL bMessages = TRUE;
|
|
|
|
|
|
DPF(7, "Entering DPL_WaitForConnectionSettings");
|
|
DPF(9, "Parameters: 0x%08x, 0x%08x", lpDPL, dwFlags);
|
|
|
|
ENTER_DPLOBBY();
|
|
|
|
TRY
|
|
{
|
|
if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDINTERFACE;
|
|
}
|
|
|
|
this = DPLOBJECT_FROM_INTERFACE(lpDPL);
|
|
if( !VALID_DPLOBBY_PTR( this ) )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDOBJECT;
|
|
}
|
|
|
|
if(!VALID_WAIT_FLAGS(dwFlags))
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
return DPERR_INVALIDFLAGS;
|
|
}
|
|
}
|
|
|
|
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
LEAVE_DPLOBBY();
|
|
DPF_ERR( "Exception encountered validating parameters" );
|
|
return DPERR_INVALIDPARAMS;
|
|
}
|
|
|
|
|
|
// Get the game node
|
|
dwProcessID = GetCurrentProcessId();
|
|
lpgn = PRV_GetGameNode(this->lpgnHead, dwProcessID);
|
|
if(!lpgn)
|
|
{
|
|
// Create the game node
|
|
hr = PRV_AddNewGameNode(this, &lpgn, dwProcessID, NULL, FALSE, NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
DPF_ERRVAL("Failed creating game node, hr = 0x%08x", hr);
|
|
goto EXIT_WAIT_FOR_CONN_SETTINGS;
|
|
}
|
|
|
|
// Set our flag saying we just created the game node
|
|
bCreated = TRUE;
|
|
}
|
|
|
|
// when doing a wait for connection settings, we do NOT use the
|
|
// IPC_GUID, this is because the lobby launching us may not have
|
|
// provided the GUID.
|
|
lpgn->dwFlags &= ~(GN_IPCGUID_SET);
|
|
|
|
// Make sure we have a valid memory pointer
|
|
// Note: Take the GameNode lock so that nobody changes the flags
|
|
// for the buffers, or the buffers themselves out from under us.
|
|
ENTER_DPLGAMENODE();
|
|
if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
|
|
{
|
|
// First we need to try to setup access to the buffers assuming
|
|
// they already exist (we were lobby launched). If this doesn't
|
|
// work, then we need to create them.
|
|
hr = PRV_SetupAllSharedMemory(lpgn);
|
|
if(FAILED(hr))
|
|
{
|
|
// We don't have any memory, so set it up
|
|
// HACK!!!! -- WaitForConnectionSettings may get called from the game
|
|
// without having been lobbied. If that is the case, we need to
|
|
// create the shared memory with the game's process ID (this process)
|
|
// so we'll set the lobby client flag to fake out the creation
|
|
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
|
|
{
|
|
// Fake the setup routine by setting the lobby client flag
|
|
lpgn->dwFlags |= GN_LOBBY_CLIENT;
|
|
|
|
// Set our flag
|
|
bGameCreate = TRUE;
|
|
}
|
|
|
|
// Setup the shared buffers
|
|
hr = PRV_SetupAllSharedMemory(lpgn);
|
|
|
|
// HACK!!!! -- Reset the settings we changed to fake the setup routines
|
|
if(bGameCreate)
|
|
{
|
|
lpgn->dwFlags &= (~GN_LOBBY_CLIENT);
|
|
}
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
LEAVE_DPLGAMENODE();
|
|
DPF_ERRVAL("Unable to access Connect Data memory, hr = 0x%08x", hr);
|
|
goto EXIT_WAIT_FOR_CONN_SETTINGS;
|
|
}
|
|
}
|
|
|
|
// Drop the lock
|
|
LEAVE_DPLGAMENODE();
|
|
|
|
// If we are in wait mode, and the caller wants to end it, do so,
|
|
// otherwise, just return success
|
|
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
|
|
lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
|
|
if(lpConnControl->dwFlags & BC_WAIT_MODE)
|
|
{
|
|
if(dwFlags & DPLWAIT_CANCEL)
|
|
{
|
|
// Release Mutex
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
|
|
// Take us out of wait mode
|
|
PRV_LeaveConnSettingsWaitMode(lpgn);
|
|
goto EXIT_WAIT_FOR_CONN_SETTINGS;
|
|
}
|
|
else
|
|
{
|
|
// Release Mutex
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
|
|
// Might as well just return OK since we're already doing it
|
|
DPF_ERR("We're already in wait mode");
|
|
goto EXIT_WAIT_FOR_CONN_SETTINGS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We're not it wait mode, and the caller asked us to turn it off
|
|
if(dwFlags & DPLWAIT_CANCEL)
|
|
{
|
|
// Release Mutex
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
|
|
DPF_ERR("Cannot turn off wait mode - we're not in wait mode");
|
|
hr = DPERR_UNAVAILABLE;
|
|
goto EXIT_WAIT_FOR_CONN_SETTINGS;
|
|
}
|
|
}
|
|
|
|
// Release Mutex
|
|
ReleaseMutex(lpgn->hConnectDataMutex);
|
|
|
|
// See if a lobby client exists on the other side, if it does, we
|
|
// need to tell him we are going into wait mode by sending him an
|
|
// AppTerminated message.
|
|
PRV_SendStandardSystemMessage(lpDPL, DPLSYS_APPTERMINATED, 0);
|
|
|
|
// Go into wait mode
|
|
PRV_EnterConnSettingsWaitMode(lpgn);
|
|
|
|
// Kick the receive thread to empty the buffer (just in case there
|
|
// are any messages in it)
|
|
SetEvent(lpgn->hLobbyWriteEvent);
|
|
|
|
// Spin waiting for the buffer to get emptied
|
|
while(bMessages)
|
|
{
|
|
// Grab the mutex for the lobby write buffer
|
|
WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE);
|
|
lpBuffControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
|
|
|
|
if(!lpBuffControl->dwMessages)
|
|
bMessages = FALSE;
|
|
|
|
// Drop the mutex
|
|
ReleaseMutex(lpgn->hLobbyWriteMutex);
|
|
|
|
if(bMessages)
|
|
{
|
|
// Now sleep to give the receive thread a chance to work
|
|
Sleep(50);
|
|
}
|
|
}
|
|
|
|
// Now clean out the message queue
|
|
PRV_CleanUpQueue(lpgn);
|
|
|
|
// Fall through
|
|
|
|
EXIT_WAIT_FOR_CONN_SETTINGS:
|
|
|
|
LEAVE_DPLOBBY();
|
|
return hr;
|
|
|
|
} // DPL_WaitForConnectionSettings
|
|
|
|
|
|
|