/* Autor: Leandro Bernsmuller
Controla comunicacao entre duas aplicacoes usando DirectPlay.

A comunicacao eh baseada em mensagens. O envio eh feito por "Send...".
A recepcao por "Receive..." usando uma thread que testa a chegada de
novas mensagens.

Baseado no exemplo DPCHAT do DX 5 */



#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include <d3drmwin.h>
#include <mmsystem.h>
#include <dsound.h>
#include "..\princip\defs.h"
#include "..\extra\ddutil.h"
#include "..\extra\ddmm.h"
#include "..\extra\arqs.h"
#include "..\cenario\gr.h"
#include "..\cenario\cenario.h"
#include "..\cenario\cenfis.h"
#include "..\som\sons.h"
#include "..\ia\acoes\acao.h"
#include "..\ia\dados\dados.h"
#include "..\ia\agente.h"
#include "..\ia\agencia.h"
#include "..\interf\janela.h"
#include "..\interf\jancen.h"
#include "..\interf\dlgconpa.h"
#include "..\interf\dlgconf.h"
#include "..\interf\dlgmodo.h"
#include "..\interf\dlgsobre.h"
#include "..\princip\apl.h"

#define IDIRECTPLAY2_OR_GREATER
#include <dplay.h>
     
#include "dplayinf.h"                          

#include "..\interf\dlgconve.h"

#include "dialog.h"
#include "dpcomu.h"



// constants
const DWORD MAXSTRLEN         = 200;         // max size of a temporary string

// globals

// informacoes sobre estah conexao: quem eh servidor, ligacao
DPLAYINFO   DPInfo;

// TRUE - conexao numa rede foi estabelecida como cliente ou servidor
// FALSE - nao estah conectado, estah isolado
// Informa apenas que houve conexao por um canal via DirectPlay.
BOOL        bUSANDO_REDEDP = FALSE;

// TRUE - cliente se conectou
// FALSE - voce eh o cliente ou cliente ainda nao se conectou
// Deve ser FALSE ao desconectar.
BOOL        bCLIENTE_CONECTOU = FALSE;

// TRUE - cliente escolheu o seu personagem
// FALSE - cliente ainda nao escolheu personagem
// Deve ser FALSE ao iniciar fase.
BOOL        bCLIENTE_ESCOLHEU_PERSONAGEM = FALSE;

// TRUE - desistiu de escolher por personagem do cliente
// FALSE - nao desistiu
// Deve ser FALSE ao iniciar fase.
BOOL        bSERVIDOR_DESISTIU_DE_ESPERAR_PERSONAGEM = FALSE;

// usado para poder controlar o cliente via mensagens
extern CApl oJOGO;



// funcoes implementadas nesse modulo:
HRESULT        SetupConnection(HINSTANCE hInstance, LPDPLAYINFO lpDPInfo);
HRESULT        ShutdownConnection(LPDPLAYINFO lpDPInfo);
DWORD WINAPI   ReceiveThread(LPVOID lpThreadParameter);
HRESULT        ReceiveMessage(LPDPLAYINFO lpDPInfo);
void           HandleApplicationMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
                               DPID idFrom, DPID idTo);
void           HandleSystemMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
                           DPID idFrom, DPID idTo);
HRESULT        GetChatPlayerName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
                          LPDPNAME *lplpName);
HRESULT        SendChatMessage(HWND hWnd, LPDPLAYINFO lpDPInfo);
HRESULT        NewChatString(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
                       LPSTR lpszMsg, LPSTR *lplpszStr);

HANDLE      ghReceiveThread = NULL;       // handle of receive thread
DWORD       gidReceiveThread = 0;         // id of receive thread
HANDLE      ghKillReceiveEvent = NULL;    // event used to kill receive thread



// inicia conexao com colega
int TentaConectar ( HINSTANCE hInstance )
{
   // se jah estah conectado
   if ( bUSANDO_REDEDP )
      return 0;

   HRESULT     hr;

   // Initialize COM library
   hr = CoInitialize(NULL);
   if (FAILED(hr)) {
      Desconectar ();
      return 0;
   }

   // setup the connection
   hr = SetupConnection(hInstance, &DPInfo);
   if (FAILED(hr)) {
      Desconectar ();
      return 0;
   }

   bUSANDO_REDEDP = TRUE;

   return 0;
}



