Initial commit of Command & Conquer Renegade source code.

This commit is contained in:
LFeenanEA
2025-02-27 16:39:46 +00:00
parent 74ab8fa5e0
commit 58ed459113
4918 changed files with 1366710 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
// RenegadeGR.cpp : Defines the entry point for the DLL application.
//
#include <malloc.h>
#include <string.h>
#include "jni.h"
#include "RenegadeGR.h"
#include "RenegadeNet.h"
#include "rengameres.h"
#include "tcpmgr.h"
#include "tcpcon.h"
JNIEXPORT jint JNICALL Java_RenegadeNet__1nativeSendGR
(JNIEnv* env, jclass cls, jobjectArray loginArray, jdoubleArray scoreArray)
{
int i = 0;
char** packetLogins = NULL;
double* packetScores = NULL;
// Build packetLogins array
jsize loginlen = env->GetArrayLength(loginArray);
packetLogins = new char*[loginlen];
for(i = 0; i < loginlen; i++)
{
jstring jstr = (jstring)env->GetObjectArrayElement(loginArray, i);
const char *str = env->GetStringUTFChars(jstr, 0);
packetLogins[i] = new char[strlen(str)+1];
strcpy(packetLogins[i], str);
env->ReleaseStringUTFChars(jstr, str);
}
// Build packetScores array
jsize scorelen = env->GetArrayLength(scoreArray);
jdouble *jscores = env->GetDoubleArrayElements(scoreArray, 0);
packetScores = new double[scorelen];
for (i = 0; i < scorelen; i++)
packetScores[i] = jscores[i];
env->ReleaseDoubleArrayElements(scoreArray, jscores, 0);
RenegadeGameRes renegadeGameRes("10.2.20.28", 4850);
//RenegadeGameRes renegadeGameRes;
renegadeGameRes.setGameID(21450402);
renegadeGameRes.setPlayerCount((unsigned char)loginlen);
renegadeGameRes.setClanGame(0);
renegadeGameRes.setDuration(100);
renegadeGameRes.setMapName("Renegade Map");
renegadeGameRes.setSKU(GR_ENGLISH);
renegadeGameRes.setStyle(GR_DEATHMATCH);
renegadeGameRes.setNumClans(0);
renegadeGameRes.setStartTime(100);
renegadeGameRes.setTournament(1);
for(i = 0; i < loginlen; i++)
{
renegadeGameRes.addPlayer(packetLogins[i], packetScores[i], 0, 245, 0, 5, 5, 3, 244);
}
int sendlen = renegadeGameRes.sendResults();
delete[] packetScores;
for(i = 0; i < loginlen; i++)
delete[] (packetLogins[i]);
delete[] packetLogins;
return sendlen;
}
JNIEXPORT void JNICALL Java_RenegadeNet_startWinSock(JNIEnv *, jclass)
{
WORD wVersionRequested;
WSADATA wsaData;
int err = 1;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup( wVersionRequested, &wsaData);
/*if (err != 0)
{
LOG_END("failed");
//assert(false);
}
else
{
LOG_END("ok");
}*/
}
JNIEXPORT void JNICALL Java_RenegadeNet_stopWinSock(JNIEnv *, jclass)
{
WSACleanup();
}

View File

@@ -0,0 +1,175 @@
# Microsoft Developer Studio Project File - Name="RenegadeGR" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=RenegadeGR - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "RenegadeGR.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "RenegadeGR.mak" CFG="RenegadeGR - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "RenegadeGR - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "RenegadeGR - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "RenegadeGR - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RENEGADEGR_EXPORTS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\\" /I "..\wlib" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RENEGADEGR_EXPORTS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /machine:I386
!ELSEIF "$(CFG)" == "RenegadeGR - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RENEGADEGR_EXPORTS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\\" /I "..\wlib" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RENEGADEGR_EXPORTS" /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "RenegadeGR - Win32 Release"
# Name "RenegadeGR - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\field.cpp
# End Source File
# Begin Source File
SOURCE=.\packet.cpp
# End Source File
# Begin Source File
SOURCE=.\RenegadeGR.cpp
# End Source File
# Begin Source File
SOURCE=.\rengameres.cpp
# End Source File
# Begin Source File
SOURCE=.\tcpcon.cpp
# End Source File
# Begin Source File
SOURCE=.\tcpmgr.cpp
# End Source File
# Begin Source File
SOURCE=.\wencrypt.cpp
# End Source File
# Begin Source File
SOURCE=.\wtime.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\field.h
# End Source File
# Begin Source File
SOURCE=.\jni.h
# End Source File
# Begin Source File
SOURCE=.\packet.h
# End Source File
# Begin Source File
SOURCE=.\RenegadeGR.h
# End Source File
# Begin Source File
SOURCE=.\RenegadeNet.h
# End Source File
# Begin Source File
SOURCE=.\rengameres.h
# End Source File
# Begin Source File
SOURCE=.\tcpcon.h
# End Source File
# Begin Source File
SOURCE=.\tcpmgr.h
# End Source File
# Begin Source File
SOURCE=.\wencrypt.h
# End Source File
# Begin Source File
SOURCE=.\wtime.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

View File

@@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "RenegadeGR"=.\RenegadeGR.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

View File

@@ -0,0 +1,51 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Generic Game Results Server
File Name : RenegadeGR.h
Author : Joe Howes (jhowes@westwood.com)
Start Date : Apr 5, 2000
Last Update :
------------------------------------------------------------------------------
This class deals with the processing of first person shooter games.
\****************************************************************************/
#ifndef __RENEGADEGR_H_
#define __RENEGADEGR_H_
/*----------------------------------------------------------------------.
| MACROS |
`----------------------------------------------------------------------*/
;
/*----------------------------------------------------------------------.
| PROTOTYPES |
`----------------------------------------------------------------------*/
#endif

View File

