
#ifndef __WINE_DPLAYX_MESSAGES__
#define __WINE_DPLAYX_MESSAGES__

#include "windef.h"
#include "dplay.h"
#include "rpc.h" /* For GUID */
#include "winsock2.h"

#include "dplay_global.h"

DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart,
                                         HANDLE hDeath, HANDLE hConnRead );

HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
                                    LPDPID lpdipidAllocatedId );
HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* This, DPID dpidServer );

void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId,
                           LPCVOID lpMsgBody, DWORD dwMsgBodySize );
void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
                           LPCVOID lpMsgBody, DWORD dwMsgBodySize );
void DP_MSG_ToSelf( IDirectPlay2AImpl* This, DPID dpidSelf );
HRESULT DP_MSG_SendNameTable( IDirectPlay2Impl* This, LPCVOID lpcMessageHeader,
                              LPVOID* lplpReplyMsg, LPDWORD lpdwReplySize );

/* Timings -> 1000 ticks/sec */
#define DPMSG_WAIT_5_SECS   5000
#define DPMSG_WAIT_30_SECS 30000
#define DPMSG_WAIT_60_SECS 60000
#define DPMSG_DEFAULT_WAIT_TIME DPMSG_WAIT_30_SECS

/* Message types etc. */
#include "pshpack1.h"

/* Non provided messages for DPLAY - guess work which may be wrong :( */
#define DPMSGCMD_ENUMSESSIONSREPLY    1
#define DPMSGCMD_ENUMSESSIONSREQUEST  2

#define DPMSGCMD_REQUESTNEWPLAYERID   5

#define DPMSGCMD_NEWPLAYERIDREPLY     7
#define DPMSGCMD_CREATESESSION        8 /* Might be a create nameserver or new player msg */
#define DPMSGCMD_CREATENEWPLAYER      9
#define DPMSGCMD_SYSTEMMESSAGE        10
#define DPMSGCMD_DELETEPLAYER         11
#define DPMSGCMD_DELETEGROUP          12

#define DPMSGCMD_ENUMGROUPS           17

#define DPMSGCMD_FORWARDADDPLAYER     19

#define DPMSGCMD_PIGGYBACKTOFOLLOW    21 /* This seems to have another packet attached */

#define DPMSGCMD_PLAYERCHAT           22 /* Most likely wrong */
#define DPMSGCMD_PLAYERCHATREPLY      23 /* Just a guess at this point */

#define DPMSGCMD_FORWARDADDPLAYERNACK 36

#define DPMSGCMD_NAMETABLE            41  /* ?Contains all existing players in session? */

#define DPMSGCMD_JUSTENVELOPE         1000
#define DPMSGCMD_JUSTENVELOPEREPLY    1001

/* This is what DP 6 defines it as. Don't know what it means. All messages
 * defined below are DPMSGVER_DP6.
 */
#define DPMSGVER_DP6 11

/* MAGIC number at the start of all dplay packets ("play" in ASCII) */
#define DPMSGMAGIC_DPLAYMSG  0x79616c70

/* All messages sent from the system are sent with this at the beginning of
 * the message.
 * Size is 8 bytes
 */
typedef struct tagDPMSG_SENDENVELOPE
{
  DWORD dwMagic;
  WORD  wCommandId;
  WORD  wVersion;
} DPMSG_SENDENVELOPE, *LPDPMSG_SENDENVELOPE;
typedef const DPMSG_SENDENVELOPE* LPCDPMSG_SENDENVELOPE;

/* System messages exchanged between players seems to have this
 * payload envelope on top of the basic envelope
 */
typedef struct tagDPMSG_SYSMSGENVELOPE
{
  DWORD dwPlayerFrom;
  DWORD dwPlayerTo;
} DPMSG_SYSMSGENVELOPE, *LPDPMSG_SYSMSGENVELOPE;
typedef const DPMSG_SYSMSGENVELOPE* LPCDPMSG_SYSMSGENVELOPE;

/* Reply sent in response to an enumsession request */
typedef struct tagDPMSG_ENUMSESSIONSREPLY
{
  DPMSG_SENDENVELOPE envelope;

  DPSESSIONDESC2 sd;
  /* NOTES: dwReserved1 is the name server id */

  DWORD dwUnknown;  /* Seems to be equal to 0x5c which is a "\\" */
                    /* Encryption package string? */

  /* At the end we have ... */
  /* FIXME: Uncomment this and use it */
  /* WCHAR wszSessionName[0];  Var length with NULL terminal */

} DPMSG_ENUMSESSIONSREPLY, *LPDPMSG_ENUMSESSIONSREPLY;
typedef const DPMSG_ENUMSESSIONSREPLY* LPCDPMSG_ENUMSESSIONSREPLY;