// termina conexao com colega
int Desconectar ()
{
   HRESULT     hr;

   // shut down the connection
   hr = ShutdownConnection(&DPInfo);

   // Uninitialize the COM library
   CoUninitialize();

   bUSANDO_REDEDP = FALSE;

   return 0;
}



// configura conexao com colega
HRESULT SetupConnection(HINSTANCE hInstance, LPDPLAYINFO lpDPInfo)
{
   HRESULT     hr;

   ZeroMemory(lpDPInfo, sizeof(DPLAYINFO));

   // create event used by DirectPlay to signal a message has arrived
   lpDPInfo->hPlayerEvent = CreateEvent(NULL,      // no security
                               FALSE,     // auto reset
                               FALSE,     // initial event reset
                               NULL);     // no name
   if (lpDPInfo->hPlayerEvent == NULL)
   {
      hr = DPERR_NOMEMORY;
      goto FAILURE;
   }

   // create event used to signal that the receive thread should exit
   ghKillReceiveEvent = CreateEvent(NULL,    // no security
                            FALSE,     // auto reset
                            FALSE,     // initial event reset
                            NULL);     // no name
   if (ghKillReceiveEvent == NULL)
   {
      hr = DPERR_NOMEMORY;
      goto FAILURE;
   }

   // create thread to receive player messages
   ghReceiveThread = CreateThread(NULL,         // default security
                           0,          // default stack size
                           ReceiveThread, // pointer to thread routine
                           lpDPInfo,      // argument for thread
                           0,          // start it right away
                           &gidReceiveThread);
   if (ghReceiveThread == NULL)
   {
      hr = DPERR_NOMEMORY;
      goto FAILURE;
   }

   // try to connect using the lobby
   // removida conexao por lobby: nao requer execucao automatica da
   // aplicacao

   hr = ConnectUsingDialog(hInstance, lpDPInfo);
   if FAILED(hr)
      goto FAILURE;

   return (DP_OK);   

FAILURE:
   ShutdownConnection(lpDPInfo);

   return (hr);
}



// termina conexao do DirectPlay
HRESULT ShutdownConnection(LPDPLAYINFO lpDPInfo)
{
   if (ghReceiveThread)
   {
      // wake up receive thread and wait for it to quit
      SetEvent(ghKillReceiveEvent);
      WaitForSingleObject(ghReceiveThread, INFINITE);

      CloseHandle(ghReceiveThread);
      ghReceiveThread = NULL;
   }

   if (ghKillReceiveEvent)
   {
      CloseHandle(ghKillReceiveEvent);
      ghKillReceiveEvent = NULL;
   }

   if (lpDPInfo->lpDirectPlay3A)
   {
      if (lpDPInfo->dpidPlayer)
      {
         lpDPInfo->lpDirectPlay3A->DestroyPlayer(lpDPInfo->dpidPlayer);
         lpDPInfo->dpidPlayer = 0;
      }
      lpDPInfo->lpDirectPlay3A->Close();
      lpDPInfo->lpDirectPlay3A->Release();
      lpDPInfo->lpDirectPlay3A = NULL;
   }

   if (lpDPInfo->hPlayerEvent)
   {
      CloseHandle(lpDPInfo->hPlayerEvent);
      lpDPInfo->hPlayerEvent = NULL;
   }

   return (DP_OK);
}