@@ -0,0 +1,63 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class RenegadeNet */
#ifndef _Included_RenegadeNet
#define _Included_RenegadeNet
#ifdef __cplusplus
extern "C" {
#endif
/* Inaccessible static: threadInitNumber */
/* Inaccessible static: stopThreadPermission */
#undef RenegadeNet_MIN_PRIORITY
#define RenegadeNet_MIN_PRIORITY 1L
#undef RenegadeNet_NORM_PRIORITY
#define RenegadeNet_NORM_PRIORITY 5L
#undef RenegadeNet_MAX_PRIORITY
#define RenegadeNet_MAX_PRIORITY 10L
/*
* Class: RenegadeNet
* Method: startWinSock
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_RenegadeNet_startWinSock
(JNIEnv *, jclass);
/*
* Class: RenegadeNet
* Method: stopWinSock
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_RenegadeNet_stopWinSock
(JNIEnv *, jclass);
/*
* Class: RenegadeNet
* Method: _nativeSendGR
* Signature: ([Ljava/lang/String;[D)I
*/
JNIEXPORT jint JNICALL Java_RenegadeNet__1nativeSendGR
(JNIEnv *, jclass, jobjectArray, jdoubleArray);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,482 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Generic Game Results Server
File Name : wencrypt.cpp
Author : Joe Howes
Start Date : Jul 9, 1999
Last Update : Jul 20, 1999
------------------------------------------------------------------------------
A simple encryption system for game results packets. Specifically designed
to work only with unsigned char buffers of least 15 bytes long.
\****************************************************************************/
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <malloc.h>
#include <winsock2.h>
#include "wencrypt.h"
#include "wnet/packet.h"
/*extern "C" {
extern unsigned char* PrepareEncryptedPacket(unsigned char* packet, int* size);
}*/
/*------------------------------------------------------------------------------.
| FUNCTION: PrepareEncryptedPacket() |
| If an error occurs, a NULL will be returned and the size value will be a |
| negative integer corresponding to an error in wencrypt.h. |
| JUL 20/99: In the Windows WOLAPI, the function RequestGameresSend requires |
| a buffer that can be passed to the constructor of PacketClass. Thus we need |
| to wrap our encrypted buffer in a packet with one big data field and pass |
| it's Comms Packet output back. |
`------------------------------------------------------------------------------*/
unsigned char* PrepareEncryptedPacket(unsigned char* packet, int* size)
{
unsigned char* rlebuf = NULL;
unsigned char* encbuf = NULL;
PacketClass wrap;
static char field[5] = { "CNTL" };
rlebuf = (unsigned char *) malloc((*size)*2);
// Run length encode
RLEncode(packet, rlebuf, size); // Does not allocate any memory
// XOR encrypt
encbuf = SimpleEncrypt(rlebuf, size);
if( rlebuf != NULL ) free(rlebuf);
if( encbuf == NULL ) return NULL; // SimpleEncrypt fills the error in
// Now we have to wrap the encrypted data inside a valid PacketClass blob
wrap.Add_Field((char*)"CNTL", (void*)encbuf, *size);
free(encbuf);
return (unsigned char*)(wrap.Create_Comms_Packet(*size));
}
/*------------------------------------------------------------------------------.
| FUNCTION: DecryptPacket() |
| If an error occurs, a NULL will be returned and the size value will be a |
| negative integer corresponding to an error in wencrypt.h |
`------------------------------------------------------------------------------*/
#ifndef __CLIENT__
unsigned char* DecryptPacket(unsigned char* encbuf, int* size)
{
unsigned char* rlebuf;
unsigned char* packet;
rlebuf = SimpleDecrypt(encbuf, size);
if( rlebuf == NULL )
return NULL; // SimpleDecrypt fills the error in
packet = RLDecode(rlebuf, size); // Allocates memory!!
if( packet == NULL )
return NULL; // RLDecode fills the error in
return packet;
}
#endif
/*------------------------------------------------------------------------------.
| FUNCTION: RLEncode() |
| - src is a buffer conatining the uncompressed data. |
| - dst is a buffer which should be twice the size of the src buffer. |
| - size indicates the size of the source buffer at the start of the function, |
| and is filled with the length of the dest buffer on return. |
| |
| NOTE: As is the nature of RLE, it is possible the "compressed" buffer |
| will be bigger than the uncompressed one by one byte. Actually in the |
| absolute worst case it can be bigger than that if the escape code occurs |
| often enough in the buffer and needs to be stuffed several times. I *think* |
| it's impossible for the compressed buffer to ever get twice as big as the |
| original, being that we choose the lowest frequency value as the escape, but |
| there will never be a memory overrun this way, and gameres packets are tiny. |
`------------------------------------------------------------------------------*/
void RLEncode(unsigned char* src, unsigned char* dst, int* size)
{
int i, j, k;
int frequencies[256];
int lowest;
unsigned char escape;
unsigned char currtally = 1;
unsigned char curr;
// Determine what we should use for the escape code. Ideally it's a value
// that doesn't occur in the buffer, but we'll settle for the value that
// appears the least number of times.
memset(frequencies, 0, 256 * sizeof(int));
for(i = 0; i < *size; frequencies[src[i++]]++);
lowest = frequencies[0];
escape = 0;
for(i = 0; i < 256; i++)
{
if( frequencies[i] == 0 )
{
escape = (unsigned char)i;
break;
}
if( frequencies[i] < lowest )
{
lowest = frequencies[i];
escape = (unsigned char)i;
}
}
dst[0] = escape;
// Build the dest buffer
curr = src[0];
for(i = 1, j = 1; i <= *size; i++)
{
if( src[i] == curr && i < *size )
currtally++;
else
{
if( currtally > 3 )
{
dst[j++] = escape;
dst[j++] = currtally;
dst[j++] = curr;
}
else
{
// Stuff the escape character
for(k = 0; k < currtally; k++)
{
if( curr == escape )
dst[j++] = curr;
dst[j++] = curr;
}
}
curr = src[i];
currtally = 1;
}
}
*size = j;
}
/*------------------------------------------------------------------------------.
| FUNCTION: RLDecode() |
| Nothing special...just remember byte 0 is the escape and not part of the |
| original buffer. If there is an error, the returned pointer will be NULL |
| and the size will contain a negative number indicating the error code. |
`------------------------------------------------------------------------------*/
#ifndef __CLIENT__
unsigned char* RLDecode(unsigned char* src, int* size)
{
int i, j, k;
int dstsize = 0;
unsigned char escape = src[0];
unsigned char* dst;
// How big will the resultant buffer be?
for(i = 1; i < *size; i++)
{
if( src[i] == escape && i < (*size-1) )
{
if( src[i+1] == escape )
{
dstsize++; // Stuffed escape
i++;
}
else
{
dstsize += (int)src[i+1]; // Add the occurences
i += 2; // Skip the token
}
}
else if( src[i] == escape && i == (*size-1) )
{
*size = ERR_SPURIOUS_ESCAPE;
return NULL;
}
else
dstsize++;
}
// Build the new buffer
dst = malloc( dstsize );
j = 0;
for(i = 1; i < *size; i++)
{
if( src[i] == escape && i < (*size-1) )
{
if( src[i+1] == escape )
{
dst[j++] = src[i]; // Stuffed escape
i++;
}
else
{
for(k = 0; k < (int)src[i+1]; k++)
dst[j+k] = src[i+2];
j += k;
i += 2; // Skip the token
}
}
else if( src[i] == escape && i == (*size-1) )
{
*size = ERR_SPURIOUS_ESCAPE;
delete[] dst;
return NULL;
}
else
dst[j++] = src[i];
}
*size = dstsize;
return dst;
}
#endif
/*--------------------------------------------------------------------------------------.
| FUNCTION: ran3() |
| From 'Numerical Recipies In C', Chapter 7, 'Portable Random Number Generators'. |
`--------------------------------------------------------------------------------------*/
float ran3(long* idnum)
{
static int inext, inextp;
static long ma[66];
static int iff = 0;
long mj, mk;
int i, ii, k;
if( *idnum < 0 || iff == 0 )
{
iff = 1;
mj = MSEED - (*idnum < 0 ? -*idnum : *idnum);
mj %= MBIG;
ma[55] = mj;
mk = 1;
for(i = 1; i <= 54; i++)
{
ii = (21*i) % 55;
ma[ii] = mk;
mk = mj - mk;
if( mk < MZ ) mk += MBIG;
mj = ma[ii];
}
for(k = 1; k <=4; k++)
{
for(i = 1; i <= 55; i++)
{
ma[i] -= ma[1+(i+30) % 55];
if( ma[i] < MZ ) ma[i] += MBIG;
}
}
inext = 0;
inextp = 31;
*idnum = 1;
}
if( ++inext == 56 ) inext = 1;
if( ++inextp == 56 ) inextp = 1;
mj = ma[inext] - ma[inextp];
if( mj < MZ ) mj += MBIG;
ma[inext] = mj;
return (float) (mj*FAC );
}
/*------------------------------------------------------------------------------.
| FUNCTION: GenerateKey() |
| Make a key the same length as the buffer. Gameres packets are typically |
| < 600 bytes so period isn't a concern. |
`------------------------------------------------------------------------------*/
void GenerateKey(unsigned char* key, int len, long seed)
{
int i;
float running;
long num;
if( seed > 0 ) seed *= -1; // Initial val must be negative
running = ran3(&seed);
for(i = 0; i < len; i++)
{
num = (long)(running * (float)255.0);
if( num < 0 ) num *= -1;
key[i] = (unsigned char)num;
running = ran3(&seed);
}
}
/*--------------------------------------------------------------.
| FUNCTON: SimpleEncrypt() |
| Ok, not entirely simple but it's not bad. We generate the |
| key, then prepare a new buffer which will contain the |
| encrypted data. Byte 6 of the buffer is a random offset into |
| bytes 10 - 256 which will contain the four byte seed value |
| used to construct the key. |
| If there is an error, the returned pointer will be NULL and |
| the len value will be filled with a negative number |
| indicating the error code. |
`--------------------------------------------------------------*/
unsigned char* SimpleEncrypt(const unsigned char* src, int* len)
{
int i, j;
unsigned char* key;
unsigned char* dst;
unsigned char offset = 0;
long offsetseed = 0;
unsigned long netseed;
long seed;
float limit;
key = (unsigned char *) malloc( *len );
dst = (unsigned char *) malloc( (*len)+5 );
if( *len < 15 )
{
*len = -2;
free( key );
free( dst );
return NULL;
}
// Generate the random stuff
seed = (long)time(NULL);
if( seed > 0 ) seed *= -1; // Don't check for seed == 0 because a ligit
// packet builder should never set a seed of
// 0 and it's another mistake a cracker could
// possibly make.
netseed = htonl((unsigned long)seed);
GenerateKey(key, *len, seed);
// We want the offset to be somewhere between byte 10 and byte (255-14), or
// (*len - 4), whichever is smaller
offsetseed = (long)time(NULL);
if( offsetseed > 0 ) offsetseed *= -1;
limit = ((*len) < 241) ? (float)((*len)-14) : (float)241.0;
offset = (unsigned char)(ran3(&offsetseed) * limit);
offset += (unsigned char)10;
// Construct the buffer
for(i = 0, j = 0; i < *len; i++)
{
if( j == 5 )
dst[j++] = offset;
if( j == (int)offset )
{
memcpy(&dst[j], &netseed, 4);
j += 4;
}
dst[j++] = src[i] ^ key[i];
}
*len += 5;
free( key );
return dst;
}
/*--------------------------------------------------------------.
| FUNCTON: SimpleDecrypt() |
| We look for the offset, grab the seed, generate a key, then |
| decrypt. |
`--------------------------------------------------------------*/
#ifndef __CLIENT__
unsigned char* SimpleDecrypt(unsigned char* src, int* len)
{
int i, j;
unsigned char* key;
unsigned char* dst;
unsigned char offset = 0;
unsigned long netseed;
long seed;
key = malloc( *len );
dst = malloc((*len) - 5 );
if( *len < 20 )
{
*len = -2;
delete[] key;
delete[] dst;
return NULL;
}
// Generate the key
offset = src[5];
memcpy(&netseed, &src[offset], 4);
seed = (long)ntohl(netseed);
if( seed > 0 )
{
*len = -3;
delete[] key;
delete[] dst;
return NULL;
}
GenerateKey(key, (*len)-5, seed);
// Construct the buffer
for(i = 0, j = 0; i < *len; i++)
{
if( i == 5 )
continue;
else if( i >= (int)offset && i <= (int)(offset+(unsigned char)3) )
continue;
else
{
dst[j] = src[i] ^ key[j];
j++;
}
}
*len -= 5;
delete[] key;
return dst;
}
#endif

View File

@@ -0,0 +1,96 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Generic Game Results Server
File Name : wencrypt.h
Author : Joe Howes
Start Date : Jul 9, 1999
Last Update :
------------------------------------------------------------------------------
A simple encryption system for game results packets. Specifically designed
to work only with unsigned char buffers of least 15 bytes long.
\****************************************************************************/
#ifndef __WENCRYPT_H__
#define __WENCRYPT_H__
/*------------------------------------------------------------------------------.
| BUILD TYPE |
| In the interest of smaller code and distributing as little of our |
| encryption system as possible with game CDs, this preprocessor directive |
| should be uncommented when building a client. When uncommented, only those |
| functions relevant to encrypting will be compiled, and those relevant only |
| to decrypting will not. |
`------------------------------------------------------------------------------*/
#define __CLIENT__
/*------------------------------------------------------------------------------.
| PROTOTYPES |
`------------------------------------------------------------------------------*/
#ifndef __CLIENT__
unsigned char* DecryptPacket(unsigned char* encbuf, int* size);
#endif
// These are for munching the data
unsigned char* PrepareEncryptedPacket(unsigned char* packet, int* size);
void RLEncode(unsigned char* src, unsigned char* dst, int* size);
float ran3(long* idnum);
void GenerateKey(unsigned char* key, int len, long seed);
unsigned char* SimpleEncrypt(const unsigned char* src, int* len);
#ifndef __CLIENT__
unsigned char* RLDecode(unsigned char* src, int* size);
unsigned char* SimpleDecrypt(unsigned char* buf, int* len);
#endif
/*------------------------------------------------------------------------------.
| MACROS |
`------------------------------------------------------------------------------*/
// For key generation
#define MBIG 1000000000
#define MSEED 161803398
#define MZ 0
#define FAC (1.0/MBIG)
// Errors
#define ERR_SPURIOUS_ESCAPE -1 // A single escape was found at the end of a compressed
// buffer. Should never happen as escapes in the
// stream are stuffed and should otherwise indicate
// the start of a token.
#define ERR_BUFFER_TOO_SMALL -2 // The buffer being passed to Simple Encrypt is
// smaller than 15 bytes, which is too small to work
// with it's seed-stuffing. A gameres packet should
// never be as small as 15 bytes anyway.
#define ERR_BAD_SEED -3 // The seed value being passed is zero. Not a valid
// seed.
#endif

View File

@@ -0,0 +1,305 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
* *
* Project Name : Westwood Auto Registration App *
* *
* File Name : FIELD.CPP *
* *
* Programmer : Philip W. Gorrow *
* *
* Start Date : 04/22/96 *
* *
* Last Update : April 22, 1996 [PWG] *
* *
* Actual member function for the field class. *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include <string.h>
#include <sys/types.h>
#include <winsock.h>
#include "field.h"
// private member func
void FieldClass::Clear(void)
{
delete[](Data);
strcpy(ID,"");
DataType=0;
Size=0;
Data=NULL;
Next=NULL;
}
FieldClass::FieldClass(char *id, char data)
{
Data=NULL;
Set(id,data);
}
FieldClass::FieldClass(char *id, unsigned char data)
{
Data=NULL;
Set(id,data);
}
FieldClass::FieldClass(char *id, short data)
{
Data=NULL;
Set(id,data);
}
FieldClass::FieldClass(char *id, unsigned short data)
{
Data=NULL;
Set(id,data);
}
FieldClass::FieldClass(char *id, long data)
{
Data=NULL;
Set(id,data);
}
FieldClass::FieldClass(char *id, unsigned long data)
{
Data=NULL;
Set(id,data);
}
FieldClass::FieldClass(char *id, char *data)
{
Data=NULL;
Set(id,data);
}
FieldClass::FieldClass(char *id, void *data, int length)
{
Data=NULL;
Set(id,data,length);
}
void FieldClass::Set(char *id, char data)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_CHAR;
Size = sizeof(data);
Data = new char[Size];
memcpy(Data, &data, Size);
Next = Nextsave;
}
void FieldClass::Set(char *id, unsigned char data)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_UNSIGNED_CHAR;
Size = sizeof(data);
Data = new char[Size];
memcpy(Data, &data, Size);
Next = Nextsave;
}
void FieldClass::Set(char *id, short data)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_SHORT;
Size = sizeof(data);
Data = new char[Size];
memcpy(Data, &data, Size);
Next = Nextsave;
}
void FieldClass::Set(char *id, unsigned short data)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_UNSIGNED_SHORT;
Size = sizeof(data);
Data = new char[Size];
memcpy(Data, &data, Size);
Next = Nextsave;
}
void FieldClass::Set(char *id, long data)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_LONG;
Size = sizeof(data);
Data = new char[Size];
memcpy(Data, &data, Size);
Next = Nextsave;
}
void FieldClass::Set(char *id, unsigned long data)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_UNSIGNED_LONG;
Size = sizeof(data);
Data = new char[Size];
memcpy(Data, &data, Size);
Next = Nextsave;
}
void FieldClass::Set(char *id, char *data)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_STRING;
Size = (unsigned short)(strlen(data)+1);
Data = new char[Size];
memcpy(Data, data, Size);
Next = Nextsave;
}
void FieldClass::Set(char *id, void *data, int length)
{
FieldClass *Nextsave=Next;
Clear();
strncpy(ID, id, sizeof(ID));
DataType = TYPE_CHUNK;
Size = (unsigned short)length;
Data = new char[Size];
memcpy(Data, data, Size);
Next = Nextsave;
}
FieldClass::~FieldClass()
{
Clear();
}
// Fetch the datatype
int FieldClass::Get_Type(void)
{
return(DataType);
}
/**************************************************************************
* PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format *
* *
* INPUT: FIELD * to the data field we need to convert *
* *
* OUTPUT: none *
* *
* HISTORY: *
* 04/22/1996 PWG : Created. *
*========================================================================*/
void FieldClass::Host_To_Net(void)
{
//
// Before we convert the data type, we should convert the actual data
// sent.
//
switch (DataType) {
case TYPE_CHAR:
case TYPE_UNSIGNED_CHAR:
case TYPE_STRING:
break;
case TYPE_SHORT:
case TYPE_UNSIGNED_SHORT:
*((unsigned short *)Data) = htons(*((unsigned short *)Data));
break;
case TYPE_LONG:
case TYPE_UNSIGNED_LONG:
*((unsigned long *)Data) = htonl(*((unsigned long *)Data));
break;
//
// Might be good to insert some type of error message here for unknown
// datatypes -- but will leave that for later.
//
default:
break;
}
//
// Finally convert over the data type and the size of the packet.
//
DataType = htons(DataType);
Size = htons(Size);
}
/**************************************************************************
* PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format *
* *
* INPUT: FIELD * to the data field we need to convert *
* *
* OUTPUT: none *
* *
* HISTORY: *
* 04/22/1996 PWG : Created. *
*========================================================================*/
void FieldClass::Net_To_Host(void)
{
//
// Finally convert over the data type and the size of the packet.
//
DataType = ntohs(DataType);
Size = ntohs(Size);
//
// Before we convert the data type, we should convert the actual data
// sent.
//
switch (DataType) {
case TYPE_CHAR:
case TYPE_UNSIGNED_CHAR:
case TYPE_STRING:
break;
case TYPE_SHORT:
case TYPE_UNSIGNED_SHORT:
*((unsigned short *)Data) = ntohs(*((unsigned short *)Data));
break;
case TYPE_LONG:
case TYPE_UNSIGNED_LONG:
*((unsigned long *)Data) = ntohl(*((unsigned long *)Data));
break;
//
// Might be good to insert some type of error message here for unknown
// datatypes -- but will leave that for later.
//
default:
break;
}
}