/* Msg sent to find out what sessions are available */
typedef struct tagDPMSG_ENUMSESSIONSREQUEST
{
  DPMSG_SENDENVELOPE envelope;

  GUID  guidApplication;

  DWORD dwPasswordSize; /* A Guess. This is 0x00000000. */
                        /* This might be the name server DPID which
                           is needed for the reply */

  DWORD dwFlags; /* dwFlags from EnumSessions */

} DPMSG_ENUMSESSIONSREQUEST, *LPDPMSG_ENUMSESSIONSREQUEST;
typedef const DPMSG_ENUMSESSIONSREQUEST* LPCDPMSG_ENUMSESSIONSREQUEST;

/* Size is 146 received - with 18 or 20 bytes header = ~128 bytes */
typedef struct tagDPMSG_CREATESESSION
{
  DPMSG_SENDENVELOPE envelope;
} DPMSG_CREATESESSION, *LPDPMSG_CREATESESSION;
typedef const DPMSG_CREATESESSION* LPCDPMSG_CREATESESSION;

/* 12 bytes msg */
typedef struct tagDPMSG_REQUESTNEWPLAYERID
{
  DPMSG_SENDENVELOPE envelope;

  DWORD dwFlags;  /* dwFlags used for CreatePlayer */

} DPMSG_REQUESTNEWPLAYERID, *LPDPMSG_REQUESTNEWPLAYERID;
typedef const DPMSG_REQUESTNEWPLAYERID* LPCDPMSG_REQUESTNEWPLAYERID;

/* 48 bytes msg */
typedef struct tagDPMSG_NEWPLAYERIDREPLY
{
  DPMSG_SENDENVELOPE envelope;

  DPID dpidNewPlayerId;

  /* Assume that this is data that is tacked on to the end of the message
   * that comes from the SP remote data stored that needs to be propagated.
   */
  BYTE unknown[36];     /* This appears to always be 0 - not sure though */
} DPMSG_NEWPLAYERIDREPLY, *LPDPMSG_NEWPLAYERIDREPLY;
typedef const DPMSG_NEWPLAYERIDREPLY* LPCDPMSG_NEWPLAYERIDREPLY;


/* 114 bytes */
typedef struct tagDPMSG_FORWARDADDPLAYER
{
  DPMSG_SENDENVELOPE envelope;

  DWORD unknown; /* 0 */

  DPID  dpidAppServer; /* Remote application server id */
  DWORD unknown2[5]; /* 0x0, 0x1c, 0x6c, 0x50, 0x9 */ /* NOTE: Same in versions 0xb and 0xd */

  DPID  dpidAppServer2; /* Remote application server id again !? */
  DWORD unknown3[5]; /* 0x0, 0x0, 0x20, 0x0, 0x0 */ /* NOTE: Same in versions 0xb and 0xd */

  DPID  dpidAppServer3; /* Remote application server id again !? */

  DWORD dwUnknown4; /* Seems to be 0x30 */
  DWORD dwVersion;  /* Same as the wVersion field of the DPMSG_SENDENVELOPE - is it a DWORD here? */
  DWORD dwUnknown5; /* Seems to be 0x0 */

  SOCKADDR clientAddress; /* Why is there TCP information in an independent layer? */
  SOCKADDR serverAddress; /* Why is there TCP information in an independent layer? */

  DPID dpidNameServer; /* This is the name server - comes from EnumSessionsReply.dwReserved1 */

} DPMSG_FORWARDADDPLAYER, *LPDPMSG_FORWARDADDPLAYER;
typedef const DPMSG_FORWARDADDPLAYER* LPCDPMSG_FORWARDADDPLAYER;


/* This is a message which seems to always have another message tagged on afterwards */
/* DPMSGCMD_PIGGYBACKTOFOLLOW */
typedef struct tagDPMSG_PIGGYBACKTOFOLLOW
{
  DPMSG_SENDENVELOPE envelope;

  GUID unknownGuid;  /* Seems to be some sort of unique identifier. Different between messages */

  DWORD dwUnknown1;  /* Seems to always be 00 00 00 00 */
  DWORD dwSizeOfPiggyBack1; /* Size of piggybacked dplay message */
  DWORD dwUnknown2;  /* Seems to always be 00 00 00 00 */
#define PIGGYBACKTOFOLLOW_UNKNOWN_ZERO (DWORD)0

  DWORD dwUnknown3;  /* Seems to always be 01 00 00 00 (second packet information?) */
  DWORD dwSizeOfPiggyBack2; /* Size of piggybacked dplay message */
  DWORD dwUnknown4;  /* Seems to always be 00 00 00 00 */
#define PIGGYBACKTOFOLLOW_UNKNOWN_ONE (DWORD)1

  /* Then we have another packet immediately following with "play" of size dwSizeOfPiggyBack{1,2} */

} DPMSG_PIGGYBACKTOFOLLOW, *LPDPMSG_PIGGYBACKTOFOLLOW;
typedef const DPMSG_PIGGYBACKTOFOLLOW* LPCDPMSG_PIGGYBACKTOFOLLOW;