// funcao da thread que recebe as mensagens via DirectPlay
DWORD WINAPI ReceiveThread(LPVOID lpThreadParameter)
{
    LPDPLAYINFO   lpDPInfo = (LPDPLAYINFO) lpThreadParameter;
   HANDLE      eventHandles[2];

   eventHandles[0] = lpDPInfo->hPlayerEvent;
   eventHandles[1] = ghKillReceiveEvent;

   // loop waiting for player events. If the kill event is signaled
   // the thread will exit
   while (WaitForMultipleObjects(2, eventHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
   {
      // receive any messages in the queue
      ReceiveMessage(lpDPInfo);
   }

   ExitThread(0);

   return (0);
}



// funcao que trata mensagens recebidas via DirectPlay
HRESULT ReceiveMessage(LPDPLAYINFO lpDPInfo)
{
   DPID           idFrom, idTo;
   LPVOID            lpvMsgBuffer;
   DWORD          dwMsgBufferSize;
   HRESULT           hr;

   lpvMsgBuffer = NULL;
   dwMsgBufferSize = 0;

   // loop to read all messages in queue
   do
   {
      // loop until a single message is successfully read
      do
      {
         // read messages from any player, including system player
         idFrom = 0;
         idTo = 0;

         hr = lpDPInfo->lpDirectPlay3A->Receive(&idFrom, &idTo, DPRECEIVE_ALL,
                                       lpvMsgBuffer, &dwMsgBufferSize);

         // not enough room, so resize buffer
         if (hr == DPERR_BUFFERTOOSMALL)
         {
            if (lpvMsgBuffer)
               GlobalFreePtr(lpvMsgBuffer);
            lpvMsgBuffer = GlobalAllocPtr(GHND, dwMsgBufferSize);
            if (lpvMsgBuffer == NULL)
               hr = DPERR_OUTOFMEMORY;
         }
      } while (hr == DPERR_BUFFERTOOSMALL);

      if ((SUCCEEDED(hr)) &&                    // successfully read a message
         (dwMsgBufferSize >= sizeof(DPMSG_GENERIC)))  // and it is big enough
      {
         // check for system message
         if (idFrom == DPID_SYSMSG)
         {
            HandleSystemMessage(lpDPInfo, (LPDPMSG_GENERIC) lpvMsgBuffer,
                           dwMsgBufferSize, idFrom, idTo);
         }
         else
         {
            HandleApplicationMessage(lpDPInfo, (LPDPMSG_GENERIC) lpvMsgBuffer,
                               dwMsgBufferSize, idFrom, idTo);
         }
      }
   } while ( SUCCEEDED ( hr ) );

   // free any memory we created
   if ( lpvMsgBuffer )
      GlobalFreePtr ( lpvMsgBuffer );

   return ( DP_OK );
}



/* Recebe mensagens envidas pelo jogo
**********************************************************/
void HandleApplicationMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
                       DPID idFrom, DPID idTo)
{
   LPSTR lpszStr = NULL;
   HRESULT  hr;

   // interpreta as mensagens recebidas
   switch (lpMsg->dwType) {
   
      case APPMSG_CHATSTRING:
         {
           // cliente ou servidor recebem mensagem com chat

           LPMSG_CHATSTRING   lp = (LPMSG_CHATSTRING) lpMsg;

           // create string to display
           hr = NewChatString(lpDPInfo->lpDirectPlay3A, idFrom, lp->szMsg, &lpszStr);
           if FAILED(hr)
              break;
         }
         break;

      case APPMSG_DADOS_SERVIDOR:
         {
            // cliente recebe dados do servidor:
            // semente, personagem, eventos

            LPMSG_CHATSTRING
               lpMsgApl = (LPMSG_CHATSTRING) lpMsg;

            tagEventoTempo
               *pstEventos = (tagEventoTempo*) ( ( (DWORD*) lpMsgApl->szMsg ) + 2 );

            oJOGO.mIniciaPartidaCliente ( ( (DWORD*) lpMsgApl->szMsg ) [ 0 ],
                                          ( (DWORD*) lpMsgApl->szMsg ) [ 1 ],
                                          pstEventos );
         }
         break;

      case APPMSG_DADOS_CLIENTE:
         {
            // servidor recebe ID do personagem do cliente

            LPMSG_CHATSTRING
               lpMsgApl = (LPMSG_CHATSTRING) lpMsg;                         

            oJOGO.m_dwQuemJogador2 = * ( (DWORD*) lpMsgApl->szMsg );
            bCLIENTE_ESCOLHEU_PERSONAGEM = TRUE;
            oJOGO.mAlteraBmpsPersonagens ( oJOGO.m_dwQuemJogador1, oJOGO.m_dwQuemJogador2 );

         }
         break;

      case APPMSG_SERVIDOR_TERMINOU_PARTIDA:
         // cliente recebe aviso de partida terminada
      
         oJOGO.mTerminaPartida ();
         break;
      
      case APPMSG_TERMINEI_EXECUCAO: 
         // alguem terminou conexao (e aplicacao)
         
         // avisa, fecha conexao
         ::MessageBox ( oJOGO.m_oJanPrincipal.m_hWnd, STR_OUTRO_TERMINOU, STR_TITULO_CAIXA_MSG, MB_ICONINFORMATION | MB_OK );
         Desconectar ();

         // termina esta aplicacao
         // nao termina - PostMessage ( oJOGO.m_oJanPrincipal.m_hWnd, WM_CLOSE, 0, 0 );
         break;

      case APPMSG_COLEGA_COMANDOU:
         // colega pressionou um controle de ferramenta
         // (cliente ou servidor podem receber)
         {
            LPMSG_CHATSTRING
               lpMsgApl = (LPMSG_CHATSTRING) lpMsg;

            oJOGO.mExecutaAcaoBotao ( *( (DWORD*) lpMsgApl->szMsg ),
                                      oJOGO.m_dwQuemJogador2,
                                      oJOGO.m_adwNumFerramentas2 );
         }
         break;

      case APPMSG_SERVIDOR_DESISTIU_ESPERA_PERSONAGEM:
         // servidor desistiu de esperar escolha de personagem
         // do cliente (cliente recebe aviso)

         bSERVIDOR_DESISTIU_DE_ESPERAR_PERSONAGEM = TRUE;
         break;   

         
      case APPMSG_DADOS_OBJETOS_GRUPO:
         // servidor enviou posicao dos objetos de um grupo
         // (cliente deve atualisar posicao dos objetos do grupo)
         {
            LPMSG_CHATSTRING
               lpMsgApl = (LPMSG_CHATSTRING) lpMsg;

            // numero de objetos
            DWORD
               dwNumObjetos = (DWORD) ( ( (float*) lpMsgApl->szMsg ) [ 0 ] ),
               dwGrupo = (DWORD) ( ( (float*) lpMsgApl->szMsg ) [ 1 ] );

            // procura objetos no grupo que devem ser alterados
            DWORD
               dwPos = 0;   // qual elemento da mensagem serah usado
            for ( int ncO = 0; ncO < dwNumObjetos; ncO ++ ) {

               // qual objeto
               DWORD
                  dwNdxObjeto = ( (float*) lpMsgApl->szMsg ) [ ncO * 4 + 2 + 0 ];

               // verifica se existe objeto de fato
               if ( oJOGO.m_oCen.m_lpFrObjetos [ dwGrupo ] [ dwNdxObjeto ] == NULL ) {
                  // se nao tem informa bug
                  MSGSTR ( "Bug - dpcomu.cpp - HandleAplicationMessage - APPMSG_DADOS_OBJETOS_GRUPO" );
                  break;
               }

               // altera posicao
               oJOGO.m_oCen.mMudaPosicaoObjeto ( dwGrupo, dwNdxObjeto,
                                                 ( (float*) lpMsgApl->szMsg ) [ ncO * 4 + 2 + 1 ],
                                                 ( (float*) lpMsgApl->szMsg ) [ ncO * 4 + 2 + 2 ],
                                                 ( (float*) lpMsgApl->szMsg ) [ ncO * 4 + 2 + 3 ]  );
            }
         }
         break;
   }

   // post string to chat window
   if (lpszStr) {

      // make sure window is still valid
      if (hCHATWND)
         PostMessage(hCHATWND, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
      else
         GlobalFreePtr(lpszStr);
   }
}



/* Recebe mensagens envidas pelo direct play
**********************************************************/
void HandleSystemMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
                   DPID idFrom, DPID idTo)
{
   LPSTR    lpszStr = NULL;

    // The body of each case is there so you can set a breakpoint and examine
    // the contents of the message received.
   switch (lpMsg->dwType) {

      case DPSYS_CREATEPLAYERORGROUP:      // cliente conectou
           {

            // allocate space for string
            lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(STR_INICIOU_LIGACAO) + 1);
            if (lpszStr == NULL)
               break;

            // build string
            strcpy(lpszStr, STR_INICIOU_LIGACAO);

            bCLIENTE_CONECTOU = TRUE;

           }
         break;

      case DPSYS_DESTROYPLAYERORGROUP:     // cliente terminou conexao
           {

            // allocate space for string
            lpszStr = (LPSTR) GlobalAllocPtr(GHND, strlen(STR_TERMINOU_LIGACAO) + 1);
            if (lpszStr == NULL)
               break;

            // build string
            strcpy(lpszStr, STR_TERMINOU_LIGACAO);

            bCLIENTE_CONECTOU = FALSE;
           }
         break;

      case DPSYS_ADDPLAYERTOGROUP:
           {
               LPDPMSG_ADDPLAYERTOGROUP lp = (LPDPMSG_ADDPLAYERTOGROUP)lpMsg;
           }
         break;

      case DPSYS_DELETEPLAYERFROMGROUP:
           {
               LPDPMSG_DELETEPLAYERFROMGROUP lp = (LPDPMSG_DELETEPLAYERFROMGROUP)lpMsg;
           }
         break;

      case DPSYS_SESSIONLOST:
           {
               LPDPMSG_SESSIONLOST lp = (LPDPMSG_SESSIONLOST)lpMsg;
           }
         break;

      case DPSYS_HOST:        // cliente passou a ser servidor (servidor terminou conexao)
         {
            // allocate space for string
            lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(STR_VOCE_SERVIDOR) + 1);
            if (lpszStr == NULL)
               break;

            // build string
            lstrcpy(lpszStr, STR_VOCE_SERVIDOR);

            // we are now the host
            lpDPInfo->bIsHost = TRUE;
         }
         break;

      case DPSYS_SETPLAYERORGROUPDATA:
           {
               LPDPMSG_SETPLAYERORGROUPDATA lp = (LPDPMSG_SETPLAYERORGROUPDATA)lpMsg;
           }
         break;

      case DPSYS_SETPLAYERORGROUPNAME:
           {
               LPDPMSG_SETPLAYERORGROUPNAME lp = (LPDPMSG_SETPLAYERORGROUPNAME)lpMsg;
           }
         break;
   }

   // post string to chat window
   if (lpszStr) {

      // make sure window is still valid
      if (hCHATWND)
         PostMessage(hCHATWND, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
      else
         GlobalFreePtr(lpszStr);
   }
}