View File

@@ -0,0 +1,99 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
* *
* Project Name : Westwood Auto Registration App *
* *
* File Name : FIELD.H *
* *
* Programmer : Philip W. Gorrow *
* *
* Start Date : 04/22/96 *
* *
* Last Update : April 22, 1996 [PWG] *
* *
* This module takes care of maintaining the field list used to process *
* packets. *
* *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2))
#define TYPE_CHAR 1
#define TYPE_UNSIGNED_CHAR 2
#define TYPE_SHORT 3
#define TYPE_UNSIGNED_SHORT 4
#define TYPE_LONG 5
#define TYPE_UNSIGNED_LONG 6
#define TYPE_STRING 7
#define TYPE_CHUNK 20
class PacketClass;
class FieldClass
{
public:
friend PacketClass;
//
// Define constructors to be able to create all the different kinds
// of fields.
//
FieldClass(void) {};
FieldClass(char *id, char data);
FieldClass(char *id, unsigned char data);
FieldClass(char *id, short data);
FieldClass(char *id, unsigned short data);
FieldClass(char *id, long data);
FieldClass(char *id, unsigned long data);
FieldClass(char *id, char *data);
FieldClass(char *id, void *data, int length);
~FieldClass();
// Change the field contents
void Set(char *id, char data);
void Set(char *id, unsigned char data);
void Set(char *id, short data);
void Set(char *id, unsigned short data);
void Set(char *id, long data);
void Set(char *id, unsigned long data);
void Set(char *id, char *data);
void Set(char *id, void *data, int length);
int Get_Type(void); // get the datatype of this field
unsigned short Get_Size(void) { return Size; }
void Host_To_Net(void);
void Net_To_Host(void);
private:
void Clear(void); // dealloc mem & zero safely
char ID[4]; // id value of this field
unsigned short DataType; // id of the data type we are using
unsigned short Size; // size of the data portion of this field
void *Data; // pointer to the data portion of this field
FieldClass *Next; // pointer to the next field in the field list
};

View File

