1 /* ********************************************************************	*
2  *																		*
3  *	DPLAYEXT.C - DirectPlay multi-player code extensions.				*
4  *																		*
5  *	By: Garry Lancaster									Version: 0.1	*
6  *																		*
7  * ******************************************************************** */
8 
9 /* N O T E S ---------------------------------------------------------- */
10 
11 /* Define USE_DB here to use my db.c debugging file. If you wish to use
12  * another debugging system you will have to redefine the ASSERT macro to
13  * use your debugging system or turn off assertions by defining NDEBUG.
14  * - Garry.
15  */
16 
17 /* This code marks an experimental departure for me into Hungarian variable
18  * names. Enjoy! (or perhaps not.)
19  */
20 
21 /* I N C L U D E S ---------------------------------------------------- */
22 
23 /* OS includes. */
24 #include <windows.h>
25 
26 /* Include for this file. */
27 #include "dplayext.h"
28 
29 /* Custom includes. */
30 #define UseLocalAssert Yes
31 #include "ourasert.h"
32 
33 /* C O N S T A N T S -------------------------------------------------- */
34 
35 /* The maximum length of a message that can be received by the system, in
36  * bytes. DPEXT_MAX_MSG_SIZE bytes will be required to hold the message, so
37  * you can't just set this to a huge number.
38  */
39 #define DPEXT_MAX_MSG_SIZE	3072
40 
41 /* This msg is not guaranteed. */
42 #define DPEXT_NOT_GUARANTEED	1
43 
44 /* M A C R O S -------------------------------------------------------- */
45 
46 
47 /* Advance the guaranteed msg count. Wraps around to 1 when the maximum
48  * +ve value for a signed int is passed.
49  */
50 #define DPEXT_NEXT_GRNTD_MSG_COUNT() \
51 	if( ++gnCurrGrntdMsgId < 1 ) gnCurrGrntdMsgId = 1
52 
53 /* Generate the msg stamp for a reply to a msg with a specified
54  * guaranteed msg id.
55  */
56 #define DPEXT_TO_REPLY_STAMP( iSentStamp ) ( -( iSentStamp ) )
57 
58 /* Identify the original msg stamp given a reply stamp. */
59 #define DPEXT_TO_ORIGINAL_STAMP( iReplyStamp ) \
60 	DPEXT_GET_REPLY_STAMP( iReplyStamp )
61 
62 /* T Y P E S ---------------------------------------------------------- */
63 
64 struct DpExtGrntdMsgInfo
65 {
66 	void **abufPending;
67 	DWORD cBuffers;
68 	DWORD iLastUsed;
69 };
70 
71 /* G L O B A L S . . . ------------------------------------------------ */
72 
73 /* ...with external scope. */
74 
75 /* ...with internal (static) scope. */
76 
77 /* Buffer used to store incoming messages. */
78 static unsigned char gbufDpExtRecv[ DPEXT_MAX_MSG_SIZE ];
79 
80 /* Are we expected to add our own error checking? */
81 static BOOL gbDpExtDoErrChcks = FALSE;
82 
83 /* Are we expected to implement guaranteed message sending? */
84 static BOOL gbDpExtDoGrntdMsgs = FALSE;
85 
86 /* The current count of guaranteed msgs. Zero is reserved for msgs that
87  * aren't guaranteed (using DPEXT_NOT_GUARANTEED) and negative numbers are
88  * reserved for replies to guaranteed messages.
89  */
90 static int gnCurrGrntdMsgId = 1;
91 
92 /* Storage for guaranteed msgs and replies to guaranteed msgs. */
93 static struct DpExtGrntdMsgInfo	ggmiMsgs 	= { NULL, 0, 0 };
94 static struct DpExtGrntdMsgInfo ggmiReplies = { NULL, 0, 0 };
95 
96 /* P R O T O S -------------------------------------------------------- */
97 
98 /* Should all be static. */
99 
100 /* Perform any required processing on a received msg. No processing is
101  * required if neither DpExt guaranteed msg system or the DpExt error
102  * checking system are on. Returns TRUE if the message was internal only,
103  * FALSE if it should be passed to the user.
104  */
105 static BOOL DpExtProcessRecvdMsg(BOOL bIsSystemMsg, LPVOID lpData,
106  	DWORD dwDataSize);
107 
108 /* Generates and returns a checksum for the supplied buffer. The algorithm
109  * used ignores the first 4 bytes of the buffer (this is where the checksum
110  * should eventually be stored) and is sensitive to the order of the bytes
111  * and long words stored as well as lost data, added data, duplicated data,
112  * and standard corruption.
113  */
114 static DWORD DpExtChecksumMsg(LPVOID lpData, DWORD dwDataSize);
115 
116 /* Use a destroy msg to try and free up nodes in the guaranteed msg list
117  * that will never now be freed in the usual manner i.e. by a reply to or
118  * a re-send from the player.
119  */
120 static void DpExtUseDestroyMsg(LPDPMSG_DESTROYPLAYERORGROUP pmsgDestroy);
121 
122 /* -------------------------------------------------------------------- *
123  *																		*
124  * 						F U N C T I O N S 								*
125  *																		*
126  * -------------------------------------------------------------------- */
127 
128 /* -------------------------------------------------------------------- *
129  *	I N T E R F A C E   F N S - with external scope.					*
130  * -------------------------------------------------------------------- */
131 
132 
133 /* Call this fn to initialise the DpExt module. cGrntdBufs sets the number
134  * of guaranteed message buffers to use and cBytesPerBuf is the number of
135  * bytes to allocate to each buffer, and thus the maximum length
136  * (including DpExt header) for a guaranteed msg. cGrntdBufs should be 0
137  * if you don't want DpExt's guaranteed msg sending system to be turned on.
138  * bErrChcks should be TRUE if you require DpExt's error checking system,
139  * otherwise FALSE.
140  */
DpExtInit(DWORD cGrntdBufs,DWORD cBytesPerBuf,BOOL bErrChcks)141 BOOL DpExtInit(DWORD cGrntdBufs, DWORD cBytesPerBuf,
142 	BOOL bErrChcks)
143 {
144 	return TRUE;
145 }
146 
147 
148 /* Un-initialises the DpExt module. */
DpExtUnInit(void)149 void DpExtUnInit(void)
150 {
151 }
152 
153 
154 /* This fn has the same parameters as the standard DirectPlay Send() fn.
155  * However, you *must* leave DPEXT_HEADER_SIZE bytes free at the start of
156  * your data buffer. The dwDataSize byte count must includes these bytes.
157  */
DpExtSend(LPDIRECTPLAY4 lpDP2A,DPID idFrom,DPID idTo,DWORD dwFlags,LPVOID lpData,DWORD dwDataSize)158 HRESULT DpExtSend
159 (
160 	LPDIRECTPLAY4 lpDP2A,	/* IN: Ptr to IDirectPlay3A (DBCS) interface. */
161 	DPID idFrom, 			/* IN: ID of sending player (you)	*/
162 	DPID idTo,				/* IN: ID of destination player.	*/
163 	DWORD dwFlags, 			/* IN: DirectPlay Flags.			*/
164 	LPVOID lpData,			/* IN: Ptr to start of message. 	*/
165 	DWORD dwDataSize		/* IN: Byte count of message. 		*/
166 )
167 {
168 	HRESULT hrSend;
169 
170 	/* Assert input conditions that the DirectPlay call may not check. */
171 	LOCALASSERT( lpDP2A );
172 	LOCALASSERT( lpData );
173 	LOCALASSERT( dwDataSize >= DPEXT_HEADER_SIZE );
174 
175 	/* Add header information. */
176 	{
177 		struct DpExtHeader *pmsghdr	= (struct DpExtHeader *) lpData;
178 
179 		if( gbDpExtDoGrntdMsgs && ( DPSEND_GUARANTEED & dwFlags ) )
180 		{
181 			/* Add guaranteed msg stamp. */
182 			pmsghdr->dwMsgStamp = gnCurrGrntdMsgId;
183 			DPEXT_NEXT_GRNTD_MSG_COUNT();
184 		}
185 		else
186 		{
187 			pmsghdr->dwMsgStamp = DPEXT_NOT_GUARANTEED;
188 		}
189 		if( gbDpExtDoErrChcks )
190 		{
191 			pmsghdr->dwChecksum = DpExtChecksumMsg( lpData, dwDataSize );
192 		}
193 	}
194 
195 	hrSend = IDirectPlay3_Send( lpDP2A, idFrom, idTo, dwFlags, lpData,
196 		dwDataSize );
197 
198 	return hrSend;
199 }
200 
201 
202 /* This fn has similar parameters to the standard DirectPlay Receive()
203  * fn. Be aware that the 2 id parameters, the lplpData parameter and the
204  * lpdwDataSize parameters are only valid as inputs if the
205  * appropriate flags are used in the dwFlags parameter. They are always
206  * valid as outputs.
207  *
208  * The main difference from the standard Receive() is that the LPVOID
209  * lpData field has changed to LPVOID *lplpData. Now instead of supplying
210  * the buffer for the message and having the fn fail if the	buffer isn't
211  * big enough, the buffer is allocated for you and you get a pointer to it.
212  * I re-use the same buffer each time DpExtRecv() is called, so the pointer
213  * is only valid until the next time you call this fn.
214  *
215  * If you want to maintain the message longer than that you can (a) copy the
216  * message data to your own buffer or (b) more efficiently, pass a ptr to
217  * a ptr to your own allocated data buffer in lplpData, a ptr to its size
218  * in lpdwDataSize and set the flag DPEXT_USER_BUFFER in dwFlags - this is
219  * more like the original DirectPlay Receive() fn behaviour. In the case of
220  * (b), you must leave DPEXT_HEADER_SIZE bytes free at the start of your
221  * user buffer and include these bytes in *lpdwDataSize.
222  *
223  * All non-system messages received will use their first DPEXT_HEADER_SIZE
224  * bytes for header information. Your message proper begins after this
225  * header.
226  */
DpExtRecv(LPDIRECTPLAY4 lpDP2A,LPDPID lpidFrom,LPDPID lpidTo,DWORD dwFlags,LPVOID * lplpData,LPDWORD lpdwDataSize)227 HRESULT DpExtRecv
228 (
229 	LPDIRECTPLAY4 lpDP2A,	/* IN: Ptr to IDirectPlay3A (DBCS) interface. */
230 	LPDPID lpidFrom, 		/* IN/OUT: Ptr to from player id.  	*/
231 	LPDPID lpidTo, 			/* IN/OUT: Ptr to to player id.		*/
232 	DWORD dwFlags, 			/* IN: DirectPlay flags.			*/
233 	LPVOID *lplpData, 		/* IN/OUT: Ptr to ptr to message data.	*/
234 	LPDWORD lpdwDataSize	/* IN/OUT: Ptr to byte count of message.	*/
235 )
236 {
237 	HRESULT hrRecv;
238 	BOOL	bInternalOnly;
239 	BOOL	bIsSysMsg;
240 
241 	/* Assert input conditions that the DirectPlay call may not check. */
242 	LOCALASSERT( lpDP2A );
243 	LOCALASSERT( lpidFrom );
244 	LOCALASSERT( lpidTo );
245 	LOCALASSERT( lpdwDataSize );
246 
247 	/* Did the user want to use their own data buffer?
248 	 * N.B. Does this need to go in the loop?
249 	 */
250 	if( !( DPEXT_USER_BUFFER & dwFlags ) )
251 	{
252 		/* No. Set parameters to write to internal buffer. */
253 		*lplpData = gbufDpExtRecv;
254 		*lpdwDataSize = DPEXT_MAX_MSG_SIZE;
255 	}
256 
257 	do
258 	{
259 		bInternalOnly = FALSE;	/* Default. */
260 
261 		hrRecv = IDirectPlay3_Receive( lpDP2A, lpidFrom, lpidTo, dwFlags,
262 			*lplpData, lpdwDataSize );
263 
264 		/* DirectPlay bug work-around. *lpdwDataSize does not get filled in
265 		 * automatically for some system messages.
266 		 */
267 		if( ( DPID_SYSMSG == *lpidFrom ) &&
268 			( DP_OK == hrRecv ) )
269 		{
270 		   	DPMSG_GENERIC msgGenSys	= *( (LPDPMSG_GENERIC) *lplpData );
271 
272 			bIsSysMsg = TRUE;
273 			switch( msgGenSys.dwType )
274 			{
275 				case DPSYS_ADDPLAYERTOGROUP:
276 				case DPSYS_DELETEPLAYERFROMGROUP:
277 					/* Didn't test for bug - possibly okay. */
278 					*lpdwDataSize = sizeof( DPMSG_ADDPLAYERTOGROUP );
279 					break;
280 				case DPSYS_CREATEPLAYERORGROUP:
281 					/* Not necessary - bug doesn't affect this message. */
282 					break;
283 				case DPSYS_DESTROYPLAYERORGROUP:
284 					*lpdwDataSize = sizeof( DPMSG_DESTROYPLAYERORGROUP );
285 					break;
286 				case DPSYS_SETPLAYERORGROUPDATA:
287 					/* Didn't test for bug - possibly okay. */
288 					*lpdwDataSize = sizeof( DPMSG_SETPLAYERORGROUPDATA );
289 					break;
290 				case DPSYS_SETPLAYERORGROUPNAME:
291 					/* Didn't test for bug - possibly okay. */
292 					*lpdwDataSize = sizeof( DPMSG_SETPLAYERORGROUPNAME );
293 					break;
294 				case DPSYS_HOST:
295 				case DPSYS_SESSIONLOST:
296 					*lpdwDataSize = sizeof( DPMSG_GENERIC );
297 					break;
298 			}
299 		}
300 		else bIsSysMsg = FALSE;
301 
302 		/* Might we need to process this message? */
303 		if( ( DP_OK == hrRecv ) &&
304 			( gbDpExtDoGrntdMsgs || gbDpExtDoErrChcks ) )
305 		{
306 			bInternalOnly = DpExtProcessRecvdMsg
307 			(
308 				bIsSysMsg,
309 				*lplpData,
310 				*lpdwDataSize
311 			);
312 		}
313 	}
314 	while( bInternalOnly );
315 
316 	return hrRecv;
317 }
318 
319 
320 /* -------------------------------------------------------------------- *
321  *	S T A T I C   F N S - with internal scope.							*
322  * -------------------------------------------------------------------- */
323 
324 
325 /* Perform any required processing on a received msg. No processing is
326  * required if neither DpExt guaranteed msg system or the DpExt error
327  * checking system are on. Returns TRUE if the message was internal only,
328  * FALSE if it should be passed to the user.
329  */
DpExtProcessRecvdMsg(BOOL bIsSystemMsg,LPVOID lpData,DWORD dwDataSize)330 static BOOL DpExtProcessRecvdMsg(BOOL bIsSystemMsg, LPVOID lpData,
331  	DWORD dwDataSize)
332 {
333 	/* Is this a system message? */
334 	if( bIsSystemMsg )
335 	{
336 		/* Yes. Do we need to intercept any system messages for our own
337 		 * nefarious purposes?
338 		 */
339 		if( gbDpExtDoGrntdMsgs )
340 		{
341 			/* Yes, we need to intercept messages about deleted players or
342 			 * groups. Is this one?
343 			 */
344 			DPMSG_GENERIC *pmsgGeneric = (DPMSG_GENERIC *) lpData;
345 
346 			if( DPSYS_DESTROYPLAYERORGROUP == pmsgGeneric->dwType )
347 			{
348 				DpExtUseDestroyMsg( (LPDPMSG_DESTROYPLAYERORGROUP)
349 					pmsgGeneric );
350 			}
351 		}
352 	}
353 	else
354 	{
355 		/* No, this isn't a system message. Use DpExt header... */
356 		struct DpExtHeader *pmsghdr = (struct DpExtHeader *) lpData;
357 
358 		LOCALASSERT( dwDataSize >= DPEXT_HEADER_SIZE );
359 
360 		/* ...Should we check this message for errors? */
361 		if( gbDpExtDoErrChcks )
362 		{
363 
364 		}
365 
366 		/* ...Was this message sent by the DpExt guaranteed message system? */
367 	}
368 
369 	return FALSE;
370 }
371 
372 
373 /* Generates and returns a checksum for the supplied buffer. The algorithm
374  * used ignores the first 4 bytes of the buffer (this is where the checksum
375  * should eventually be stored) and is sensitive to the order of the bytes
376  * and long words stored as well as lost data, added data, duplicated data,
377  * and standard corruption.
378  */
DpExtChecksumMsg(LPVOID lpData,DWORD dwDataSize)379 static DWORD DpExtChecksumMsg(LPVOID lpData, DWORD dwDataSize)
380 {
381 	return 0;	/* No implementation yet. */
382 }
383 
384 
385 /* Use a destroy msg to try and free up nodes in the guaranteed msg list
386  * that will never now be freed in the usual manner i.e. by a reply to or
387  * a re-send from the player.
388  */
DpExtUseDestroyMsg(LPDPMSG_DESTROYPLAYERORGROUP pmsgDestroy)389 static void DpExtUseDestroyMsg(LPDPMSG_DESTROYPLAYERORGROUP pmsgDestroy)
390 {
391 	/* No implementation yet. */
392 }