// nome do chat
HRESULT GetChatPlayerName(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
                    LPDPNAME *lplpName)
{
   LPDPNAME lpName = NULL;
   DWORD    dwNameSize;
   HRESULT     hr;

   // get size of player name data
   hr = lpDirectPlay3A->GetPlayerName(dpidPlayer, NULL, &dwNameSize);
   if (hr != DPERR_BUFFERTOOSMALL)
      goto FAILURE;

   // make room for it
   lpName = (LPDPNAME) GlobalAllocPtr(GHND, dwNameSize);
   if (lpName == NULL)
   {
      hr = DPERR_OUTOFMEMORY;
      goto FAILURE;
   }

   // get player name data
   hr = lpDirectPlay3A->GetPlayerName(dpidPlayer, lpName, &dwNameSize);
   if FAILED(hr)
      goto FAILURE;

   // return pointer to name structure
   *lplpName = lpName;

   return (DP_OK);

FAILURE:
   if (lpName)
      GlobalFreePtr(lpName);

   return (hr);
}



// envia uma mensagem de chat para colega
HRESULT SendChatMessage(HWND hWnd, LPDPLAYINFO lpDPInfo)
{
   LPSTR          lpszChatStr = NULL;
   LPSTR          lpszStr = NULL;
   LPMSG_CHATSTRING  lpChatMessage = NULL;
   DWORD          dwChatMessageSize;
   LONG           lStrLen;
   HRESULT           hr;

   // get length of item text
   lStrLen = SendDlgItemMessage(hWnd, IDC_SENDEDIT, WM_GETTEXTLENGTH,
                        (WPARAM) 0, (LPARAM) 0);

   // make room for it
   lpszChatStr = (LPSTR) GlobalAllocPtr(GHND, lStrLen + 1);
   if (lpszChatStr == NULL)
   {
      hr = DPERR_OUTOFMEMORY;
      goto FAILURE;
   }

   // get item text
   lStrLen = GetDlgItemText(hWnd, IDC_SENDEDIT, lpszChatStr, lStrLen + 1);

   // create string to display this text
   hr = NewChatString(lpDPInfo->lpDirectPlay3A, lpDPInfo->dpidPlayer, lpszChatStr, &lpszStr);
   if FAILED(hr)
      goto FAILURE;

   // display this string
   PostMessage(hWnd, WM_USER_ADDSTRING, (WPARAM) 0, (LPARAM) lpszStr);
   lpszStr = NULL;                  // set to NULL so we don't delete it below

   // create space for message plus string (string length included in message header)
   dwChatMessageSize = sizeof(MSG_CHATSTRING) + lstrlen(lpszChatStr);
   lpChatMessage = (LPMSG_CHATSTRING) GlobalAllocPtr(GHND, dwChatMessageSize);
   if (lpChatMessage == NULL)
   {
      hr = DPERR_OUTOFMEMORY;
      goto FAILURE;
   }

   // build message  
   lpChatMessage->dwType = APPMSG_CHATSTRING;
   lstrcpy(lpChatMessage->szMsg, lpszChatStr);
   
   // send this string to all other players
   hr = lpDPInfo->lpDirectPlay3A->Send(lpDPInfo->dpidPlayer, DPID_ALLPLAYERS,
                              DPSEND_GUARANTEED, lpChatMessage, dwChatMessageSize);
   if FAILED(hr)
      goto FAILURE;

FAILURE:
   if (lpszChatStr)
      GlobalFreePtr(lpszChatStr);

   if (lpszStr)
      GlobalFreePtr(lpszStr);

   if (lpChatMessage)
      GlobalFreePtr(lpChatMessage);

   SetDlgItemText(hWnd, IDC_SENDEDIT, "");

   return (hr);
}



