1999-11-04 03:17:03 +01:00
|
|
|
/* dplayx.dll global data implementation.
|
|
|
|
*
|
2000-05-27 00:26:30 +02:00
|
|
|
* Copyright 1999,2000 - Peter Hunnisett
|
1999-11-04 03:17:03 +01:00
|
|
|
*
|
|
|
|
* <presently under construction - contact hunnise@nortelnetworks.com>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* NOTE: Methods that begin with DPLAYX_ are used for dealing with
|
|
|
|
* dplayx.dll data which is accessible from all processes.
|
|
|
|
*/
|
|
|
|
|
2000-02-10 23:15:21 +01:00
|
|
|
#include <stdio.h>
|
1999-11-04 03:17:03 +01:00
|
|
|
#include "debugtools.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winerror.h"
|
|
|
|
|
|
|
|
#include "dplayx_global.h"
|
2000-07-10 15:19:28 +02:00
|
|
|
#include "dplayx_messages.h" /* For CreateMessageReceptionThread only */
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-02-10 23:15:21 +01:00
|
|
|
DEFAULT_DEBUG_CHANNEL(dplay);
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
/* FIXME: Need to do all that fun other dll referencing type of stuff */
|
|
|
|
|
|
|
|
/* Static data for all processes */
|
2000-07-10 15:19:28 +02:00
|
|
|
static LPCSTR lpszDplayxSemaName = "WINE_DPLAYX_SM";
|
1999-11-04 03:17:03 +01:00
|
|
|
static HANDLE hDplayxSema;
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
static LPCSTR lpszDplayxFileMapping = "WINE_DPLAYX_FM";
|
|
|
|
static HANDLE hDplayxSharedMem;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
static LPVOID lpSharedStaticData = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
#define DPLAYX_AquireSemaphore() TRACE( "Waiting for DPLAYX semaphore\n" ); \
|
|
|
|
WaitForSingleObject( hDplayxSema, INFINITE );\
|
|
|
|
TRACE( "Through wait\n" )
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
#define DPLAYX_ReleaseSemaphore() ReleaseSemaphore( hDplayxSema, 1, NULL ); \
|
2000-07-10 15:19:28 +02:00
|
|
|
TRACE( "DPLAYX Semaphore released\n" ) /* FIXME: Is this correct? */
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* HACK for simple global data right now */
|
2000-07-10 15:19:28 +02:00
|
|
|
#define dwStaticSharedSize (128 * 1024) /* FIXME: Way too much */
|
|
|
|
#define dwDynamicSharedSize (128 * 1024) /* FIXME: Enough? */
|
|
|
|
#define dwTotalSharedSize (dwStaticSharedSize + dwDynamicSharedSize)
|
|
|
|
|
|
|
|
|
|
|
|
/* FIXME: Is there no easier way? */
|
|
|
|
|
|
|
|
/* Pretend the entire dynamic area is carved up into 512 byte blocks.
|
|
|
|
* Each block has 4 bytes which are 0 unless used */
|
|
|
|
#define dwBlockSize 512
|
|
|
|
#define dwMaxBlock (dwDynamicSharedSize/dwBlockSize)
|
|
|
|
DWORD dwBlockOn = 0;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
DWORD used;
|
|
|
|
DWORD data[dwBlockSize-sizeof(DWORD)];
|
|
|
|
} DPLAYX_MEM_SLICE;
|
|
|
|
|
|
|
|
DPLAYX_MEM_SLICE* lpMemArea;
|
|
|
|
|
|
|
|
void DPLAYX_PrivHeapFree( LPVOID addr );
|
|
|
|
void DPLAYX_PrivHeapFree( LPVOID addr )
|
|
|
|
{
|
|
|
|
LPVOID lpAddrStart;
|
|
|
|
DWORD dwBlockUsed;
|
|
|
|
|
|
|
|
/* Handle getting passed a NULL */
|
|
|
|
if( addr == NULL )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lpAddrStart = addr - sizeof(DWORD); /* Find block header */
|
|
|
|
dwBlockUsed = ((BYTE*)lpAddrStart - (BYTE*)lpMemArea)/dwBlockSize;
|
|
|
|
|
|
|
|
lpMemArea[ dwBlockUsed ].used = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size );
|
|
|
|
LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size )
|
|
|
|
{
|
|
|
|
LPVOID lpvArea = NULL;
|
|
|
|
UINT uBlockUsed;
|
|
|
|
|
|
|
|
if( size > (dwBlockSize - sizeof(DWORD)) )
|
|
|
|
{
|
|
|
|
FIXME( "Size exceeded. Request of 0x%08lx\n", size );
|
|
|
|
size = dwBlockSize - sizeof(DWORD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find blank area */
|
|
|
|
uBlockUsed = 0;
|
|
|
|
while( ( lpMemArea[ uBlockUsed ].used != 0 ) && ( uBlockUsed <= dwMaxBlock ) ) { uBlockUsed++; }
|
|
|
|
|
|
|
|
if( uBlockUsed <= dwMaxBlock )
|
|
|
|
{
|
|
|
|
/* Set the area used */
|
|
|
|
lpMemArea[ uBlockUsed ].used = 1;
|
|
|
|
lpvArea = &(lpMemArea[ uBlockUsed ].data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR( "No free block found\n" );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( flags & HEAP_ZERO_MEMORY )
|
|
|
|
{
|
|
|
|
ZeroMemory( lpvArea, size );
|
|
|
|
}
|
|
|
|
|
|
|
|
return lpvArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str );
|
|
|
|
LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str )
|
|
|
|
{
|
|
|
|
LPSTR p = DPLAYX_PrivHeapAlloc( flags, strlen(str) + 1 );
|
|
|
|
if(p) {
|
|
|
|
strcpy( p, str );
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str );
|
|
|
|
LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str )
|
|
|
|
{
|
|
|
|
INT len = lstrlenW(str) + 1;
|
|
|
|
LPWSTR p = DPLAYX_PrivHeapAlloc( flags, len * sizeof(WCHAR) );
|
|
|
|
if(p) {
|
|
|
|
lstrcpyW( p, str );
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-05-27 00:26:30 +02:00
|
|
|
enum { numSupportedLobbies = 32, numSupportedSessions = 32 };
|
2000-07-10 15:19:28 +02:00
|
|
|
typedef struct tagDPLAYX_LOBBYDATA
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Points to lpConn + block of contiguous extra memory for dynamic parts
|
|
|
|
* of the struct directly following
|
|
|
|
*/
|
|
|
|
LPDPLCONNECTION lpConn;
|
2000-05-27 00:26:30 +02:00
|
|
|
|
|
|
|
/* Information for dplobby interfaces */
|
1999-11-04 03:17:03 +01:00
|
|
|
DWORD dwAppID;
|
|
|
|
HANDLE hReceiveEvent;
|
1999-11-25 23:04:53 +01:00
|
|
|
DWORD dwAppLaunchedFromID;
|
2000-05-27 00:26:30 +02:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Should this lobby app send messages to creator at important life
|
|
|
|
* stages
|
|
|
|
*/
|
|
|
|
BOOL bInformOnConnect; /* FIXME: Not used yet */
|
|
|
|
BOOL bInformOnSettingRead;
|
|
|
|
BOOL bInformOnAppDeath; /* FIXME: Not used yet */
|
|
|
|
|
|
|
|
BOOL bWaitForConnectionSettings;
|
|
|
|
|
|
|
|
} DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
static DPLAYX_LOBBYDATA* lobbyData = NULL;
|
|
|
|
/* static DPLAYX_LOBBYDATA lobbyData[ numSupportedLobbies ]; */
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
static DPSESSIONDESC2* sessionData = NULL;
|
|
|
|
/* static DPSESSIONDESC2* sessionData[ numSupportedSessions ]; */
|
2000-05-27 00:26:30 +02:00
|
|
|
|
1999-11-04 03:17:03 +01:00
|
|
|
/* Function prototypes */
|
2000-07-10 15:19:28 +02:00
|
|
|
DWORD DPLAYX_SizeOfLobbyDataA( LPDPLCONNECTION lpDplData );
|
|
|
|
DWORD DPLAYX_SizeOfLobbyDataW( LPDPLCONNECTION lpDplData );
|
|
|
|
void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src );
|
|
|
|
void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, LPDPLCONNECTION src );
|
|
|
|
BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppId, LPDPLAYX_LOBBYDATA* dplData );
|
|
|
|
void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData );
|
2000-05-27 00:26:30 +02:00
|
|
|
BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2 lpSessionDest,
|
|
|
|
LPCDPSESSIONDESC2 lpSessionSrc );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* Called to initialize the global data. This will only be used on the
|
|
|
|
* loading of the dll
|
|
|
|
***************************************************************************/
|
2000-07-10 15:19:28 +02:00
|
|
|
BOOL DPLAYX_ConstructData(void)
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
SECURITY_ATTRIBUTES s_attrib;
|
|
|
|
BOOL bInitializeSharedMemory = FALSE;
|
|
|
|
LPVOID lpDesiredMemoryMapStart = (LPVOID)0x50000000;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
TRACE( "DPLAYX dll loaded - construct called\n" );
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Create a semaphore to block access to DPLAYX global data structs */
|
|
|
|
|
|
|
|
s_attrib.bInheritHandle = TRUE;
|
|
|
|
s_attrib.lpSecurityDescriptor = NULL;
|
|
|
|
s_attrib.nLength = sizeof(s_attrib);
|
|
|
|
|
|
|
|
hDplayxSema = CreateSemaphoreA( &s_attrib, 1, 1, lpszDplayxSemaName );
|
|
|
|
|
|
|
|
/* First instance creates the semaphore. Others just use it */
|
|
|
|
if( GetLastError() == ERROR_SUCCESS )
|
|
|
|
{
|
|
|
|
TRACE( "Semaphore %u created\n", hDplayxSema );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* The semaphore creator will also build the shared memory */
|
|
|
|
bInitializeSharedMemory = TRUE;
|
|
|
|
}
|
|
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
TRACE( "Found semaphore handle %u\n", hDplayxSema );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR( ": semaphore error 0x%08lx\n", GetLastError() );
|
|
|
|
return FALSE;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
SetLastError( ERROR_SUCCESS );
|
|
|
|
|
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
|
|
|
hDplayxSharedMem = CreateFileMappingA( INVALID_HANDLE_VALUE,
|
|
|
|
&s_attrib,
|
|
|
|
PAGE_READWRITE | SEC_COMMIT,
|
|
|
|
0,
|
|
|
|
dwTotalSharedSize,
|
|
|
|
lpszDplayxFileMapping );
|
|
|
|
|
|
|
|
if( GetLastError() == ERROR_SUCCESS )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
TRACE( "File mapped %u created\n", hDplayxSharedMem );
|
|
|
|
}
|
|
|
|
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
|
|
|
|
{
|
|
|
|
TRACE( "Found FileMapping handle %u\n", hDplayxSharedMem );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR( ": unable to create shared memory 0x%08lx\n", GetLastError() );
|
|
|
|
return FALSE;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
lpSharedStaticData = MapViewOfFileEx( hDplayxSharedMem,
|
|
|
|
FILE_MAP_WRITE,
|
|
|
|
0, 0, 0, lpDesiredMemoryMapStart );
|
|
|
|
|
|
|
|
if( lpSharedStaticData == NULL )
|
2000-05-27 00:26:30 +02:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
ERR( ": unable to map static data into process memory space: 0x%08lx\n",
|
|
|
|
GetLastError() );
|
|
|
|
return FALSE;
|
2000-05-27 00:26:30 +02:00
|
|
|
}
|
2000-07-10 15:19:28 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if( lpDesiredMemoryMapStart == lpSharedStaticData )
|
|
|
|
{
|
|
|
|
TRACE( "File mapped to %p\n", lpSharedStaticData );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Presently the shared data structures use pointers. If the
|
|
|
|
* files are no mapped into the same area, the pointers will no
|
|
|
|
* longer make any sense :(
|
|
|
|
* FIXME: In the future make the shared data structures have some
|
|
|
|
* sort of fixup to make them independent between data spaces.
|
|
|
|
* This will also require a rework of the session data stuff.
|
|
|
|
*/
|
|
|
|
ERR( "File mapped to %p (not %p). Expect failure\n",
|
|
|
|
lpSharedStaticData, lpDesiredMemoryMapStart );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dynamic area starts just after the static area */
|
|
|
|
lpMemArea = (LPVOID)((BYTE*)lpSharedStaticData + dwStaticSharedSize);
|
|
|
|
|
|
|
|
/* FIXME: Crude hack */
|
|
|
|
lobbyData = (DPLAYX_LOBBYDATA*)lpSharedStaticData;
|
|
|
|
sessionData = (DPSESSIONDESC2*)((BYTE*)lpSharedStaticData + (dwStaticSharedSize/2));
|
|
|
|
|
|
|
|
/* Initialize shared data segments. */
|
|
|
|
if( bInitializeSharedMemory )
|
|
|
|
{
|
|
|
|
UINT i;
|
2000-05-27 00:26:30 +02:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
TRACE( "Initializing shared memory\n" );
|
|
|
|
|
|
|
|
/* Set all lobbies to be "empty" */
|
|
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
|
|
{
|
|
|
|
DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set all sessions to be "empty" */
|
|
|
|
for( i=0; i < numSupportedSessions; i++ )
|
|
|
|
{
|
|
|
|
sessionData[i].dwSize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Zero out the dynmaic area */
|
|
|
|
ZeroMemory( lpMemArea, dwDynamicSharedSize );
|
|
|
|
|
|
|
|
/* Just for fun sync the whole data area */
|
|
|
|
FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize );
|
|
|
|
}
|
|
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
|
|
|
|
return TRUE;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* Called to destroy all global data. This will only be used on the
|
|
|
|
* unloading of the dll
|
|
|
|
***************************************************************************/
|
2000-07-10 15:19:28 +02:00
|
|
|
BOOL DPLAYX_DestructData(void)
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
|
|
|
TRACE( "DPLAYX dll unloaded - destruct called\n" );
|
1999-11-25 23:04:53 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Delete the semaphore */
|
1999-11-25 23:04:53 +01:00
|
|
|
CloseHandle( hDplayxSema );
|
2000-07-10 15:19:28 +02:00
|
|
|
|
|
|
|
/* Delete shared memory file mapping */
|
|
|
|
UnmapViewOfFile( lpSharedStaticData );
|
|
|
|
CloseHandle( hDplayxSharedMem );
|
|
|
|
|
|
|
|
return FALSE;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData )
|
1999-11-25 23:04:53 +01:00
|
|
|
{
|
|
|
|
ZeroMemory( lpData, sizeof( *lpData ) );
|
|
|
|
|
|
|
|
/* Set the handle to a better invalid value */
|
|
|
|
lpData->hReceiveEvent = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
1999-11-04 03:17:03 +01:00
|
|
|
/* NOTE: This must be called with the semaphore aquired.
|
1999-11-25 23:04:53 +01:00
|
|
|
* TRUE/FALSE with a pointer to it's data returned. Pointer data is
|
|
|
|
* is only valid if TRUE is returned.
|
1999-11-04 03:17:03 +01:00
|
|
|
*/
|
2000-07-10 15:19:28 +02:00
|
|
|
BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppID, LPDPLAYX_LOBBYDATA* lplpDplData )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
UINT i;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
*lplpDplData = NULL;
|
|
|
|
|
1999-11-04 03:17:03 +01:00
|
|
|
if( dwAppID == 0 )
|
|
|
|
{
|
|
|
|
dwAppID = GetCurrentProcessId();
|
1999-11-25 23:04:53 +01:00
|
|
|
TRACE( "Translated dwAppID == 0 into 0x%08lx\n", dwAppID );
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
|
|
{
|
|
|
|
if( lobbyData[ i ].dwAppID == dwAppID )
|
|
|
|
{
|
|
|
|
/* This process is lobbied */
|
2000-07-10 15:19:28 +02:00
|
|
|
TRACE( "Found 0x%08lx @ %u\n", dwAppID, i );
|
1999-11-04 03:17:03 +01:00
|
|
|
*lplpDplData = &lobbyData[ i ];
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* Reserve a spot for the new appliction. TRUE means success and FALSE failure. */
|
|
|
|
BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID, HANDLE hReceiveEvent )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
1999-11-25 23:04:53 +01:00
|
|
|
UINT i;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* 0 is the marker for unused application data slots */
|
|
|
|
if( dwAppID == 0 )
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_AquireSemaphore();
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* Find an empty space in the list and insert the data */
|
|
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
|
|
{
|
|
|
|
if( lobbyData[ i ].dwAppID == 0 )
|
|
|
|
{
|
|
|
|
/* This process is now lobbied */
|
2000-07-10 15:19:28 +02:00
|
|
|
TRACE( "Setting lobbyData[%u] for (0x%08lx,%u,0x%08lx)\n",
|
|
|
|
i, dwAppID, hReceiveEvent, GetCurrentProcessId() );
|
|
|
|
|
|
|
|
lobbyData[ i ].dwAppID = dwAppID;
|
|
|
|
lobbyData[ i ].hReceiveEvent = hReceiveEvent;
|
|
|
|
lobbyData[ i ].dwAppLaunchedFromID = GetCurrentProcessId();
|
|
|
|
|
|
|
|
lobbyData[ i ].bInformOnConnect = TRUE;
|
|
|
|
lobbyData[ i ].bInformOnSettingRead = TRUE;
|
|
|
|
lobbyData[ i ].bInformOnAppDeath = TRUE;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
ERR( "No empty lobbies\n" );
|
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return FALSE;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* I'm not sure when I'm going to need this, but here it is */
|
|
|
|
BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
1999-11-25 23:04:53 +01:00
|
|
|
UINT i;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* Find an empty space in the list and insert the data */
|
1999-11-04 03:17:03 +01:00
|
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
|
|
{
|
1999-11-25 23:04:53 +01:00
|
|
|
if( lobbyData[ i ].dwAppID == dwAppID )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
/* FIXME: Should free up anything unused. Tisk tisk :0 */
|
1999-11-25 23:04:53 +01:00
|
|
|
/* Mark this entry unused */
|
2000-07-10 15:19:28 +02:00
|
|
|
TRACE( "Marking lobbyData[%u] unused\n", i );
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
1999-11-25 23:04:53 +01:00
|
|
|
ERR( "Unable to find global entry for application\n" );
|
1999-11-04 03:17:03 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DPLAYX_GetConnectionSettingsA
|
|
|
|
( DWORD dwAppID,
|
|
|
|
LPVOID lpData,
|
2000-07-10 15:19:28 +02:00
|
|
|
LPDWORD lpdwDataSize,
|
|
|
|
LPBOOL lpbSendHaveReadMessage )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
|
|
|
DWORD dwRequiredDataSize = 0;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
2000-05-23 03:16:36 +02:00
|
|
|
|
|
|
|
TRACE( "Application 0x%08lx is not lobbied\n", dwAppID );
|
1999-11-25 23:04:53 +01:00
|
|
|
return DPERR_NOTLOBBIED;
|
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
dwRequiredDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Do they want to know the required buffer size or is the provided buffer
|
|
|
|
* big enough?
|
|
|
|
*/
|
|
|
|
if ( ( lpData == NULL ) ||
|
|
|
|
( *lpdwDataSize < dwRequiredDataSize )
|
|
|
|
)
|
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
*lpdwDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
return DPERR_BUFFERTOOSMALL;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* They have gotten the information */
|
|
|
|
*lpbSendHaveReadMessage = lpDplData->bInformOnSettingRead;
|
|
|
|
lpDplData->bInformOnSettingRead = FALSE;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_CopyConnStructA( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_ReleaseSemaphore();
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
return DP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assumption: Enough contiguous space was allocated at dest */
|
|
|
|
void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src )
|
|
|
|
{
|
|
|
|
BYTE* lpStartOfFreeSpace;
|
|
|
|
|
|
|
|
memcpy( dest, src, sizeof( DPLCONNECTION ) );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION );
|
|
|
|
|
|
|
|
/* Copy the LPDPSESSIONDESC2 structure if it exists */
|
|
|
|
if( src->lpSessionDesc )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
|
|
|
|
memcpy( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Session names may or may not exist */
|
|
|
|
if( src->lpSessionDesc->sess.lpszSessionNameA )
|
|
|
|
{
|
|
|
|
lstrcpyA( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->sess.lpszSessionNameA );
|
|
|
|
dest->lpSessionDesc->sess.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace +=
|
|
|
|
lstrlenA( (LPSTR)dest->lpSessionDesc->sess.lpszSessionName ) + 1;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( src->lpSessionDesc->pass.lpszPasswordA )
|
|
|
|
{
|
|
|
|
lstrcpyA( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->pass.lpszPasswordA );
|
|
|
|
dest->lpSessionDesc->pass.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace +=
|
|
|
|
lstrlenA( (LPSTR)dest->lpSessionDesc->pass.lpszPasswordA ) + 1;
|
|
|
|
}
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* DPNAME structure is optional */
|
|
|
|
if( src->lpPlayerName )
|
|
|
|
{
|
|
|
|
dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof( DPNAME );
|
|
|
|
memcpy( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( src->lpPlayerName->psn.lpszShortNameA )
|
|
|
|
{
|
|
|
|
lstrcpyA( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->psn.lpszShortNameA );
|
|
|
|
dest->lpPlayerName->psn.lpszShortNameA = (LPSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace +=
|
|
|
|
lstrlenA( (LPSTR)dest->lpPlayerName->psn.lpszShortNameA ) + 1;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( src->lpPlayerName->pln.lpszLongNameA )
|
|
|
|
{
|
|
|
|
lstrcpyA( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->pln.lpszLongNameA );
|
|
|
|
dest->lpPlayerName->pln.lpszLongNameA = (LPSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace +=
|
|
|
|
lstrlenA( (LPSTR)dest->lpPlayerName->pln.lpszLongName ) + 1 ;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy address if it exists */
|
|
|
|
if( src->lpAddress )
|
|
|
|
{
|
|
|
|
dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
|
|
|
|
memcpy( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
|
|
|
|
/* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT DPLAYX_GetConnectionSettingsW
|
|
|
|
( DWORD dwAppID,
|
|
|
|
LPVOID lpData,
|
2000-07-10 15:19:28 +02:00
|
|
|
LPDWORD lpdwDataSize,
|
|
|
|
LPBOOL lpbSendHaveReadMessage )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
|
|
|
DWORD dwRequiredDataSize = 0;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return DPERR_NOTLOBBIED;
|
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
dwRequiredDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Do they want to know the required buffer size or is the provided buffer
|
|
|
|
* big enough?
|
|
|
|
*/
|
|
|
|
if ( ( lpData == NULL ) ||
|
|
|
|
( *lpdwDataSize < dwRequiredDataSize )
|
|
|
|
)
|
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
*lpdwDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
return DPERR_BUFFERTOOSMALL;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* They have gotten the information */
|
|
|
|
*lpbSendHaveReadMessage = lpDplData->bInformOnSettingRead;
|
|
|
|
lpDplData->bInformOnSettingRead = FALSE;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_CopyConnStructW( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_ReleaseSemaphore();
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
return DP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assumption: Enough contiguous space was allocated at dest */
|
|
|
|
void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, LPDPLCONNECTION src )
|
|
|
|
{
|
|
|
|
BYTE* lpStartOfFreeSpace;
|
|
|
|
|
|
|
|
memcpy( dest, src, sizeof( DPLCONNECTION ) );
|
|
|
|
|
|
|
|
lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Copy the LPDPSESSIONDESC2 structure if it exists */
|
|
|
|
if( src->lpSessionDesc )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
|
|
|
|
memcpy( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Session names may or may not exist */
|
|
|
|
if( src->lpSessionDesc->sess.lpszSessionName )
|
|
|
|
{
|
|
|
|
lstrcpyW( (LPWSTR)lpStartOfFreeSpace, dest->lpSessionDesc->sess.lpszSessionName );
|
|
|
|
src->lpSessionDesc->sess.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
|
|
( lstrlenW( (LPWSTR)dest->lpSessionDesc->sess.lpszSessionName ) + 1 );
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( src->lpSessionDesc->pass.lpszPassword )
|
|
|
|
{
|
|
|
|
lstrcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->pass.lpszPassword );
|
|
|
|
dest->lpSessionDesc->pass.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
|
|
( lstrlenW( (LPWSTR)dest->lpSessionDesc->pass.lpszPassword ) + 1 );
|
|
|
|
}
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* DPNAME structure is optional */
|
|
|
|
if( src->lpPlayerName )
|
|
|
|
{
|
|
|
|
dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof( DPNAME );
|
|
|
|
memcpy( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
|
|
|
|
|
|
|
|
if( src->lpPlayerName->psn.lpszShortName )
|
|
|
|
{
|
|
|
|
lstrcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->psn.lpszShortName );
|
|
|
|
dest->lpPlayerName->psn.lpszShortName = (LPWSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
|
|
( lstrlenW( (LPWSTR)dest->lpPlayerName->psn.lpszShortName ) + 1 );
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( src->lpPlayerName->pln.lpszLongName )
|
|
|
|
{
|
|
|
|
lstrcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->pln.lpszLongName );
|
|
|
|
dest->lpPlayerName->pln.lpszLongName = (LPWSTR)lpStartOfFreeSpace;
|
|
|
|
lpStartOfFreeSpace += sizeof(WCHAR) *
|
|
|
|
( lstrlenW( (LPWSTR)dest->lpPlayerName->pln.lpszLongName ) + 1 );
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Copy address if it exists */
|
|
|
|
if( src->lpAddress )
|
|
|
|
{
|
|
|
|
dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
|
|
|
|
memcpy( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
|
|
|
|
/* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Store the structure into the shared data structre. Ensure that allocs for
|
|
|
|
* variable length strings come from the shared data structure.
|
|
|
|
* FIXME: We need to free information as well
|
|
|
|
*/
|
1999-11-04 03:17:03 +01:00
|
|
|
HRESULT DPLAYX_SetConnectionSettingsA
|
|
|
|
( DWORD dwFlags,
|
|
|
|
DWORD dwAppID,
|
|
|
|
LPDPLCONNECTION lpConn )
|
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* Paramater check */
|
|
|
|
if( dwFlags || !lpConn )
|
|
|
|
{
|
|
|
|
ERR("invalid parameters.\n");
|
|
|
|
return DPERR_INVALIDPARAMS;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* Store information */
|
|
|
|
if( lpConn->dwSize != sizeof(DPLCONNECTION) )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
1999-11-25 23:04:53 +01:00
|
|
|
ERR(": old/new DPLCONNECTION type? Size=%08lx vs. expected=%ul bytes\n",
|
|
|
|
lpConn->dwSize, sizeof( DPLCONNECTION ) );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
return DPERR_INVALIDPARAMS;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
return DPERR_NOTLOBBIED;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if( (!lpConn->lpSessionDesc ) ||
|
|
|
|
( lpConn->lpSessionDesc->dwSize != sizeof( DPSESSIONDESC2 ) )
|
|
|
|
)
|
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
ERR("DPSESSIONDESC passed in? Size=%lu vs. expected=%u bytes\n",
|
1999-11-04 03:17:03 +01:00
|
|
|
lpConn->lpSessionDesc->dwSize, sizeof( DPSESSIONDESC2 ) );
|
|
|
|
|
|
|
|
return DPERR_INVALIDPARAMS;
|
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Free the existing memory */
|
|
|
|
DPLAYX_PrivHeapFree( lpDplData->lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
|
|
|
|
DPLAYX_SizeOfLobbyDataA( lpConn ) );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_CopyConnStructA( lpDplData->lpConn, lpConn );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_ReleaseSemaphore();
|
1999-11-04 03:17:03 +01:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* FIXME: Send a message - I think */
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
return DP_OK;
|
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Store the structure into the shared data structre. Ensure that allocs for
|
|
|
|
* variable length strings come from the shared data structure.
|
|
|
|
* FIXME: We need to free information as well
|
|
|
|
*/
|
1999-11-04 03:17:03 +01:00
|
|
|
HRESULT DPLAYX_SetConnectionSettingsW
|
|
|
|
( DWORD dwFlags,
|
|
|
|
DWORD dwAppID,
|
|
|
|
LPDPLCONNECTION lpConn )
|
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
LPDPLAYX_LOBBYDATA lpDplData;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
|
|
|
/* Paramater check */
|
|
|
|
if( dwFlags || !lpConn )
|
|
|
|
{
|
|
|
|
ERR("invalid parameters.\n");
|
|
|
|
return DPERR_INVALIDPARAMS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store information */
|
|
|
|
if( lpConn->dwSize != sizeof(DPLCONNECTION) )
|
|
|
|
{
|
1999-11-25 23:04:53 +01:00
|
|
|
ERR(": old/new DPLCONNECTION type? Size=%lu vs. expected=%u bytes\n",
|
1999-11-04 03:17:03 +01:00
|
|
|
lpConn->dwSize, sizeof( DPLCONNECTION ) );
|
|
|
|
|
|
|
|
return DPERR_INVALIDPARAMS;
|
|
|
|
}
|
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
|
|
|
if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
|
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
2000-07-10 15:19:28 +02:00
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
return DPERR_NOTLOBBIED;
|
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Free the existing memory */
|
|
|
|
DPLAYX_PrivHeapFree( lpDplData->lpConn );
|
|
|
|
|
|
|
|
lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
|
|
|
|
DPLAYX_SizeOfLobbyDataW( lpConn ) );
|
|
|
|
|
|
|
|
DPLAYX_CopyConnStructW( lpDplData->lpConn, lpConn );
|
|
|
|
|
|
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
|
|
|
|
/* FIXME: Send a message - I think */
|
|
|
|
|
|
|
|
return DP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD DPLAYX_SizeOfLobbyDataA( LPDPLCONNECTION lpConn )
|
|
|
|
{
|
|
|
|
DWORD dwTotalSize = sizeof( DPLCONNECTION );
|
|
|
|
|
|
|
|
/* Just a safety check */
|
|
|
|
if( lpConn == NULL )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
ERR( "lpConn is NULL\n" );
|
|
|
|
return 0;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpSessionDesc != NULL )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( DPSESSIONDESC2 );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpSessionDesc->sess.lpszSessionNameA )
|
|
|
|
{
|
|
|
|
dwTotalSize += lstrlenA( lpConn->lpSessionDesc->sess.lpszSessionNameA ) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( lpConn->lpSessionDesc->pass.lpszPasswordA )
|
|
|
|
{
|
|
|
|
dwTotalSize += lstrlenA( lpConn->lpSessionDesc->pass.lpszPasswordA ) + 1;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpPlayerName != NULL )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( DPNAME );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpPlayerName->psn.lpszShortNameA )
|
|
|
|
{
|
|
|
|
dwTotalSize += lstrlenA( lpConn->lpPlayerName->psn.lpszShortNameA ) + 1;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpPlayerName->pln.lpszLongNameA )
|
|
|
|
{
|
|
|
|
dwTotalSize += lstrlenA( lpConn->lpPlayerName->pln.lpszLongNameA ) + 1;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
dwTotalSize += lpConn->dwAddressSize;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
return dwTotalSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD DPLAYX_SizeOfLobbyDataW( LPDPLCONNECTION lpConn )
|
|
|
|
{
|
|
|
|
DWORD dwTotalSize = sizeof( DPLCONNECTION );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
/* Just a safety check */
|
|
|
|
if( lpConn == NULL )
|
1999-11-04 03:17:03 +01:00
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
ERR( "lpConn is NULL\n" );
|
|
|
|
return 0;
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpSessionDesc != NULL )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( DPSESSIONDESC2 );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpSessionDesc->sess.lpszSessionName )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
|
|
( lstrlenW( lpConn->lpSessionDesc->sess.lpszSessionName ) + 1 );
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpSessionDesc->pass.lpszPassword )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
|
|
( lstrlenW( lpConn->lpSessionDesc->pass.lpszPassword ) + 1 );
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpPlayerName != NULL )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( DPNAME );
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpPlayerName->psn.lpszShortName )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
|
|
( lstrlenW( lpConn->lpPlayerName->psn.lpszShortName ) + 1 );
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
if( lpConn->lpPlayerName->pln.lpszLongName )
|
|
|
|
{
|
|
|
|
dwTotalSize += sizeof( WCHAR ) *
|
|
|
|
( lstrlenW( lpConn->lpPlayerName->pln.lpszLongName ) + 1 );
|
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
}
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
dwTotalSize += lpConn->dwAddressSize;
|
1999-11-04 03:17:03 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
return dwTotalSize;
|
1999-11-04 03:17:03 +01:00
|
|
|
}
|
1999-11-25 23:04:53 +01:00
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
|
|
|
|
|
2000-05-27 00:26:30 +02:00
|
|
|
LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateSessionDesc2A( LPCDPSESSIONDESC2 lpSessionSrc )
|
|
|
|
{
|
2000-07-10 15:19:28 +02:00
|
|
|
LPDPSESSIONDESC2 lpSessionDest =
|
|
|
|
(LPDPSESSIONDESC2)DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY, sizeof( *lpSessionSrc ) );
|
2000-05-27 00:26:30 +02:00
|
|
|
DPLAYX_CopyIntoSessionDesc2A( lpSessionDest, lpSessionSrc );
|
|
|
|
|
|
|
|
return lpSessionDest;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy an ANSI session desc structure to the given buffer */
|
|
|
|
BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2 lpSessionDest,
|
|
|
|
LPCDPSESSIONDESC2 lpSessionSrc )
|
|
|
|
{
|
|
|
|
memcpy( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
|
|
|
|
|
|
|
|
if( lpSessionSrc->sess.lpszSessionNameA )
|
|
|
|
{
|
|
|
|
lpSessionDest->sess.lpszSessionNameA =
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_strdupA( HEAP_ZERO_MEMORY, lpSessionSrc->sess.lpszSessionNameA );
|
2000-05-27 00:26:30 +02:00
|
|
|
}
|
|
|
|
if( lpSessionSrc->pass.lpszPasswordA )
|
|
|
|
{
|
|
|
|
lpSessionDest->pass.lpszPasswordA =
|
2000-07-10 15:19:28 +02:00
|
|
|
DPLAYX_strdupA( HEAP_ZERO_MEMORY, lpSessionSrc->pass.lpszPasswordA );
|
2000-05-27 00:26:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start the index at 0. index will be updated to equal that which should
|
|
|
|
be passed back into this function for the next element */
|
|
|
|
LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateLocalSession( UINT* index )
|
|
|
|
{
|
|
|
|
for( ; (*index) < numSupportedSessions; (*index)++ )
|
|
|
|
{
|
|
|
|
if( sessionData[(*index)].dwSize != 0 )
|
|
|
|
{
|
|
|
|
return DPLAYX_CopyAndAllocateSessionDesc2A( &sessionData[(*index)++] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No more sessions */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start the index at 0. index will be updated to equal that which should
|
|
|
|
be passed back into this function for the next element */
|
|
|
|
BOOL DPLAYX_CopyLocalSession( UINT* index, LPDPSESSIONDESC2 lpsd )
|
|
|
|
{
|
|
|
|
for( ; (*index) < numSupportedSessions; (*index)++ )
|
|
|
|
{
|
|
|
|
if( sessionData[(*index)].dwSize != 0 )
|
|
|
|
{
|
|
|
|
return DPLAYX_CopyIntoSessionDesc2A( lpsd, &sessionData[(*index)++] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No more sessions */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DPLAYX_SetLocalSession( LPCDPSESSIONDESC2 lpsd )
|
|
|
|
{
|
|
|
|
UINT i;
|
|
|
|
|
|
|
|
/* FIXME: Is this an error if it exists already? */
|
|
|
|
|
|
|
|
/* Crude/wrong implementation for now. Just always add to first empty spot */
|
|
|
|
for( i=0; i < numSupportedSessions; i++ )
|
|
|
|
{
|
|
|
|
/* Is this one empty? */
|
|
|
|
if( sessionData[i].dwSize == 0 )
|
|
|
|
{
|
|
|
|
DPLAYX_CopyIntoSessionDesc2A( &sessionData[i], lpsd );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2000-07-10 15:19:28 +02:00
|
|
|
BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait )
|
|
|
|
{
|
|
|
|
LPDPLAYX_LOBBYDATA lpLobbyData;
|
|
|
|
|
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
|
|
|
if( !DPLAYX_IsAppIdLobbied( 0, &lpLobbyData ) )
|
|
|
|
{
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
lpLobbyData->bWaitForConnectionSettings = bWait;
|
|
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void)
|
|
|
|
{
|
|
|
|
UINT i;
|
|
|
|
BOOL bFound = FALSE;
|
|
|
|
|
|
|
|
DPLAYX_AquireSemaphore();
|
|
|
|
|
|
|
|
for( i=0; i < numSupportedLobbies; i++ )
|
|
|
|
{
|
|
|
|
if( ( lobbyData[ i ].dwAppID != 0 ) && /* lobby initialized */
|
|
|
|
( lobbyData[ i ].bWaitForConnectionSettings ) /* Waiting */
|
|
|
|
)
|
|
|
|
{
|
|
|
|
bFound = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DPLAYX_ReleaseSemaphore();
|
|
|
|
|
|
|
|
return bFound;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-11-25 23:04:53 +01:00
|
|
|
/* NOTE: This is potentially not thread safe. You are not guaranteed to end up
|
|
|
|
with the correct string printed in the case where the HRESULT is not
|
|
|
|
known. You'll just get the last hr passed in printed. This can change
|
|
|
|
over time if this method is used alot :) */
|
|
|
|
LPCSTR DPLAYX_HresultToString(HRESULT hr)
|
|
|
|
{
|
|
|
|
static char szTempStr[12];
|
|
|
|
|
|
|
|
switch (hr)
|
|
|
|
{
|
|
|
|
case DP_OK:
|
|
|
|
return "DP_OK";
|
|
|
|
case DPERR_ALREADYINITIALIZED:
|
|
|
|
return "DPERR_ALREADYINITIALIZED";
|
|
|
|
case DPERR_ACCESSDENIED:
|
|
|
|
return "DPERR_ACCESSDENIED";
|
|
|
|
case DPERR_ACTIVEPLAYERS:
|
|
|
|
return "DPERR_ACTIVEPLAYERS";
|
|
|
|
case DPERR_BUFFERTOOSMALL:
|
|
|
|
return "DPERR_BUFFERTOOSMALL";
|
|
|
|
case DPERR_CANTADDPLAYER:
|
|
|
|
return "DPERR_CANTADDPLAYER";
|
|
|
|
case DPERR_CANTCREATEGROUP:
|
|
|
|
return "DPERR_CANTCREATEGROUP";
|
|
|
|
case DPERR_CANTCREATEPLAYER:
|
|
|
|
return "DPERR_CANTCREATEPLAYER";
|
|
|
|
case DPERR_CANTCREATESESSION:
|
|
|
|
return "DPERR_CANTCREATESESSION";
|
|
|
|
case DPERR_CAPSNOTAVAILABLEYET:
|
|
|
|
return "DPERR_CAPSNOTAVAILABLEYET";
|
|
|
|
case DPERR_EXCEPTION:
|
|
|
|
return "DPERR_EXCEPTION";
|
|
|
|
case DPERR_GENERIC:
|
|
|
|
return "DPERR_GENERIC";
|
|
|
|
case DPERR_INVALIDFLAGS:
|
|
|
|
return "DPERR_INVALIDFLAGS";
|
|
|
|
case DPERR_INVALIDOBJECT:
|
|
|
|
return "DPERR_INVALIDOBJECT";
|
|
|
|
case DPERR_INVALIDPARAMS:
|
|
|
|
return "DPERR_INVALIDPARAMS";
|
|
|
|
case DPERR_INVALIDPLAYER:
|
|
|
|
return "DPERR_INVALIDPLAYER";
|
|
|
|
case DPERR_INVALIDGROUP:
|
|
|
|
return "DPERR_INVALIDGROUP";
|
|
|
|
case DPERR_NOCAPS:
|
|
|
|
return "DPERR_NOCAPS";
|
|
|
|
case DPERR_NOCONNECTION:
|
|
|
|
return "DPERR_NOCONNECTION";
|
|
|
|
case DPERR_OUTOFMEMORY:
|
|
|
|
return "DPERR_OUTOFMEMORY";
|
|
|
|
case DPERR_NOMESSAGES:
|
|
|
|
return "DPERR_NOMESSAGES";
|
|
|
|
case DPERR_NONAMESERVERFOUND:
|
|
|
|
return "DPERR_NONAMESERVERFOUND";
|
|
|
|
case DPERR_NOPLAYERS:
|
|
|
|
return "DPERR_NOPLAYERS";
|
|
|
|
case DPERR_NOSESSIONS:
|
|
|
|
return "DPERR_NOSESSIONS";
|
|
|
|
/* This one isn't defined yet in WINE sources. I don't know the value
|
|
|
|
case DPERR_PENDING:
|
|
|
|
return "DPERR_PENDING";
|
|
|
|
*/
|
|
|
|
case DPERR_SENDTOOBIG:
|
|
|
|
return "DPERR_SENDTOOBIG";
|
|
|
|
case DPERR_TIMEOUT:
|
|
|
|
return "DPERR_TIMEOUT";
|
|
|
|
case DPERR_UNAVAILABLE:
|
|
|
|
return "DPERR_UNAVAILABLE";
|
|
|
|
case DPERR_UNSUPPORTED:
|
|
|
|
return "DPERR_UNSUPPORTED";
|
|
|
|
case DPERR_BUSY:
|
|
|
|
return "DPERR_BUSY";
|
|
|
|
case DPERR_USERCANCEL:
|
|
|
|
return "DPERR_USERCANCEL";
|
|
|
|
case DPERR_NOINTERFACE:
|
|
|
|
return "DPERR_NOINTERFACE";
|
|
|
|
case DPERR_CANNOTCREATESERVER:
|
|
|
|
return "DPERR_CANNOTCREATESERVER";
|
|
|
|
case DPERR_PLAYERLOST:
|
|
|
|
return "DPERR_PLAYERLOST";
|
|
|
|
case DPERR_SESSIONLOST:
|
|
|
|
return "DPERR_SESSIONLOST";
|
|
|
|
case DPERR_UNINITIALIZED:
|
|
|
|
return "DPERR_UNINITIALIZED";
|
|
|
|
case DPERR_NONEWPLAYERS:
|
|
|
|
return "DPERR_NONEWPLAYERS";
|
|
|
|
case DPERR_INVALIDPASSWORD:
|
|
|
|
return "DPERR_INVALIDPASSWORD";
|
|
|
|
case DPERR_CONNECTING:
|
|
|
|
return "DPERR_CONNECTING";
|
|
|
|
case DPERR_CONNECTIONLOST:
|
|
|
|
return "DPERR_CONNECTIONLOST";
|
|
|
|
case DPERR_UNKNOWNMESSAGE:
|
|
|
|
return "DPERR_UNKNOWNMESSAGE";
|
|
|
|
case DPERR_CANCELFAILED:
|
|
|
|
return "DPERR_CANCELFAILED";
|
|
|
|
case DPERR_INVALIDPRIORITY:
|
|
|
|
return "DPERR_INVALIDPRIORITY";
|
|
|
|
case DPERR_NOTHANDLED:
|
|
|
|
return "DPERR_NOTHANDLED";
|
|
|
|
case DPERR_CANCELLED:
|
|
|
|
return "DPERR_CANCELLED";
|
|
|
|
case DPERR_ABORTED:
|
|
|
|
return "DPERR_ABORTED";
|
|
|
|
case DPERR_BUFFERTOOLARGE:
|
|
|
|
return "DPERR_BUFFERTOOLARGE";
|
|
|
|
case DPERR_CANTCREATEPROCESS:
|
|
|
|
return "DPERR_CANTCREATEPROCESS";
|
|
|
|
case DPERR_APPNOTSTARTED:
|
|
|
|
return "DPERR_APPNOTSTARTED";
|
|
|
|
case DPERR_INVALIDINTERFACE:
|
|
|
|
return "DPERR_INVALIDINTERFACE";
|
|
|
|
case DPERR_NOSERVICEPROVIDER:
|
|
|
|
return "DPERR_NOSERVICEPROVIDER";
|
|
|
|
case DPERR_UNKNOWNAPPLICATION:
|
|
|
|
return "DPERR_UNKNOWNAPPLICATION";
|
|
|
|
case DPERR_NOTLOBBIED:
|
|
|
|
return "DPERR_NOTLOBBIED";
|
|
|
|
case DPERR_SERVICEPROVIDERLOADED:
|
|
|
|
return "DPERR_SERVICEPROVIDERLOADED";
|
|
|
|
case DPERR_ALREADYREGISTERED:
|
|
|
|
return "DPERR_ALREADYREGISTERED";
|
|
|
|
case DPERR_NOTREGISTERED:
|
|
|
|
return "DPERR_NOTREGISTERED";
|
|
|
|
case DPERR_AUTHENTICATIONFAILED:
|
|
|
|
return "DPERR_AUTHENTICATIONFAILED";
|
|
|
|
case DPERR_CANTLOADSSPI:
|
|
|
|
return "DPERR_CANTLOADSSPI";
|
|
|
|
case DPERR_ENCRYPTIONFAILED:
|
|
|
|
return "DPERR_ENCRYPTIONFAILED";
|
|
|
|
case DPERR_SIGNFAILED:
|
|
|
|
return "DPERR_SIGNFAILED";
|
|
|
|
case DPERR_CANTLOADSECURITYPACKAGE:
|
|
|
|
return "DPERR_CANTLOADSECURITYPACKAGE";
|
|
|
|
case DPERR_ENCRYPTIONNOTSUPPORTED:
|
|
|
|
return "DPERR_ENCRYPTIONNOTSUPPORTED";
|
|
|
|
case DPERR_CANTLOADCAPI:
|
|
|
|
return "DPERR_CANTLOADCAPI";
|
|
|
|
case DPERR_NOTLOGGEDIN:
|
|
|
|
return "DPERR_NOTLOGGEDIN";
|
|
|
|
case DPERR_LOGONDENIED:
|
|
|
|
return "DPERR_LOGONDENIED";
|
|
|
|
default:
|
|
|
|
/* For errors not in the list, return HRESULT as a string
|
|
|
|
This part is not thread safe */
|
|
|
|
WARN( "Unknown error 0x%08lx\n", hr );
|
|
|
|
sprintf( szTempStr, "0x%08lx", hr );
|
|
|
|
return szTempStr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|