@@ -0,0 +1,494 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
* *
* Project Name : Westwood Auto Registration App *
* *
* File Name : PACKET.CPP *
* *
* Programmer : Philip W. Gorrow *
* *
* Start Date : 04/22/96 *
* *
* Last Update : April 24, 1996 [PWG] *
* *
*-------------------------------------------------------------------------*
* Functions: *
* *PacketClass::Find_Field -- Finds a field if it exists in the packets *
* Get_Field -- Find specified name and returns data *
* PacketClass::~PacketClass -- destroys a packet class be freeing list *
* PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <winsock.h>
#include "packet.h"
/**************************************************************************
* PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* HISTORY: *
* 04/24/1996 PWG : Created. *
*========================================================================*/
PacketClass::~PacketClass()
{
FieldClass *current;
FieldClass *next;
//
// Loop through the entire field list and delete each entry.
//
for (current = Head; current; current = next) {
next = current->Next;
delete(current);
}
}
/**************************************************************************
* PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li *
* *
* INPUT: FieldClass * - a properly constructed field class entry. *
* *
* OUTPUT: none *
* *
* HISTORY: *
* 04/24/1996 PWG : Created. *
*========================================================================*/
void PacketClass::Add_Field(FieldClass *field)
{
field->Next = Head;
Head = field;
}
/**************************************************************************
* PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 04/22/1996 PWG : Created. *
*========================================================================*/
PacketClass::PacketClass(char *curbuf)
{
int remaining_size;
//
// Pull the size and packet ID out of the linear packet stream.
//
Size = *((unsigned short *)curbuf);
curbuf += sizeof(unsigned short);
Size = ntohs(Size);
ID = *((short *)curbuf);
curbuf += sizeof(unsigned short);
ID = ntohs(ID);
Head = NULL;
//
// Calculate the remaining size so that we can loop through the
// packets and extract them.
//
remaining_size = Size - 4;
//
// Loop through the linear packet until we run out of room and
// create a field for each.
//
while (remaining_size > 0)
{
FieldClass *field = new FieldClass;
//
// Copy the adjusted header into the buffer and then advance the buffer
//
memcpy(field, curbuf, FIELD_HEADER_SIZE);
curbuf += FIELD_HEADER_SIZE;
remaining_size -= FIELD_HEADER_SIZE;
//
// Copy the data into the buffer
//
int size = ntohs(field->Size);
field->Data = new char[size];
memcpy(field->Data, curbuf, size);
curbuf += size;
remaining_size -= size;
//
// Make sure we allow for the pad bytes.
//
int pad = (4 - (ntohs(field->Size) & 3)) & 3;
curbuf += pad;
remaining_size -= pad;
//
// Convert the field back to the host format
//
field->Net_To_Host();
//
// Finally add the field to the field list in the packet
// structure.
//
Add_Field(field);
}
}
/**************************************************************************
* CREATE_COMMS_PACKET -- Walks field list creating a packet *
* *
* INPUT: short - the id of the packet so the server can identify it *
* unsigned short & - the size of the packet returned here *
* *
* OUTPUT: void * pointer to the linear packet data *
* *
* WARNINGS: This routine allocates memory that the user is responsible *
* for freeing. *
* *
* HISTORY: *
* 04/22/1996 PWG : Created. *
*========================================================================*/
char *PacketClass::Create_Comms_Packet(int &size)
{
FieldClass *current;
//
// Size starts at four because that is the size of the packet header.
//
size = 4;
//
// Take a quick spin through and calculate the size of the packet we
// are building.
//
for (current = Head; current; current=current->Next)
{
size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size
size += current->Size; // add in data size
size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet
}
//
// Now that we know the size allocate a buffer big enough to hold the
// packet.
//
char *retval = new char[size];
char *curbuf = retval;
//
// write the size into the packet header
//
*((unsigned short *)curbuf) = (unsigned short)htons(size);
curbuf += sizeof(unsigned short);
*((short *)curbuf) = htons(ID);
curbuf += sizeof(unsigned short);
//
// Ok now that the actual header information has been written we need to write out
// field information.
//
for (current = Head; current; current = current->Next)
{
//
// Temporarily convert the packet to net format (this saves alot of
// effort, and seems safe...)
//
current->Host_To_Net();
//
// Copy the adjusted header into the buffer and then advance the buffer
//
memcpy(curbuf, current, FIELD_HEADER_SIZE);
curbuf += FIELD_HEADER_SIZE;
//
// Copy the data into the buffer and then advance the buffer
//
memcpy(curbuf, current->Data, ntohs(current->Size));
curbuf += ntohs(current->Size);
//
// Finally take care of any pad bytes by setting them to 0
//
int pad = (4 - (ntohs(current->Size) & 3)) & 3;
curbuf += pad;
current->Net_To_Host();
}
return(retval);
}
/**************************************************************************
* PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets *
* *
* INPUT: char * - the id of the field we are looking for. *
* *
* OUTPUT: FieldClass * pointer to the field class *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
FieldClass *PacketClass::Find_Field(char *id)
{
for (FieldClass *current = Head; current; current = current->Next)
{
if ( strncmp(id, current->ID, 4) == 0)
return current;
}
return NULL;
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* char & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, char &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((char *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* unsigned char & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, unsigned char &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned char *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* short & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, short &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((short *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* unsigned short & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, unsigned short &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned short *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* long & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, long &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((long *)field->Data);
}
return((field) ? true : false);
}
bit8 PacketClass::Get_Field(char *id, int &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((int *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data as a string *
* *
* INPUT: char * - the id of the field that holds the data. *
* char * - the string to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The string is not changed if the field is not found. It *
* is assumed that the string variabled specified by the *
* pointer is large enough to hold the data. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, char *data)
{
FieldClass *field = Find_Field(id);
if (field) {
strcpy(data, (char *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* unsigned long & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, unsigned long &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned long *)field->Data);
}
return((field) ? true : false);
}
bit8 PacketClass::Get_Field(char *id, unsigned &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* void * - the reference to store the data into *
* int - the length of the buffer passed in *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 6/4/96 4:46PM ST : Created *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, void *data, int &length)
{
FieldClass *field = Find_Field(id);
if (field) {
memcpy (data, field->Data, MIN(field->Size, length));
length = (int) field->Size;
}
return((field) ? true : false);
}
unsigned short PacketClass::Get_Field_Size(char* id)
{
FieldClass *field = Find_Field(id);
if (field)
return field->Get_Size();
else
return 0;
}

View File

@@ -0,0 +1,105 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
* *
* Project Name : Westwood Auto Registration App *
* *
* File Name : PACKET.H *
* *
* Programmer : Philip W. Gorrow *
* *
* Start Date : 04/19/96 *
* *
* Last Update : April 19, 1996 [PWG] *
* *
* This header defines the functions for the PacketClass. The packet *
* class is used to create a linked list of field entries which can be *
* converted to a linear packet in a COMMS API compatible format. *
* *
* Packets can be created empty and then have fields added to them or can *
* be created from an existing linear packet. *
* *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "field.h"
#include "wlib/wstypes.h"
class PacketClass
{
public:
PacketClass(short id = 0)
{
Size = 0;
ID = id;
Head = 0;
}
PacketClass(char *cur_buf);
~PacketClass();
//
// This function allows us to add a field to the start of the list. As the field is just
// a big linked list it makes no difference which end we add a member to.
//
void Add_Field(FieldClass *field);
//
// These conveniance functions allow us to add a field directly to the list without
// having to worry about newing one first.
//
void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));};
void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));};
void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));};
void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));};
void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));};
void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));};
void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));};
void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));};
//
// These functions search for a field of a given name in the list and
// return the data via a reference value.
//
FieldClass *Find_Field(char *id);
bit8 Get_Field(char *id, int &data);
bit8 Get_Field(char *id, char &data);
bit8 Get_Field(char *id, unsigned char &data);
bit8 Get_Field(char *id, short &data);
bit8 Get_Field(char *id, unsigned short &data);
bit8 Get_Field(char *id, long &data);
bit8 Get_Field(char *id, unsigned long &data);
bit8 Get_Field(char *id, unsigned &data);
bit8 Get_Field(char *id, char *data);
bit8 Get_Field(char *id, void *data, int &length);
unsigned short Get_Field_Size(char* id);
char *Create_Comms_Packet(int &size);
private:
unsigned short Size;
short ID;
FieldClass *Head;
FieldClass *Current;
};

View File

@@ -0,0 +1,294 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Generic Game Results Server
File Name : rengameres.cpp
Author : Joe Howes (jhowes@westwood.com)
Start Date : Apr 5, 2000
Last Update :
------------------------------------------------------------------------------
Wraps game results for a game of multiplayer Renegade and provides method
for sending to a WOL game results server.
\****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "packet.h"
#include "tcpmgr.h"
#include "tcpcon.h"
#include "wencrypt.h"
#include "rengameres.h"
RenegadeGameRes::~RenegadeGameRes()
{
if( _host != NULL ) delete[] _host;
if( _map_name != NULL ) delete[] _map_name;
if( _logins != NULL )
{
for(int i = 0; i < _myplayercount; i++)
if( _logins[i] != NULL ) delete[] _logins[i];
delete[] _logins;
}
if( _scores != NULL ) delete[] _scores;
if( _clan_ids != NULL ) delete[] _clan_ids;
if( _durations != NULL ) delete[] _durations;
if( _ips != NULL ) delete[] _ips;
if( _deaths != NULL ) delete[] _deaths;
if( _kills != NULL ) delete[] _kills;
if( _selfkills != NULL ) delete[] _selfkills;
if( _damagepoints != NULL ) delete[] _damagepoints;
}
void RenegadeGameRes::setMapName(const char* val)
{
if( _map_name != NULL ) delete[] _map_name;
if( val == NULL ) return;
_map_name = new char[strlen(val)+1];
strcpy(_map_name, val);
}
void RenegadeGameRes::addPlayer(const char* login, double score, long unsigned int clan_id,
long unsigned int duration, long unsigned int ip,
long unsigned int deaths, long unsigned int kills,
long unsigned int selfkills, long unsigned int damagepoints)
{
char** newstr = _addToArr(_logins, login);
delete[] _logins;
_logins = newstr;
// There's no happy easy way to shoot a floating value over
// the wire. The values in jscores are in (0.0 <= x <= 1.0), so
// we take the maximum value for a 4-byte int and multiply it
// by the jscore floating value. We now have a scale of the same
// accuracy as a 4-byte float.
// Expecting a score X such that -0.5 <= X <= 0.5
score += 0.5; // So that it can be stored unsigned
long unsigned int convscore = (long unsigned int)(score * GR_SCORE_SCALE);
long unsigned int* newlui = _addToArr(_scores, convscore);
delete[] _scores;
_scores = newlui;
newlui = _addToArr(_clan_ids, clan_id);
delete[] _clan_ids;
_clan_ids = newlui;
newlui = _addToArr(_durations, duration);
delete[] _durations;
_durations = newlui;
newlui = _addToArr(_ips, ip);
delete[] _ips;
_ips = newlui;
newlui = _addToArr(_deaths, deaths);
delete[] _deaths;
_deaths = newlui;
newlui = _addToArr(_kills, kills);
delete[] _kills;
_kills = newlui;
newlui = _addToArr(_selfkills, selfkills);
delete[] _selfkills;
_selfkills = newlui;
newlui = _addToArr(_damagepoints, damagepoints);
delete[] _damagepoints;
_damagepoints = newlui;
_myplayercount++; // *** MUST BE INCREMENTED __AFTER__ ADDING ALL THIS STUFF!!!! ***
}
int RenegadeGameRes::sendResults()
{
assert( _host != NULL );
assert( _port != 0 );
//GameResPacket grPacket
// Build the packet
PacketClass rawPacket;
rawPacket.Add_Field(GR_GAME_ID, _game_id);
rawPacket.Add_Field(GR_PLAYER_COUNT, (long)_player_count);
rawPacket.Add_Field(GR_CLAN_GAME, _clan_game);
rawPacket.Add_Field(GR_DURATION, _duration);
rawPacket.Add_Field(GR_MAP_NAME, _map_name);
rawPacket.Add_Field(GR_SKU, _sku);
rawPacket.Add_Field(GR_STYLE, _style);
rawPacket.Add_Field(GR_NUM_CLANS, _num_clans);
rawPacket.Add_Field(GR_START_TIME, _start_time);
rawPacket.Add_Field(GR_TOURNAMENT, _tournament);
for (int i = 0 ; i < _player_count; i++)
{
GR_LOGINS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_LOGINS, (char *)((const char*)_logins[i]));
GR_SCORES[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_SCORES, _scores[i]);
GR_CLANIDS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_CLANIDS, _clan_ids[i]);
GR_DURATIONS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_DURATIONS, _durations[i]);
GR_IPS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_IPS, _ips[i]);
GR_DEATHS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_DEATHS, _deaths[i]);
GR_KILLS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_KILLS, _kills[i]);
GR_SELFKILLS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_SELFKILLS, _selfkills[i]);
GR_DAMAGEPOINTS[3] = (char)('0' + (char)i);
rawPacket.Add_Field(GR_DAMAGEPOINTS, _damagepoints[i]);
}
int packetsize = 0;
void* outPacket = rawPacket.Create_Comms_Packet(packetsize);
void* encPacket = PrepareEncryptedPacket((unsigned char*)outPacket, &packetsize);
bit8 result = 0;
sint32 sendlen = 0;
// If the _host member is not set, then this method is being called in-game
// and wants to use the WOLAPI methods to send game results. Otherwise it
// is some small test application and we will use the tcp classes provided by Neal.
#ifndef GRSETTING_USING_WOLAPI
TCPMgr tcpMgr;
TCPCon* tcpCon;
uint32 handle = -1;
result = tcpMgr.connect(_host, _port, &handle);
if( result == FALSE )
sendlen = GR_ERROR_BIND_FAILED;
else
{
result = tcpMgr.getOutgoingConnection(&tcpCon, handle, 5);
if( result == FALSE )
sendlen = GR_ERROR_CONNECT_FAILED;
else
{
sendlen = tcpCon->write((uint8*)encPacket, packetsize, 5);
tcpCon->close();
}
}
#else
result = RequestGameresSend( _host, _port, encPacket, packetsize );
#endif
// Clean up
delete[] outPacket;
delete[] encPacket;
if( _host != NULL )
return sendlen;
else
return (int)result;
return 0;
}
/*----------------------------------------------------------------------------------.
| METHOD: _addToArr |
| Takes a pointer to an array and a new item, constructs a new array and returns |
| a pointer to it. |
`----------------------------------------------------------------------------------*/
char** RenegadeGameRes::_addToArr(char** arr, const char* item)
{
char** newarr = NULL;
if( arr == NULL )
{
// Make a new array
assert( _myplayercount == 0 );
newarr = new char*[1];
newarr[0] = new char[strlen(item)+1]; // Space for the new item
// Add the new item
strcpy(newarr[_myplayercount], item);
}
else
{
// Make a new array and copy all the old stuff over
int i = 0;
assert( _myplayercount > 0 );
newarr = new char*[_myplayercount+1];
for(i = 0; i < _myplayercount; i++)
newarr[i] = arr[i];
newarr[_myplayercount] = new char[strlen(item)+1];
// Add the new item
strcpy(newarr[_myplayercount], item);
}
return newarr;
}
/*----------------------------------------------------------------------------------.
| METHOD: _addToArr |
| Takes a pointer to an array and a new item, constructs a new array and returns |
| a pointer to it. |
`----------------------------------------------------------------------------------*/
long unsigned int* RenegadeGameRes::_addToArr(long unsigned int* arr, long unsigned int item)
{
long unsigned int* newarr = NULL;
if( arr == NULL )
{
// Make a new array
assert( _myplayercount == 0 );
newarr = new long unsigned int[1];
// Add the new item
newarr[0] = item;
}
else
{
// Make a new array and copy all the old stuff over
assert( _myplayercount > 0 );
newarr = new long unsigned int[_myplayercount+1];
for(int i = 0; i < _myplayercount; i++)
newarr[i] = arr[i];
// Add the new item
newarr[_myplayercount] = item;
}
return newarr;
}

View File

@@ -0,0 +1,193 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Generic Game Results Server
File Name : rengameres.h
Author : Joe Howes (jhowes@westwood.com)
Start Date : Apr 5, 2000
Last Update :
------------------------------------------------------------------------------
Wraps game results for a game of multiplayer Renegade and provides method
for sending to a WOL game results server.
\****************************************************************************/
#ifndef __RENGAMERES_H_
#define __RENGAMERES_H_
/*----------------------------------------------------------------------.
| MACROS |
`----------------------------------------------------------------------*/
//#define GRSETTING_USING_WOLAPI // Uncomment if the class is
// being used with the game
// (as opposed to the test applet)
static unsigned long GR_SCORE_SCALE = 10000000;
static unsigned int GR_BASE_SKU = 8704;
// Errors
static const int GR_ERROR_BIND_FAILED = -100;
static const int GR_ERROR_CONNECT_FAILED = -101;
// Game Specific
static char GR_GAME_ID[5] = { "IDNO" };
static char GR_PLAYER_COUNT[5] = { "PLRS" };
static char GR_CLAN_GAME[5] = { "CLGM" };
static char GR_DURATION[5] = { "DURA" };
static char GR_MAP_NAME[5] = { "MPNM" };
static char GR_SKU[5] = { "GSKU" };
static char GR_STYLE[5] = { "GSTY" };
static char GR_NUM_CLANS[5] = { "NTMS" };
static char GR_START_TIME[5] = { "TIME" };
static char GR_TOURNAMENT[5] = { "TRNY" };
// Player Specific
static char GR_LOGINS[5] = { "LGL?" };
static char GR_SCORES[5] = { "SCO?" };
static char GR_CLANIDS[5] = { "CLN?" };
static char GR_DURATIONS[5] = { "DUR?" };
static char GR_IPS[5] = { "IPL?" };
static char GR_DEATHS[5] = { "DTH?" };
static char GR_KILLS[5] = { "KIL?" };
static char GR_SELFKILLS[5] = { "SKL?" };
static char GR_DAMAGEPOINTS[5] = { "DMP?" };
/*----------------------------------------------------------------------.
| DATATYPES |
`----------------------------------------------------------------------*/
typedef enum gamestyle
{
GR_DEATHMATCH = 0,
GR_CAPTUREFLAG,
GR_FLAGBALL,
GR_KINGREALM,
GR_HIGHLANDER,
GR_GAUNTLET,
GR_NUM_GAMESTYLES
} GameStyle;
typedef enum language
{
GR_ENGLISH = 0,
GR_UK_ENGLISH,
GR_GERMAN,
GR_FRENCH,
GR_DUTCH,
GR_ITALIAN,
GR_JAPANESE,
GR_SPANISH,
GR_SCANDINAVIAN,
GR_KOREAN,
NUM_LANGUAGES
} Language;
class RenegadeGameRes
{
public:
// METHODS
RenegadeGameRes(const char* host = NULL, int port = 0) :
_host(NULL), _port(0),
_myplayercount(0), _game_id(0), _player_count(0), _clan_game(0),
_duration(0), _map_name(NULL), _sku(0), _style(GR_DEATHMATCH),
_num_clans(0), _start_time(0), _tournament(0), _logins(NULL),
_scores(NULL), _clan_ids(NULL), _durations(NULL), _ips(NULL),
_deaths(NULL), _kills(NULL), _selfkills(NULL), _damagepoints(NULL)
{
if( host != NULL )
{
setHost(host);
setPort(port);
}
}
~RenegadeGameRes();
void setHost(const char* host)
{
if( _host != NULL )
delete[] _host;
_host = new char[strlen(host)+1];
strcpy(_host, host);
}
void setPort(int val) { _port = val; }
void setGameID(long unsigned int val) { _game_id = val; }
void setPlayerCount(unsigned char val) { _player_count = val; }
void setClanGame(unsigned char val) { _clan_game = val; }
void setDuration(long unsigned int val) { _duration = val; }
void setMapName(const char* val);
void setSKU(Language lang) { _sku = GR_BASE_SKU | lang; }
void setStyle(GameStyle val) { _style = (unsigned char)val; }
void setNumClans(unsigned char val) { _num_clans = val; }
void setStartTime(long unsigned int val) { _start_time = val; }
void setTournament(unsigned char val) { _tournament = val; }
void addPlayer(const char* login = "INVALID", double score = 0.0, long unsigned int clan_id = 0,
long unsigned int duration = 0, long unsigned int ip = 0,
long unsigned int deaths = 0, long unsigned int kills = 0,
long unsigned int selfkills = 0, long unsigned int damagepoints = 0);
int sendResults();
private:
// METHODS
char** _addToArr(char** arr, const char* item);
long unsigned int* _addToArr(long unsigned int* arr, long unsigned int item);
// MEMBERS
char* _host;
int _port;
int _myplayercount;
// Game Specific
long unsigned int _game_id;
unsigned char _player_count;
unsigned char _clan_game; // Boolean
long unsigned int _duration; // Secs since epoch
char* _map_name; // Must be NULL terminated
long unsigned int _sku;
unsigned char _style; // Will be converted to an unsigned char
unsigned char _num_clans;
long unsigned int _start_time; // Secs since epoch
unsigned char _tournament; // Boolean
// Player Specific (These are all arrays)
char** _logins; // Must be NULL terminated
long unsigned int* _scores;
long unsigned int* _clan_ids;
long unsigned int* _durations; // Secs since epoch
long unsigned int* _ips; // As integers, not dotted quads
long unsigned int* _deaths;
long unsigned int* _kills;
long unsigned int* _selfkills;
long unsigned int* _damagepoints;
};
#endif