/* Envia uma mensagem.
dwTipo - qual o tipo de mensagem
dwComprimento - tamanho da mensagem
pvMsg - ponteiro para buffer que contem a mensagem */
HRESULT SendGeneralMessage( DWORD dwTipo, DWORD dwComprimento, void* pvMsg )
{
   HRESULT           hr;
   DWORD
      dwMessageSize;
   LPSTR
      lpszMsg;
   LPMSG_CHATSTRING
      lpMessage;

   // create space for message plus string (string length included in message header)
   dwMessageSize = sizeof(MSG_CHATSTRING) + dwComprimento;
   lpMessage = (LPMSG_CHATSTRING) GlobalAllocPtr(GHND, dwMessageSize);
   if (lpMessage == NULL)
   {
      hr = DPERR_OUTOFMEMORY;
      goto PAU;
   }

   // build message  
   lpMessage->dwType = dwTipo;
   memcpy(lpMessage->szMsg, pvMsg,dwComprimento);

   // send this string to all other players
   hr = DPInfo.lpDirectPlay3A->Send(DPInfo.dpidPlayer, DPID_ALLPLAYERS,
                              DPSEND_GUARANTEED, lpMessage, dwMessageSize);
   if FAILED(hr)
      goto PAU;

PAU:
   //if (lpszMsg)
      //GlobalFreePtr(lpszMsg);

   if (lpMessage)
      GlobalFreePtr(lpMessage);

   return (hr);
}