/* Appears to be only used in conjunction with the name table dump */
typedef struct tagNAMETABLE_PLAYER_CONNECTION_INFO
{
  BYTE SizeOfAddresses1; /* 0x20 for tcp/ip */
  SOCKADDR LocalSockAddr1;
  SOCKADDR RemoteSockAddr1;

} NAMETABLE_PLAYER_CONNECTION_INFO, *LPNAMETABLE_PLAYER_CONNECTION_INFO;
typedef const NAMETABLE_PLAYER_CONNECTION_INFO* LPCNAMETABLE_PLAYER_CONNECTION_INFO;

/* Appears to be only used in conjunction with the name table dump */
typedef struct tagNAMETABLE_PLAYER_OR_GROUP_INFO
{
  DWORD dwFlags1; /* 0x48 group, 0x4c group in group, (0x4, 0x5, 0xc, 0xf, 0x8c (server sys player - name server) player) */
  DPID  dpId;
  DWORD dwFlags2; /* 0x101 for group in group, 0x1 (empty group), 0x41 for group (non empty group), 0x4 (version 0xd follow?), 0x5 */
  DPID  dpidOfOwningAppServer;

  WCHAR wszShortName[0]; /* Var length with NULL terminal - is actually the short name?*/

} NAMETABLE_PLAYER_OR_GROUP_INFO, *LPNAMETABLE_PLAYER_OR_GROUP_INFO;
typedef const NAMETABLE_PLAYER_OR_GROUP_INFO* LPCNAMETABLE_PLAYER_OR_GROUP_INFO;

typedef struct tagNAMETABLE_PLAYER_ENTRY
{
  NAMETABLE_PLAYER_OR_GROUP_INFO player;

  /* 0 or 1 bit of connection information */
  NAMETABLE_PLAYER_CONNECTION_INFO conn[0];

} NAMETABLE_PLAYER_ENTRY, *LPNAMETABLE_PLAYER_ENTRY;
typedef const NAMETABLE_PLAYER_ENTRY* LPCNAMETABLE_PLAYER_ENTRY;


/* Information about a group. This structure is used if it is a 1st level group. */
typedef struct tagNAMETABLE_GROUP_PLAYER_INFO
{
  BYTE cUnknown; /* 0 - are these two bytes actually a word in big endian?*/
  BYTE cNumPlayers; /* Number of players in dpidPlayers array */

  DPID dpidPlayers[0]; /* At least 1 otherwise this struct not included */

} NAMETABLE_GROUP_PLAYER_INFO, *LPNAMETABLE_GROUP_PLAYER_INFO;
typedef const NAMETABLE_GROUP_PLAYER_INFO* LPCNAMETABLE_GROUP_PLAYER_INFO;

/* Information about a group. This structure is used if it is a 2nd level group
 * but they don't have any players */
typedef struct tagNAMETABLE_GROUP_IN_GROUP_NO_PLAYERS_INFO
{
  WORD wUnknown; /* Most likely 2 bytes. 1st always 0 and 2nd 0 because no players */
  DPID dpidContainedInGroup;

} NAMETABLE_GROUP_IN_GROUP_NO_PLAYERS_INFO, *LPNAMETABLE_GROUP_IN_GROUP_NO_PLAYERS_INFO;
typedef const NAMETABLE_GROUP_IN_GROUP_NO_PLAYERS_INFO* LPCNAMETABLE_GROUP_IN_GROUP_NO_PLAYERS_INFO;

/* Information about a group's owner */
typedef struct tagNAMETABLE_GROUP_IN_GROUP_WITH_PLAYERS_INFO
{
  BYTE cUnknown; /* 0 */

  DPID dpidContainedInGroup; /* What 1st level group contains this */

  BYTE cNumPlayers; /* Number of players to follow */

  DPID dpidPlayers[0];

} NAMETABLE_GROUP_IN_GROUP_WITH_PLAYERS_INFO, *LPNAMETABLE_GROUP_IN_GROUP_WITH_PLAYERS_INFO;
typedef const NAMETABLE_GROUP_IN_GROUP_WITH_PLAYERS_INFO* LPCNAMETABLE_GROUP_IN_GROUP_WITH_PLAYERS_INFO;