View File

@@ -0,0 +1,332 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
#include "tcpcon.h"
#include <time.h>
#define DEFAULT_TCP_DELAY 15
TCPCon::TCPCon(SOCKET sock) :
BufferedWrites_(false),
TCPMgrPtr_(NULL)
{
Socket_=sock;
isConnected(); // This will set the internal connect status
setInputDelay(DEFAULT_TCP_DELAY);
setOutputDelay(DEFAULT_TCP_DELAY);
}
TCPCon::~TCPCon()
{
close(); // will try and flush the write queue
ReadQueue_.clear();
WriteQueue_.clear();
if ((BufferedWrites_) && (TCPMgrPtr_))
TCPMgrPtr_->setBufferedWrites(this, FALSE);
}
SOCKET TCPCon::getFD(void)
{ return(Socket_); }
void TCPCon::close(void)
{
if (BufferedWrites_)
{
while(WriteQueue_.length())
pumpWrites();
}
::closesocket(Socket_);
State_=TCPMgr::CLOSED;
}
//
// Write data
//
// Returns 'n' bytes written, 0 if closed, or -1 for error.
//
sint32 TCPCon::write(IN uint8 *msg,uint32 len, sint32 wait_secs)
{
if (State_==TCPMgr::CLOSED)
return(0);
if (BufferedWrites_)
{
WriteQueue_.addMany(msg, WriteQueue_.length(), len); // add to tail
pumpWrites(); // send what I can
return(len);
}
else
{
return (normalWrite(msg, len, wait_secs));
}
}
//
// set buffered status
//
void TCPCon::setBufferedWrites(TCPMgr *mgrptr, bit8 enabled)
{
if (enabled)
BufferedWrites_=TRUE;
else
{
while(WriteQueue_.length())
pumpWrites();
BufferedWrites_=FALSE;
}
TCPMgrPtr_=mgrptr;
}
//
// Try and send queued data (PRIVATE)
//
void TCPCon::pumpWrites(void)
{
if (State_==TCPMgr::CLOSED)
WriteQueue_.clear();
if (WriteQueue_.length()==0)
return;
int sendlen;
int retval;
uint8 *bufptr=NULL;
while(1)
{
sendlen=WriteQueue_.length();
if (sendlen == 0)
break;
WriteQueue_.getPointer(&bufptr,0); // pointer to first byte
retval=normalWrite((uint8 *)bufptr, sendlen, 0);
if (retval <= 0)
break;
WriteQueue_.removeMany(NULL, 0, retval); // successful send
}
return;
}
//
// Non-buffered write (PRIVATE METHOD)
//
// Returns 'n' bytes written, 0 if closed, or -1 for error.
//
sint32 TCPCon::normalWrite(IN uint8 *msg,uint32 len, sint32 wait_secs)
{
if (State_==TCPMgr::CLOSED)
return(0);
if (wait_secs < 0)
wait_secs=OutputDelay_;
sint32 retval=0;
sint32 sendCount=0;
time_t start=time(NULL);
TCPMgr::STATUS status;
while(1)
{
retval=send(Socket_,(const char *)(msg+sendCount),(len-sendCount),0);
if ((retval > 0) && (retval+sendCount)==int(len))
break;
if (retval==SOCKET_ERROR)
{
status=TCPMgr::getStatus();
if ((status != TCPMgr::INTR) && (status != TCPMgr::WOULDBLOCK) && (status != TCPMgr::INPROGRESS))
{
if (sendCount)
return(sendCount);
else
return(-1);
}
}
else if (retval > 0)
sendCount+=retval;
sint32 remaining_wait=wait_secs - (time(NULL)-start);
if ((remaining_wait > 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,FALSE) > 0))
continue; // I can write now....
if (remaining_wait <= 0)
break;
}
return(retval);
}
//
// Read data
//
// Returns 'n' bytes read, 0 for close, or -1 for error.
// This may return less than we asked for
//
sint32 TCPCon::read(OUT uint8 *msg,uint32 maxlen, sint32 wait_secs)
{
sint32 retval=0;
sint32 recvCount=0;
time_t start=time(NULL);
char readBuffer[257];
if (wait_secs < 0)
wait_secs=InputDelay_;
if (State_==TCPMgr::CLOSED)
return(0);
TCPMgr::STATUS status;
while(1)
{
// Do we even nead to read from the net?
if (ReadQueue_.length() >= int(maxlen))
{
ReadQueue_.removeMany(msg, 0, maxlen);
return(maxlen);
}
do
{
retval=recv(Socket_,readBuffer, 256,0);
if (retval > 0) // add to the tail of the list
{
readBuffer[retval]=0;
DBGMSG("RECV: "<<readBuffer);
// Add to tail
ReadQueue_.addMany((uint8 *)readBuffer, ReadQueue_.length(), retval);
}
} while ((retval > 0)&&(ReadQueue_.length() < int(maxlen)));
if (ReadQueue_.length()) // OK, we'll take what we've got
{
uint8 *cptr;
ReadQueue_.getPointer(&cptr,0);
/*******
fprintf(stderr,"ReadQueue(%d): '",ReadQueue_.length());
for (int i=0; i<ReadQueue_.length(); i++)
fprintf(stderr,"%c",cptr[i]);
fprintf(stderr,"'\n");
********/
int retcount=MIN(ReadQueue_.length(), int(maxlen));
ReadQueue_.removeMany(msg, 0, retcount);
return(retcount);
}
else if (retval==0)
{
close();
return(0);
}
else if (retval==SOCKET_ERROR)
{
status=TCPMgr::getStatus();
if ((status != TCPMgr::INTR) && (status != TCPMgr::WOULDBLOCK) &&
(status != TCPMgr::INPROGRESS))
return(-1);
}
sint32 remaining_wait=wait_secs - (time(NULL)-start);
if ((remaining_wait > 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,TRUE) > 0))
continue; // I can read now....
if (remaining_wait <= 0)
break;
}
return(retval);
}
// Push data back onto the read queue
bit8 TCPCon::unread(uint8 *data, int length)
{
ReadQueue_.addMany(data, 0, length);
return(TRUE);
}
// Returns 0 on failure
// Returns IP in host byte order!
bit8 TCPCon::getRemoteAddr(uint32 *ip, uint16 *port)
{
struct sockaddr_in sin;
int sinSize=sizeof(sin);
if(getpeername(Socket_,(sockaddr *)&sin,&sinSize)==0)
{
if (ip)
*ip=ntohl(sin.sin_addr.s_addr);
if (port)
*port=ntohs(sin.sin_port);
return(TRUE);
}
return(FALSE);
}
//
// only use for strings up to 4096 chars!
//
sint32 TCPCon::printf(const char *format, ...)
{
va_list arg;
char string[4097];
sint32 retval;
va_start(arg,format);
vsprintf(string,format,arg);
va_end(arg);
string[4096]=0;
retval=write((IN uint8 *)string,strlen(string), OutputDelay_);
return(retval);
}
bit8 TCPCon::isConnected(void)
{
uint32 remoteIp;
uint16 remotePort;
if (getRemoteAddr(&remoteIp,&remotePort)==TRUE)
{
State_=TCPMgr::CONNECTED;
return(TRUE);
}
else
{
State_=TCPMgr::CLOSED;
return(FALSE);
}
}
/*********
// For the OutputDevice interface
int TCPCon::print(IN char *str, int len)
{
return(write((IN uint8 *)str,len,0));
}
********/

View File

@@ -0,0 +1,67 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
#ifndef TCP_CON_HEADER
#define TCP_CON_HEADER
#include "tcpmgr.h"
#include "wlib/arraylist.h"
class TCPMgr;
class TCPCon /// : OutputDevice
{
public:
TCPCon(SOCKET sock);
~TCPCon();
SOCKET getFD(void);
void close(void);
sint32 write(IN uint8 *msg, uint32 len, sint32 wait_secs=-1);
sint32 read(OUT uint8 *msg, uint32 maxlen, sint32 wait_secs=-1);
bit8 unread(uint8 *data, int length);
bit8 getRemoteAddr(uint32 *ip, uint16 *port);
sint32 printf(const char *format, ...);
bit8 isConnected(void);
bit8 setInputDelay(sint32 delay) { InputDelay_=delay; return(TRUE); };
bit8 setOutputDelay(sint32 delay) { OutputDelay_=delay; return(TRUE); };
// For OutputDevice
/// virtual int print(IN char *str, int len);
private:
friend class TCPMgr;
void pumpWrites(void); // for buffered mode
void setBufferedWrites(TCPMgr *mgrptr, bit8 enabled);
sint32 normalWrite(IN uint8 *msg, uint32 len, sint32 wait_secs=-1);
sint32 InputDelay_; // default max time for input
sint32 OutputDelay_; // default max time for output
TCPMgr::CONN_STATE State_;
SOCKET Socket_;
ArrayList<uint8> ReadQueue_; // reads are buffered
bit8 BufferedWrites_; // T/F buffer writes?
ArrayList<uint8> WriteQueue_; // writes _can_ be buffered
TCPMgr *TCPMgrPtr_; // pointer to my manager object
};
#endif

View File