// Aloca mensagem para uma mensagem a ser exibida na janela
HRESULT NewChatString(LPDIRECTPLAY3A lpDirectPlay3A, DPID dpidPlayer,
                 LPSTR lpszMsg, LPSTR *lplpszStr)
{
   LPSTR    lpszStr = NULL;
   LPSTR    szDisplayFormat = "\r\n- %s";
   HRESULT     hr;

   // allocate space for display string
   lpszStr = (LPSTR) GlobalAllocPtr(GHND, lstrlen(szDisplayFormat) +
                                 lstrlen(lpszMsg) + 1);
   if (lpszStr == NULL)
   {
      hr = DPERR_OUTOFMEMORY;
      goto FAILURE;
   }

   // build string
   wsprintf(lpszStr, szDisplayFormat, lpszMsg);

   *lplpszStr = lpszStr;
   lpszStr = NULL;

FAILURE:
   if (lpszStr)
      GlobalFreePtr(lpszStr);
   
   return (hr);
}



// erro
void ErrorBox(LPSTR lpszErrorStr, HRESULT hr)
{
   char  szStr[MAXSTRLEN];

   wsprintf(szStr, lpszErrorStr, hr);

   MessageBox(NULL, szStr, STR_ERRO, MB_OK);
}



/* Servidor usa para enviar a posicao dos objetos do
cenario para o cliente.
dwGrupo - objetos de grupo enviar
**********************************************************/
void EnviaObjetosGrupo ( DWORD dwGrupo )
{
/* REVER
   
   // define numero total de objetos a enviar
   DWORD
      dwNumObjetos = oJOGO.m_oCen.mRetNumeroObjetos ( dwGrupo );

   // formato da mensagem:
   // float - numero de objetos que estao sendo enviados
   // float - numero do grupo
   // array, cada elemento contem informacao sobre um objeto:
   //     float - indice do objeto dentro do grupo
   //     float - posicao X
   //     float - posicao Y
   //     float - posicao Z

   // foram usados floats para facilitar o acesso aos dados
   
   // aloca memoria para envio dos objetos:
   void
      *pvMsg;
   DWORD
      dwTamanho = sizeof ( float ) +   // numero objetos
                  sizeof ( float ) +   // qual grupo
                  ( sizeof ( float ) +
                    sizeof ( float ) +
                    sizeof ( float ) +
                    sizeof ( float )   ) *
                  dwNumObjetos;
   pvMsg = GlobalAllocPtr ( GHND, dwTamanho );

   // prepara numero de objetos e grupo
   ( (float*) pvMsg ) [ 0 ] = (float) dwNumObjetos;
   ( (float*) pvMsg ) [ 1 ] = (float) dwGrupo;

   // prepara objetos no grupo
   DWORD
      dwPos = 0;   // qual elemento da mensagem serah preenchido
   for ( int ncO = 0; ncO < MAX_OBJETOS; ncO ++ )

      // procura por algum frame com objeto
      if ( oJOGO.m_oCen.m_lpFrObjetos [ dwGrupo ] [ ncO ] != NULL &&   // frame com objeto
           dwPos < dwNumObjetos ) {                                    // ainda tem espaco para informar
         
         // qual objeto
         ( (float*) pvMsg ) [ 2 + dwPos * 4 + 0 ] = (float) ncO;

         // posicao
         float
            fX, fY, fZ;
         oJOGO.m_oCen.mPegaPosicaoObjeto ( dwGrupo, ncO, fX, fY, fZ );
         ( (float*) pvMsg ) [ 2 + dwPos * 4 + 1 ] = fX;
         ( (float*) pvMsg ) [ 2 + dwPos * 4 + 2 ] = fY;
         ( (float*) pvMsg ) [ 2 + dwPos * 4 + 3 ] = fZ;

         // proximo elemento
         dwPos ++;
      }

   // manda
   SendGeneralMessage ( APPMSG_DADOS_OBJETOS_GRUPO, dwTamanho, pvMsg );
   
   // libera
   GlobalFreePtr ( pvMsg );
*/
}