typedef struct tagNAMETABLE_GROUP_ENTRY
{
  NAMETABLE_PLAYER_OR_GROUP_INFO group;

  union
  {
    NAMETABLE_GROUP_PLAYER_INFO                group[0]; /* 1st level group */
    NAMETABLE_GROUP_IN_GROUP_NO_PLAYERS_INFO   g_in_gnp[0]; /* 2nd level NO players */
    NAMETABLE_GROUP_IN_GROUP_WITH_PLAYERS_INFO g_in_gwp[0]; /* 2nd level with players */
  } u;

} NAMETABLE_GROUP_ENTRY, *LPNAMETABLE_GROUP_ENTRY;
typedef const NAMETABLE_GROUP_ENTRY* LPCNAMETABLE_GROUP_ENTRY;

/* One of a player or group entry. */
typedef struct tagNAMETABLE_DATA_ENTRY
{
  DWORD dwSize;

  union
  {
    NAMETABLE_GROUP_ENTRY  group;
    NAMETABLE_PLAYER_ENTRY player;
  } u;

} NAMETABLE_DATA_ENTRY, *LPNAMETABLE_DATA_ENTRY;
typedef const NAMETABLE_DATA_ENTRY* LPCNAMETABLE_DATA_ENTRY;

/* dwFlags1 field - indicates what type of entry we're dealing with */
#define DPMSG_NAMETABLE_FLAGS1_IS_NON_SERVER_APP_SERVER 0x05
#define DPMSG_NAMETABLE_FLAGS1_IS_NORMAL_PLAYER         0x0c
#define DPMSG_NAMETABLE_FLAGS1_IS_SERVER_APP_SERVER     0x0f
#define DPMSG_NAMETABLE_FLAGS1_IS_GROUP                 0x48
#define DPMSG_NAMETABLE_FLAGS1_IS_GROUP_IN_GROUP        0x4c
#define DPMSG_NAMETABLE_FLAGS1_IS_NAME_SERVER           0x8c

/* dwFlags2 field - indicates what type of information follows for players */
#define DPMSG_NAMETABLE_FLAGS2_PLAYER_HAS_NAME      0x00000001
#define DPMSG_NAMETABLE_FLAGS2_PLAYER_HAS_CONN_DATA 0x00000004

/* dwFlags2 field - indicates what type of information follows for groups */
#define DPMSG_NAMETABLE_FLAGS2_GROUP_HAS_NAME              DPMSG_NAMETABLE_FLAGS2_PLAYER_HAS_NAME
#define DPMSG_NAMETABLE_FLAGS2_GROUP_HAS_PLAYERS_CONTAINED 0x00000040
#define DPMSG_NAMETABLE_FLAGS2_GROUP_HAS_OWNER             0x00000100


/* The whole wacking name table (all players presently in existance - or so we think */
typedef struct tagDPMSG_NAMETABLE
{
  DPMSG_SENDENVELOPE envelope;

  DWORD dwNumberOfSessionPlayers;
  DWORD dwNumberOfSessionGroups;

  DWORD dwSizeOfSESSIONDESC2Carrier; /* Size of payload carrying dwUnknown2, SESSIONDESC2 and the game's name */
  DWORD dwUnknown2[4]; /* 0x0, 0x24, 0x74, 0x0 */
  DPSESSIONDESC2 sd;

  /* Size is dwSizeOfSESSIONDESC2Carrier - sizeof(sd) - sizeof(dwUnknown2) */
  WCHAR wszSessionName[0];  /*Var length with NULL terminal. */

  /* Array of dwNumberOfSessionPlayers + dwNumberOfSessionGroups in the session */
  NAMETABLE_DATA_ENTRY player[0];

} DPMSG_NAMETABLE, *LPDPMSG_NAMETABLE;
typedef const DPMSG_NAMETABLE* LPCDPMSG_NAMETABLE;


/* This is an error message that can be received. Not sure if this is
 * specifically for a forward add player or for all errors
 */
typedef struct tagDPMSG_FORWARDADDPLAYERNACK
{
  DPMSG_SENDENVELOPE envelope;
  HRESULT errorCode;
} DPMSG_FORWARDADDPLAYERNACK, *LPDPMSG_FORWARDADDPLAYERNACK;
typedef const DPMSG_FORWARDADDPLAYERNACK* LPCDPMSG_FORWARDADDPLAYERNACK;

#include "poppack.h"

#endif