@@ -0,0 +1,532 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
#include "tcpmgr.h"
#include "tcpcon.h"
#include "wlib/wtime.h"
TCPMgr::TCPMgr()
{
HandleSequence_=0;
}
TCPMgr::~TCPMgr()
{
}
//
// Add a listener socket to accept connections on a given port
//
bit8 TCPMgr::addListener(uint32 ip, uint16 port, bit8 reuseAddr)
{
SOCKET fd=createSocket(ip,port,reuseAddr);
if (fd == INVALID_SOCKET)
return(FALSE);
listen(fd,64); // Solaris needs lots of listen slots
ListenSocket lsock;
lsock.fd=fd;
lsock.ip=ip;
lsock.port=port;
ListenArray_.addTail(lsock);
return(TRUE);
}
//
// Remove listener on a given ip/port
//
bit8 TCPMgr::removeListener(uint32 ip, uint16 port)
{
ListenSocket *lptr;
for (int i=0; i<ListenArray_.length(); i++)
{
ListenArray_.getPointer(&lptr,i);
if ((lptr->ip == ip) && (lptr->port == port))
{
closesocket(lptr->fd);
ListenArray_.remove(i);
return(TRUE);
}
}
return(FALSE);
}
//
// Get the socket for a given listener
//
bit8 TCPMgr::getListener(uint32 ip, uint16 port, OUT SOCKET &outsock)
{
ListenSocket *lptr;
for (int i=0; i<ListenArray_.length(); i++)
{
ListenArray_.getPointer(&lptr,i);
if ((lptr->ip == ip) && (lptr->port == port))
{
outsock=lptr->fd;
return(TRUE);
}
}
return(FALSE);
}
//
// Enable/Disable buffered writes on a socket
//
bit8 TCPMgr::setBufferedWrites(TCPCon *con, bit8 enabled)
{
TCPCon *tempptr=NULL;
// Check to see if this connection is already in our list
for (int i=0; i<BufferedWriters_.length(); i++)
{
BufferedWriters_.get(tempptr, i);
if (tempptr==con)
{
con->setBufferedWrites(this, false);
BufferedWriters_.remove(i);
break;
}
}
if (enabled) // OK, now add to the list
{
con->setBufferedWrites(this, true);
BufferedWriters_.addTail(con);
}
return(TRUE);
}
//
// Let all the buffered writers send data
//
void TCPMgr::pumpWriters(void) // pump the buffered writer connections
{
TCPCon *conptr=NULL;
// Check to see if this connection is already in our list
for (int i=0; i<BufferedWriters_.length(); i++)
{
BufferedWriters_.get(conptr, i);
conptr->pumpWrites();
}
}
//
// Connect by hostname rather than IP
//
bit8 TCPMgr::connect(char *host, uint16 port, OUT uint32 *handle)
{
char hostName[129];
struct hostent *hostStruct;
struct in_addr *hostNode;
if (isdigit(host[0]))
return ( connect(ntohl(inet_addr(host)), port, handle));
strcpy(hostName, host);
hostStruct = gethostbyname(host);
if (hostStruct == NULL)
return (0);
hostNode = (struct in_addr *) hostStruct->h_addr;
return ( connect(ntohl(hostNode->s_addr),port,handle) );
}
//
// Request a connection to a given address (all values in host byte order)
//
bit8 TCPMgr::connect(uint32 ip, uint16 port,OUT uint32 *handle)
{
PendingConn pConn;
if ((pConn.fd=createSocket((uint32) 0,(uint16) 0,FALSE)) == INVALID_SOCKET)
return(FALSE);
pConn.ip=0;
pConn.port=0;
pConn.remoteIp=ip;
pConn.remotePort=port;
pConn.startTime=time(NULL);
pConn.handle=HandleSequence_++;
pConn.state=CLOSED;
pConn.incoming=FALSE; // outgoing connection
ConnectArray_.addTail(pConn);
*handle=pConn.handle;
return(TRUE);
}
//
// Get an incoming connection (specify handle or 0 for any)
//
// Wait for upto 'wait_secs' seconds for the connection.
//
bit8 TCPMgr::getOutgoingConnection(TCPCon **conn, uint32 handle, sint32 wait_secs)
{
return(getConnection(conn,handle,0,wait_secs,OUTGOING));
}
//
// Get an incoming connection (specify listen port or 0 for any)
//
// Wait for upto 'wait_secs' seconds for the connection.
//
bit8 TCPMgr::getIncomingConnection(TCPCon **conn, uint16 port, sint32 wait_secs)
{
return(getConnection(conn,INVALID_HANDLE,port,wait_secs,INCOMING));
}
//
// Return after there is data to read, or we've timed out.
//
int TCPMgr::wait(uint32 sec, uint32 usec, SOCKET *sockets, int count, bit8 readMode)
{
Wtime timeout,timenow,timethen;
fd_set givenSet;
fd_set returnSet;
fd_set backupSet;
int givenMax=0;
bit8 noTimeout=FALSE;
int retval;
uint32 i;
DBGMSG("Waiting on "<<count<<" sockets");
FD_ZERO(&givenSet);
for (i=0; i<(uint32)count; i++)
{
FD_SET(sockets[i],&givenSet);
}
timeval tv;
timeval *tvPtr=NULL;
returnSet=givenSet;
backupSet=givenSet;
if ((sec==-1)||(usec==-1))
noTimeout=TRUE;
timeout.SetSec(sec);
timeout.SetUsec(usec);
timethen+=timeout;
for (i=0; i<(uint32)count; i++)
{
if (sockets[i] > (SOCKET)givenMax)
givenMax=sockets[i];
}
bit8 done=FALSE;
while( ! done)
{
tvPtr=&tv;
if (noTimeout)
tvPtr=NULL;
else
timeout.GetTimevalMT(tv);
if (readMode) // can we read?
retval=select(givenMax+1,&returnSet,0,0,tvPtr);
else // can we write?
retval=select(givenMax+1,0,&returnSet,0,tvPtr);
DBGMSG("Select wake");
if (retval>=0)
done=TRUE;
else if ((retval==SOCKET_ERROR)&&(getStatus()==INTR)) // in case of signal
{
if (noTimeout==FALSE)
{
timenow.Update();
timeout=timethen-timenow;
}
if ((noTimeout==FALSE)&&(timenow.GetSec()==0)&&(timenow.GetUsec()==0))
done=TRUE;
else
returnSet=backupSet;
}
else // maybe out of memory?
{
done=TRUE;
}
}
return(retval);
}
TCPMgr::STATUS TCPMgr::getStatus(void)
{
#ifdef _WINDOWS
int status=WSAGetLastError();
if (status==0) return(OK);
else if (status==WSAEINTR) return(INTR);
else if (status==WSAEINPROGRESS) return(INPROGRESS);
else if (status==WSAECONNREFUSED) return(CONNREFUSED);
else if (status==WSAEINVAL) return(INVAL);
else if (status==WSAEISCONN) return(ISCONN);
else if (status==WSAENOTSOCK) return(NOTSOCK);
else if (status==WSAETIMEDOUT) return(TIMEDOUT);
else if (status==WSAEALREADY) return(ALREADY);
else if (status==WSAEWOULDBLOCK) return(WOULDBLOCK);
else if (status==WSAEBADF) return(BADF);
else return(UNKNOWN);
#else
int status=errno;
if (status==0) return(OK);
else if (status==EINTR) return(INTR);
else if (status==EINPROGRESS) return(INPROGRESS);
else if (status==ECONNREFUSED) return(CONNREFUSED);
else if (status==EINVAL) return(INVAL);
else if (status==EISCONN) return(ISCONN);
else if (status==ENOTSOCK) return(NOTSOCK);
else if (status==ETIMEDOUT) return(TIMEDOUT);
else if (status==EALREADY) return(ALREADY);
else if (status==EAGAIN) return(AGAIN);
else if (status==EWOULDBLOCK) return(WOULDBLOCK);
else if (status==EBADF) return(BADF);
else return(UNKNOWN);
#endif
}
/********************* BEGIN PRIVATE METHODS *********************************/
//
// Create a bound socket
//
SOCKET TCPMgr::createSocket(uint32 ip, uint16 port, bit8 reuseAddr)
{
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=htonl(ip);
SOCKET fd=socket(AF_INET,SOCK_STREAM,DEFAULT_PROTOCOL);
if (fd==-1)
return(INVALID_SOCKET);
if (setBlocking(fd,FALSE)==FALSE)
return(INVALID_SOCKET);
if (reuseAddr)
{
uint32 opval=1;
if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&opval,sizeof(opval)) != 0)
{
closesocket(fd);
return(INVALID_SOCKET);
}
}
if (bind(fd,(struct sockaddr *)&addr, sizeof(addr))==SOCKET_ERROR)
{
closesocket(fd);
return(INVALID_SOCKET);
}
return(fd);
}
//
// Set the blocking mode of the socket
//
bit8 TCPMgr::setBlocking(SOCKET fd, bit8 block)
{
#ifdef _WINDOWS
unsigned long flag=1;
if (block)
flag=0;
int retval;
retval=ioctlsocket(fd,FIONBIO,&flag);
if (retval==SOCKET_ERROR)
return(FALSE);
else
return(TRUE);
#else // UNIX
int flags = fcntl(fd, F_GETFL, 0);
if (block==FALSE) // set nonblocking
flags |= O_NONBLOCK;
else // set blocking
flags &= ~(O_NONBLOCK);
if (fcntl(fd, F_SETFL, flags) < 0)
{
return(FALSE);
}
return(TRUE);
#endif
}
bit8 TCPMgr::getConnection(TCPCon **conn, uint32 handle, uint16 port, sint32 wait_secs, DIRECTION dir)
{
PendingConn *connPtr=NULL;
time_t start=time(NULL);
SOCKET fdArray[1024];
for (int i=0; i<ConnectArray_.length(); i++)
{
ConnectArray_.getPointer(&connPtr,i);
fdArray[i]=connPtr->fd;
}
while(1)
{
pumpConnections();
for (int i=0; i<ConnectArray_.length(); i++)
{
ConnectArray_.getPointer(&connPtr,i);
if (connPtr->state != CONNECTED)
continue;
if (( ! ((int)dir & (int)INCOMING)) && (connPtr->incoming == TRUE))
continue;
if (( ! ((int)dir & (int)OUTGOING)) && (connPtr->incoming == FALSE))
continue;
if ((handle != INVALID_HANDLE) && (handle != connPtr->handle))
continue;
if ((port != 0) && (port != connPtr->port))
continue;
*conn=new TCPCon(connPtr->fd);
ConnectArray_.remove(i);
return(TRUE);
}
// Wait for socket activity for a bit
#ifdef _WINDOWS
Sleep(100); // windows may be getting flooded with conn msgs, test this
#endif
sint32 remaining_wait=wait_secs - (time(NULL)-start);
if ((remaining_wait > 0) && (wait(remaining_wait,0,fdArray,ConnectArray_.length(),FALSE) > 0))
continue; // got something!
if (remaining_wait <= 0)
break;
}
return(FALSE);
}
//
// TOFIX: don't forget about connect flooding on Win32
//
void TCPMgr::pumpConnections(void)
{
PendingConn *connPtr=NULL;
STATUS status;
int i;
int retval;
// Outgoing connections
for (i=0; i<ConnectArray_.length(); i++)
{
ConnectArray_.getPointer(&connPtr,i);
if ((connPtr->state == CLOSED) || (connPtr->state == CONNECTING))
{
struct sockaddr_in addr;
int addrSize=sizeof(addr);
addr.sin_family=AF_INET;
addr.sin_port=htons(connPtr->remotePort);
addr.sin_addr.s_addr=htonl(connPtr->remoteIp);
if (connPtr->state == CONNECTING)
{
if (getpeername(connPtr->fd,(sockaddr *)&addr,&addrSize)==0) // if get endpoint, then success
connPtr->state=CONNECTED;
}
else if ((retval=::connect(connPtr->fd, (struct sockaddr *)&addr, sizeof(addr)))==SOCKET_ERROR)
{
status=getStatus();
if (status==ISCONN)
retval=0;
else if ((status==INPROGRESS)||(status==ALREADY)||(status==WOULDBLOCK))
{
connPtr->state=CONNECTING;
continue; // Move on to next pending conn...
}
else // doh, real problem
{
assert(0);
closesocket(connPtr->fd);
connPtr->fd=createSocket(connPtr->ip, connPtr->port, FALSE);
}
}
if (retval==0)
{
connPtr->state=CONNECTED;
}
}
} // for ConnectArray_;
// Incoming connections
ListenSocket *listenPtr;
struct sockaddr_in clientAddr;
int addrlen;
for (i=0; i<ListenArray_.length(); i++)
{
ListenArray_.getPointer(&listenPtr,i);
while(1) // accept all incoming on each socket
{
addrlen=sizeof(clientAddr);
SOCKET newFD=accept(listenPtr->fd, (struct sockaddr *)&clientAddr, &addrlen);
if (newFD != INVALID_SOCKET)
{
setBlocking(newFD, FALSE);
DBGMSG("Connection accepted");
PendingConn newConn;
newConn.fd=newFD;
newConn.ip=0;
newConn.port=0;
newConn.remoteIp=ntohl(clientAddr.sin_addr.s_addr);
newConn.remotePort=ntohs(clientAddr.sin_port);
newConn.handle=HandleSequence_++;
newConn.state=CONNECTED;
newConn.incoming=TRUE;
newConn.remoteIp=ntohl(clientAddr.sin_addr.s_addr);
newConn.remotePort=ntohs(clientAddr.sin_port);
ConnectArray_.addTail(newConn);
}
else
break;
}
}
return;
}

