/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/////////////////////////////////////////////////////////////////////////EA-V1
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/debug/netserv/netserv.cpp $
// $Author: mhoffe $
// $Revision: #1 $
// $DateTime: 2003/07/03 11:55:26 $
//
// ©2003 Electronic Arts
//
// Simple console based server for NET I/O connections
//////////////////////////////////////////////////////////////////////////////
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include
#include
// Note: This implementation is quick'n'ugly. I've developed this
// program basically just for testing the net I/O class.
static char m_input[256];
static unsigned m_inputUsed;
static void InitConsole(void)
{
AllocConsole();
HANDLE h=GetStdHandle(STD_INPUT_HANDLE);
SetConsoleMode(h,0);
// make screen buffer same size as currently displayed area
// (prevents that our input line gets scrolled out of view)
h=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(h,&info);
COORD newSize;
newSize.X=info.srWindow.Right+1;
newSize.Y=info.srWindow.Bottom+1;
SetConsoleScreenBufferSize(h,newSize);
// hide cursor
CONSOLE_CURSOR_INFO ci;
ci.dwSize=1;
ci.bVisible=FALSE;
SetConsoleCursorInfo(h,&ci);
}
static char *InputConsole(void)
{
// update our input buffer
HANDLE h=GetStdHandle(STD_INPUT_HANDLE);
bool returnChars=false;
for (;;)
{
DWORD dwRecords;
if (!GetNumberOfConsoleInputEvents(h,&dwRecords))
break;
if (!dwRecords)
break;
INPUT_RECORD record;
ReadConsoleInput(h,&record,1,&dwRecords);
if (record.EventType!=KEY_EVENT)
continue;
KEY_EVENT_RECORD &key=record.Event.KeyEvent;
if (!key.bKeyDown||!key.uChar.AsciiChar)
continue;
if (key.uChar.AsciiChar=='\r'||
key.uChar.AsciiChar=='\n')
{
m_input[m_inputUsed++]='\n';
returnChars=true;
break;
}
/// @todo_opt if somebody wants this can be improved by adding support for cursor keys, history, etc
if (key.uChar.AsciiChar=='\b')
{
if (m_inputUsed)
m_inputUsed--;
}
else if (((unsigned char)key.uChar.AsciiChar)>=' ')
{
if (m_inputUsed1)
{
m_input[--m_inputUsed]=0;
m_inputUsed=0;
return m_input;
}
return 0;
}
class Pipe
{
Pipe(const Pipe&);
Pipe& operator=(const Pipe&);
HANDLE m_pipe;
bool m_connected;
int m_state;
int m_stringType;
int m_len;
char *m_src;
char *m_str;
public:
Pipe(void):
m_pipe(INVALID_HANDLE_VALUE),
m_src(NULL), m_str(NULL), m_stringType(0)
{
}
~Pipe()
{
if (m_pipe!=INVALID_HANDLE_VALUE)
{
DisconnectNamedPipe(m_pipe);
CloseHandle(m_pipe);
free(m_src);
free(m_str);
}
}
bool Create(const char *name)
{
m_pipe=CreateNamedPipe(name,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_NOWAIT,
PIPE_UNLIMITED_INSTANCES,1024,1024,0,NULL);
m_connected=false;
return m_pipe!=INVALID_HANDLE_VALUE;
}
bool Connected(void)
{
if (!m_connected)
{
ConnectNamedPipe(m_pipe,NULL);
if (GetLastError()==ERROR_PIPE_CONNECTED)
{
DWORD dwDummy;
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),"\n\n",11,&dwDummy,NULL);
m_connected=true;
m_state=0;
}
}
return m_connected;
}
void Write(char msg)
{
DWORD dummy;
if (!WriteFile(m_pipe,&msg,1,&dummy,NULL)||!dummy)
{
char sp[30];
wsprintf(sp,"%c:%i/%i\n",msg,dummy,GetLastError());
DWORD dwDummy;
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),sp,strlen(sp),&dwDummy,NULL);
}
}
const char *Read(void)
{
DWORD read;
switch(m_state)
{
case 0:
if (!ReadFile(m_pipe,&m_stringType,1,&read,NULL))
break;
if (read==1)
m_state++;
return NULL;
case 1:
case 3:
if (!ReadFile(m_pipe,&m_len,4,&read,NULL))
break;
if (read==4)
{
if (m_state==1)
m_src=(char *)realloc(m_src,m_len+1);
else
m_str=(char *)realloc(m_str,m_len+1);
m_state++;
}
return NULL;
case 2:
if (!ReadFile(m_pipe,m_src,m_len,&read,NULL))
break;
if (read==m_len)
{
m_src[m_len]=0;
m_state++;
}
return NULL;
case 4:
if (!ReadFile(m_pipe,m_str,m_len,&read,NULL))
break;
if (read==m_len)
{
m_str[m_len]=0;
m_state=0;
return m_str;
}
return NULL;
}
if (GetLastError()==ERROR_BROKEN_PIPE)
{
DisconnectNamedPipe(m_pipe);
DWORD dwDummy;
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),"\n\n",14,&dwDummy,NULL);
m_connected=false;
}
return NULL;
}
};
int CALLBACK WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
InitConsole();
char buf1[200],buf2[400];
DWORD dwDummy=sizeof(buf1);
GetComputerName(buf1,&dwDummy);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),buf2,
wsprintf(buf2,"\n\nSimple debug.net Server ready. Enter 'quit' to exit.\n\nLocal machine: %s\n\n",buf1),
&dwDummy,NULL);
Pipe p[10];
for (int k=0;k<10;k++)
if (!p[k].Create("\\\\.\\pipe\\ea_debug_v1"))
{
char msg[200];
wsprintf(msg,"Can't create named pipe (Code %i).",GetLastError());
MessageBox(NULL,msg,"Error",MB_OK);
return 1;
}
for (;;)
{
char *input=InputConsole();
if (input)
{
if (!strcmp(input,"quit"))
break;
}
for (int k=0;k<10;k++)
{
if (!p[k].Connected())
continue;
const char *msg=p[k].Read();
if (msg)
{
DWORD dwDummy;
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),msg,strlen(msg),&dwDummy,NULL);
}
if (input)
{
for (unsigned i=0;input[i];i++)
p[k].Write(input[i]);
p[k].Write('\n');
}
}
Sleep(10);
}
return 0;
}