View File

@@ -0,0 +1,162 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
#ifndef TCPMGR_HEADER
#define TCPMGR_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include "wlib/wstypes.h"
#include "wlib/wdebug.h"
#include "wlib/arraylist.h"
#ifdef _WINDOWS
#include <windows.h>
#include <winsock.h>
#include <io.h>
#else //UNIX
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <limits.h>
typedef sint32 SOCKET;
#define closesocket close
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#endif
#ifdef AIX
#include <sys/select.h>
#endif
class TCPCon;
#define DEFAULT_PROTOCOL 0
#define INVALID_HANDLE 0
class TCPMgr {
public:
enum CONN_STATE
{
CLOSED,
CONNECTING,
CONNECTED
};
// These defines specify a system independent way to
// get error codes for socket services.
enum STATUS
{
OK = 0, // Everything's cool
UNKNOWN = -1, // There was an error of unknown type
ISCONN = -2, // The socket is already connected
INPROGRESS = -3, // The socket is non-blocking and the operation
// isn't done yet
ALREADY = -4, // The socket is already attempting a connection
// but isn't done yet
AGAIN = -5, // Try again.
ADDRINUSE = -6, // Address already in use
ADDRNOTAVAIL = -7, // That address is not available on the remote host
BADF = -8, // Not a valid FD
CONNREFUSED = -9, // Connection was refused
INTR =-10, // Operation was interrupted
NOTSOCK =-11, // FD wasn't a socket
PIPE =-12, // That operation just made a SIGPIPE
WOULDBLOCK =-13, // That operation would block
INVAL =-14, // Invalid
TIMEDOUT =-15 // Timeout
};
enum DIRECTION
{
INCOMING = 1,
OUTGOING = 2,
EITHER = 3
};
TCPMgr();
~TCPMgr();
bit8 addListener(uint32 ip, uint16 port, bit8 reuseAddr);
bit8 removeListener(uint32 ip, uint16 port);
bit8 getListener(uint32 ip, uint16 port, OUT SOCKET &outsock);
bit8 connect(char *address, uint16 port, uint32 *handle);
bit8 connect(uint32 ip, uint16 port,OUT uint32 *handle);
bit8 getOutgoingConnection(TCPCon **conn, uint32 handle, sint32 wait_secs);
bit8 getIncomingConnection(TCPCon **conn, uint16 port, sint32 wait_secs);
bit8 setBufferedWrites(TCPCon *con, bit8 enabled);
void pumpWriters(void); // pump the buffered writer connections
// Static methods
static int wait(uint32 sec, uint32 usec, SOCKET *sockets, int count, bit8 readMode=TRUE);
static STATUS getStatus(void);
private:
SOCKET createSocket(uint32 ip, uint16 port, bit8 reuseAddr=TRUE);
bit8 setBlocking(SOCKET fd, bit8 block);
bit8 getConnection(TCPCon **conn, uint32 handle, uint16 port, sint32 wait_secs, DIRECTION dir);
void pumpConnections(void);
struct ListenSocket
{
SOCKET fd;
uint32 ip;
uint32 port;
};
ArrayList<ListenSocket> ListenArray_;
ArrayList<TCPCon * > BufferedWriters_; // need to be pumped
struct PendingConn
{
SOCKET fd;
uint32 ip;
uint16 port;
time_t startTime;
uint32 handle;
CONN_STATE state;
bit8 incoming;
uint32 remoteIp;
uint16 remotePort;
};
ArrayList<PendingConn> ConnectArray_;
uint32 HandleSequence_;
};
#endif

View File

@@ -0,0 +1,809 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
wtime Neal Kettler
\****************************************************************************/
#include <ctype.h>
#include "wtime.h"
static char *DAYS[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static char *FULLDAYS[]={"Sunday","Monday","Tuesday","Wednesday","Thursday",
"Friday","Saturday"};
static char *MONTHS[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
"Aug","Sep","Oct","Nov","Dec"};
static char *FULLMONTHS[]={"January","February","March","April","May","June",
"July","August","September","October","November","December"};
Wtime::Wtime(void)
{
Update();
}
Wtime::Wtime( Wtime &other )
{
sign=other.sign;
sec=other.sec;
usec=other.usec;
}
Wtime::Wtime( uint32 other )
{
sign=POSITIVE;
sec=other;
usec=0;
}
Wtime::~Wtime()
{
}
void Wtime::Update(void)
{
sign=POSITIVE;
#ifdef _WINDOWS
struct _timeb wintime;
_ftime(&wintime);
sec=wintime.time;
usec=(wintime.millitm)*1000;
#endif
#ifndef _WINDOWS
struct timeval unixtime;
struct timezone unixtzone;
gettimeofday(&unixtime,&unixtzone);
sec=unixtime.tv_sec;
usec=unixtime.tv_usec;
#endif
}
// Parses a date string that's in modified RFC 1123 format
// Can have a +minutes after the normal time
// eg: Thu, 20 Jun 1996 17:33:49 +100
// Returns true if successfully parsed, false otherwise
bit8 Wtime::ParseDate(char *in)
{
int i;
uint32 minOffset;
struct tm t;
char *ptr=in;
while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of string
if (*ptr==0) return(FALSE);
t.tm_wday=-1;
for (i=0; i<7; i++) // parse day of week
if (strncmp(ptr,DAYS[i],strlen(DAYS[i]))==0)
t.tm_wday=i;
if (t.tm_wday==-1)
return(FALSE);
while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to day of month
if (*ptr==0) return(FALSE);
t.tm_mday=atoi(ptr);
while ((!isalpha(*ptr))&&(*ptr!=0)) ptr++; // skip to month
if (*ptr==0) return(FALSE);
t.tm_mon=-1;
for (i=0; i<12; i++) // match month
if (strncmp(ptr,MONTHS[i],strlen(MONTHS[i]))==0) t.tm_mon=i;
if (t.tm_mon==-1) return(FALSE);
while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++;
if (*ptr==0) return(FALSE);
t.tm_year=atoi(ptr);
if (t.tm_year<70) // if they specify a 2 digit year, we'll be nice
t.tm_year+=2000;
else if (t.tm_year<100)
t.tm_year+=1900;
if (t.tm_year>2200) // I doubt my code will be around for another 203 years
return(FALSE);
while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to end of year
if (*ptr==0) return(FALSE);
while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of time
if (*ptr==0) return(FALSE);
t.tm_hour=atoi(ptr);
while ((*ptr!=':')&&(*ptr!=0)) ptr++;
ptr++; // skip past colon
if (*ptr==0) return(FALSE);
t.tm_min=atoi(ptr);
while ((*ptr!=':')&&(*ptr!=0)) ptr++;
ptr++; // skip past colon
if (*ptr==0) return(FALSE);
t.tm_sec=atoi(ptr);
t.tm_year%=100; // 1996 is stored as 96, not 1996
t.tm_isdst=-1; // daylight savings info isn't available
sec=(uint32)(mktime(&t));
if ((sint32)sec==-1)
return(FALSE);
// The next part of the time is OPTIONAL (+minutes)
// first skip past the seconds
while ((isdigit(*ptr))&&(*ptr!=0)) ptr++;
if (*ptr==0) return(TRUE);
// skip past any spaces
while ((isspace(*ptr))&&(*ptr!=0)) ptr++;
if (*ptr!='+')
{
//printf("\nNOPE ptr was '%s'\n",ptr);
return(TRUE);
}
ptr++;
if (*ptr==0)
{
//printf("\nPTR WAS 0\n");
return(TRUE);
}
minOffset=atol(ptr);
//printf("\n\nAdding %d minutes!\n\n",minOffset);
sec+=minOffset*60; // add the minutes as seconds
return(TRUE);
}
// This takes the standard Microsoft time formatting string
// make sure the out string is big enough
// An example format would be "mm/dd/yy hh:mm:ss"
bit8 Wtime::FormatTime(char *out, char *format)
{
int lastWasH=0;
out[0]=0;
char *ptr=format;
if (*ptr=='"') ptr++; // skip past open quote if exists
while (*ptr!=0)
{
if (lastWasH>0)
lastWasH--;
if (isspace(*ptr))
{
if (lastWasH==1) lastWasH=2;
sprintf(out+strlen(out),"%c",*ptr);
ptr+=1;
}
else if (strncmp(ptr,"\"",1)==0)
{
break;
}
else if (strncmp(ptr,":",1)==0)
{
if (lastWasH==1) lastWasH=2;
sprintf(out+strlen(out),":");
ptr+=1;
}
else if (strncmp(ptr,"/",1)==0)
{
sprintf(out+strlen(out),"/");
ptr+=1;
}
else if (strncmp(ptr,"c",1)==0)
{
sprintf(out+strlen(out),"%ld/%ld/%02ld %ld:%02ld:%02ld",GetMonth(),
GetMDay(),GetYear()%100,GetHour(),GetMinute(),GetSecond());
ptr+=1;
}
else if (strncmp(ptr,"dddddd",6)==0)
{
sprintf(out+strlen(out),"%s %02ld, %ld",FULLMONTHS[GetMonth()-1],
GetMDay(),GetYear());
ptr+=6;
}
else if (strncmp(ptr,"ddddd",5)==0)
{
sprintf(out+strlen(out),"%ld/%ld/%02ld",GetMonth(),GetMDay(),
GetYear()%100);
ptr+=5;
}
else if (strncmp(ptr,"dddd",4)==0)
{
sprintf(out+strlen(out),"%s",FULLDAYS[GetWDay()-1]);
ptr+=4;
}
else if (strncmp(ptr,"ddd",3)==0)
{
sprintf(out+strlen(out),"%s",DAYS[GetWDay()-1]);
ptr+=3;
}
else if (strncmp(ptr,"dd",2)==0)
{
sprintf(out+strlen(out),"%02ld",GetMDay());
ptr+=2;
}
else if (strncmp(ptr,"d",1)==0)
{
sprintf(out+strlen(out),"%ld",GetMDay());
ptr+=1;
}
else if (strncmp(ptr,"ww",2)==0)
{
sprintf(out+strlen(out),"%02ld",GetYWeek());
ptr+=2;
}
else if (strncmp(ptr,"w",1)==0)
{
sprintf(out+strlen(out),"%ld",GetWDay());
ptr+=1;
}
else if (strncmp(ptr,"mmmm",4)==0)
{
sprintf(out+strlen(out),"%s",FULLMONTHS[GetMonth()-1]);
ptr+=4;
}
else if (strncmp(ptr,"mmm",3)==0)
{
sprintf(out+strlen(out),"%s",MONTHS[GetMonth()-1]);
ptr+=3;
}
else if (strncmp(ptr,"mm",2)==0)
{
if (lastWasH==1)
sprintf(out+strlen(out),"%02ld",GetMinute());
else
sprintf(out+strlen(out),"%02ld",GetMonth());
ptr+=2;
}
else if (strncmp(ptr,"m",1)==0)
{
if (lastWasH==1)
sprintf(out+strlen(out),"%ld",GetMinute());
else
sprintf(out+strlen(out),"%ld",GetMonth());
ptr+=1;
}
else if (strncmp(ptr,"q",1)==0)
{
sprintf(out+strlen(out),"%ld",((GetMonth()-1)/4)+1); // GetQuarter
ptr+=1;
}
else if (strncmp(ptr,"yyyy",4)==0)
{
sprintf(out+strlen(out),"%ld",GetYear());
ptr+=4;
}
else if (strncmp(ptr,"yy",2)==0)
{
sprintf(out+strlen(out),"%02ld",GetYear()%100);
ptr+=2;
}
else if (strncmp(ptr,"y",1)==0)
{
sprintf(out+strlen(out),"%ld",GetYDay());
ptr+=1;
}
else if (strncmp(ptr,"hh",2)==0)
{
sprintf(out+strlen(out),"%02ld",GetHour());
lastWasH=2; // needs to be 1 after top of loop decs it
ptr+=2;
}
else if (strncmp(ptr,"h",1)==0)
{
sprintf(out+strlen(out),"%ld",GetHour());
lastWasH=2; // needs to be 1 after top of loop decs it
ptr+=1;
}
else if (strncmp(ptr,"nn",2)==0)
{
sprintf(out+strlen(out),"%02ld",GetMinute());
ptr+=2;
}
else if (strncmp(ptr,"n",1)==0)
{
sprintf(out+strlen(out),"%ld",GetMinute());
ptr+=1;
}
else if (strncmp(ptr,"ss",2)==0)
{
sprintf(out+strlen(out),"%02ld",GetSecond());
ptr+=2;
}
else if (strncmp(ptr,"s",1)==0)
{
sprintf(out+strlen(out),"%ld",GetSecond());
ptr+=1;
}
else if (strncmp(ptr,"ttttt",5)==0)
{
sprintf(out+strlen(out),"%ld:%02ld:%02ld",GetHour(),GetMinute(),
GetSecond());
ptr+=5;
}
// todo
// AM/PM am/pm A/P a/p AMPM
else // an unknown char, move to next
ptr++;
}
return(TRUE);
}
// In addition to PrintTime & PrintDate there is the 'Print' function
// which prints both in RFC 1123 format
void Wtime::PrintTime(FILE *out) const
{
char string[80];
PrintTime(string);
fprintf(out,"%s",string);
}
void Wtime::PrintTime(char *out) const
{
sprintf(out," %02lu:%02lu:%02lu",GetHour(),GetMinute(),GetSecond());
}
void Wtime::PrintDate(FILE *out) const
{
char string[80];
PrintDate(string);
fprintf(out,"%s",string);
}
void Wtime::PrintDate(char *out) const
{
sprintf(out,"%s, %lu %s %lu",DAYS[GetWDay()-1],GetMDay(),MONTHS[GetMonth()-1],
GetYear());
}
uint32 Wtime::GetSec(void) const
{
return(sec);
}
uint32 Wtime::GetUsec(void) const
{
return(usec);
}
void Wtime::SetSec(uint32 newsec)
{
sec=newsec;
}
void Wtime::SetUsec(uint32 newusec)
{
usec=newusec;
}
void Wtime::Set(uint32 newsec, uint32 newusec)
{
sec=newsec;
usec=newusec;
}
// Get a timeval ptr from a Wtime class
struct timeval *Wtime::GetTimeval(void)
{
static struct timeval tv;
tv.tv_sec=sec;
tv.tv_usec=usec;
return(&tv);
}
// Get a timeval ptr from a Wtime class
void Wtime::GetTimevalMT(struct timeval &tv)
{
tv.tv_sec=sec;
tv.tv_usec=usec;
}
uint32 Wtime::GetSecond(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
return(tptr->tm_sec);
}
uint32 Wtime::GetMinute(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
return(tptr->tm_min);
}
uint32 Wtime::GetHour(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
return(tptr->tm_hour);
}
uint32 Wtime::GetMDay(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
return(tptr->tm_mday);
}
uint32 Wtime::GetWDay(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
return(tptr->tm_wday+1);
}
uint32 Wtime::GetYDay(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
return(tptr->tm_yday+1);
}
uint32 Wtime::GetYWeek(void) const
{
uint32 yweek;
uint32 yday=GetYDay();
uint32 wday=GetWDay();
//phase holds the first weekday of the year. If (Jan 1 = Sun) phase = 0
sint32 phase=((wday-yday)%7);
if (phase<0) phase+=7;
yweek=((yday+phase-1)/7)+1;
return(yweek);
}
uint32 Wtime::GetMonth(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
return(tptr->tm_mon+1);
}
uint32 Wtime::GetYear(void) const
{
struct tm *tptr;
#ifndef _WINDOWS
struct tm t;
tptr=localtime_r((time_t *)&sec,&t);
#else
tptr=localtime((time_t *)&sec);
#endif
if ((tptr->tm_year)>=70)
return((tptr->tm_year)+1900);
else
return((tptr->tm_year)+2000);
}
bit8 Wtime::GetSign(void) const
{
return(sign);
}
// 1 = *this > other
//-1 = *this < other
// 0 = *this == other
int Wtime::Compare(const Wtime &other) const
{
if ((sec==other.sec)&&(usec==other.usec))
return(0); // equal
else if (sec>other.sec)
return(1);
else if (sec<other.sec)
return(-1);
else if (usec>other.usec)
return(1);
else
return(-1);
}
bit8 Wtime::operator == ( const Wtime &other ) const
{
bit8 retval=Compare(other);
if (retval==0)
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator != ( const Wtime &other ) const
{
bit8 retval=Compare(other);
if (retval==0)
return(FALSE);
else
return(TRUE);
}
bit8 Wtime::operator < ( const Wtime &other ) const
{
int retval=Compare(other);
if (retval==-1)
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator > ( const Wtime &other ) const
{
int retval=Compare(other);
if (retval==1)
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator <= ( const Wtime &other ) const
{
int retval=Compare(other);
if ((retval==-1)||(retval==0))
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator >= ( const Wtime &other ) const
{
int retval=Compare(other);
if ((retval==1)||(retval==0))
return(TRUE);
else
return(FALSE);
}
// None of the operators pay attention to sign
// only the functions that begin with 'Signed'
void Wtime::SignedAdd(const Wtime &other)
{
Wtime temp;
if ((sign==POSITIVE)&&(other.sign==POSITIVE))
{
*this+=other;
sign=POSITIVE;
}
else if ((sign==POSITIVE)&&(other.sign==NEGATIVE))
{
if (*this>other)
{
*this-=other;
sign=POSITIVE;
}
else
{
temp=other;
temp-=*this;
*this=temp;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==POSITIVE))
{
if (*this<other)
{
temp=other;
temp-=*this;
*this=temp;
sign=POSITIVE;
}
else
{
*this-=other;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==NEGATIVE))
{
*this+=other;
sign=NEGATIVE;
}
}
// None of the operators pay attention to sign
// only the functions that begin with 'Signed'
void Wtime::SignedSubtract(const Wtime &other)
{
Wtime temp;
if ((sign==POSITIVE)&&(other.sign==NEGATIVE))
{
*this+=other;
sign=POSITIVE;
}
else if ((sign==POSITIVE)&&(other.sign==POSITIVE))
{
if (*this>other)
{
*this-=other;
sign=POSITIVE;
}
else
{
temp=other;
temp-=*this;
*this=temp;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==NEGATIVE))
{
if (*this<other)
{
temp=other;
temp-=*this;
*this=temp;
sign=POSITIVE;
}
else
{
*this-=other;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==POSITIVE))
{
*this+=other;
sign=NEGATIVE;
}
}
Wtime &Wtime::operator += (const Wtime &other)
{
sec+=other.sec;
usec+=other.usec;
if (usec>1000000)
{
sec++;
usec-=1000000;
}
return *this;
}
Wtime &Wtime::operator -= (const Wtime &other)
{
sint32 temp;
if (Compare(other)==-1)
{
sec=0; // can't handle negative time
usec=0;
return *this;
}
sec-=other.sec;
temp=(sint32)usec;
temp-=(sint32)other.usec;
if (temp<0)
{
sec--;
temp+=1000000;
}
usec=temp;
return *this;
}
Wtime Wtime::operator - (Wtime &other)
{
Wtime temp(*this);
temp-=other;
return(temp);
}
Wtime Wtime::operator + (Wtime &other)
{
Wtime temp(*this);
temp+=other;
return(temp);
}
Wtime &Wtime::operator = (const Wtime &other)
{
sign=other.sign;
sec=other.sec;
usec=other.usec;
return *this;
}
Wtime &Wtime::operator += (const uint32 other)
{
sec+=other;
return *this;
}
Wtime &Wtime::operator -= (const uint32 other)
{
sec-=other;
return *this;
}
Wtime Wtime::operator - (uint32 other)
{
Wtime temp(*this);
temp-=other;
return(temp);
}
Wtime Wtime::operator + (uint32 other)
{
Wtime temp(*this);
temp+=other;
return(temp);
}
Wtime &Wtime::operator = (const uint32 other)
{
sign=POSITIVE;
sec=other;
usec=0;
return *this;
}

View File

@@ -0,0 +1,128 @@
/*
** Command & Conquer Renegade(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 <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
wtime Neal Kettler
\****************************************************************************/
#ifndef WTIME_HEADER
#define WTIME_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#ifndef _WINDOWS
#include <unistd.h>
#include <netinet/in.h>
#include <sys/time.h>
#else
#include <sys/timeb.h>
#include <winsock.h>
#endif
#include <time.h>
#include <string.h>
#include "wlib/wstypes.h"
class Wtime
{
public:
enum
{
POSITIVE=0,
NEGATIVE=1
};
Wtime(); // init to system time
Wtime( Wtime &other );
Wtime( uint32 other );
~Wtime();
void Update(); // Update members sec & usec to system time
void PrintTime(FILE *out) const;
void PrintTime(char *out) const;
void PrintDate(FILE *out) const;
void PrintDate(char *out) const;
uint32 GetSec(void) const; // Get member variable 'sec'
uint32 GetUsec(void) const; // Get member variable 'usec'
void SetSec(uint32 newsec);
void SetUsec(uint32 newusec);
void Set(uint32 newsec,uint32 newusec);
bit8 ParseDate(char *in);
bit8 FormatTime(char *out, char *format);
struct timeval *GetTimeval(void);
void GetTimevalMT(struct timeval &tv);
uint32 GetSecond(void) const; // Second (0- 60) (60 is for a leap second)
uint32 GetMinute(void) const; // Minute (0 - 59)
uint32 GetHour(void) const; // Hour (0-23)
uint32 GetMDay(void) const; // Day of Month (1-31)
uint32 GetWDay(void) const; // Day of Week (1-7)
uint32 GetYDay(void) const; // Day of Year (1-366)
uint32 GetMonth(void) const; // Month (1-12)
uint32 GetYWeek(void) const; // Week of Year (1-53)
uint32 GetYear(void) const; // Year (e.g. 1997)
bit8 GetSign(void) const; // 0 = pos 1 = neg
int Compare(const Wtime &other) const;
// comparisons
bit8 operator == ( const Wtime &other ) const;
bit8 operator != ( const Wtime &other ) const;
bit8 operator < ( const Wtime &other ) const;
bit8 operator > ( const Wtime &other ) const;
bit8 operator <= ( const Wtime &other ) const;
bit8 operator >= ( const Wtime &other ) const;
// assignments
Wtime &operator = (const Wtime &other);
Wtime &operator = (const uint32 other);
// math
// signed
void SignedAdd(const Wtime &other);
void SignedSubtract(const Wtime &other);
// unsigned
Wtime &operator += (const Wtime &other);
Wtime &operator -= (const Wtime &other);
Wtime operator + (Wtime &other);
Wtime operator - (Wtime &other);
Wtime &operator += (const uint32 other);
Wtime &operator -= (const uint32 other);
Wtime operator + (uint32 other);
Wtime operator - (uint32 other);
protected:
uint32 sec; // seconds since Jan 1, 1970
uint32 usec; // microseconds (millionths of a second)
bit8 sign; // for time differences 0 = pos 1 = neg
};
#endif