mirror of
https://github.com/electronicarts/CnC_Renegade.git
synced 2025-12-16 15:41:39 -05:00
Initial commit of Command & Conquer Renegade source code.
This commit is contained in:
122
Code/Tools/RenegadeGR/RenegadeGR.cpp
Normal file
122
Code/Tools/RenegadeGR/RenegadeGR.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
175
Code/Tools/RenegadeGR/RenegadeGR.dsp
Normal file
175
Code/Tools/RenegadeGR/RenegadeGR.dsp
Normal 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
|
||||
29
Code/Tools/RenegadeGR/RenegadeGR.dsw
Normal file
29
Code/Tools/RenegadeGR/RenegadeGR.dsw
Normal 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>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
51
Code/Tools/RenegadeGR/RenegadeGR.h
Normal file
51
Code/Tools/RenegadeGR/RenegadeGR.h
Normal 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
|
||||
63
Code/Tools/RenegadeGR/RenegadeNet.h
Normal file
63
Code/Tools/RenegadeGR/RenegadeNet.h
Normal 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
|
||||
482
Code/Tools/RenegadeGR/WENCRYPT.CPP
Normal file
482
Code/Tools/RenegadeGR/WENCRYPT.CPP
Normal 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
|
||||
96
Code/Tools/RenegadeGR/WENCRYPT.H
Normal file
96
Code/Tools/RenegadeGR/WENCRYPT.H
Normal 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
|
||||
|
||||
305
Code/Tools/RenegadeGR/field.cpp
Normal file
305
Code/Tools/RenegadeGR/field.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
99
Code/Tools/RenegadeGR/field.h
Normal file
99
Code/Tools/RenegadeGR/field.h
Normal 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
494
Code/Tools/RenegadeGR/packet.cpp
Normal file
494
Code/Tools/RenegadeGR/packet.cpp
Normal 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;
|
||||
}
|
||||
|
||||
105
Code/Tools/RenegadeGR/packet.h
Normal file
105
Code/Tools/RenegadeGR/packet.h
Normal 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;
|
||||
};
|
||||
|
||||
294
Code/Tools/RenegadeGR/rengameres.cpp
Normal file
294
Code/Tools/RenegadeGR/rengameres.cpp
Normal 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;
|
||||
}
|
||||
193
Code/Tools/RenegadeGR/rengameres.h
Normal file
193
Code/Tools/RenegadeGR/rengameres.h
Normal 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
|
||||
332
Code/Tools/RenegadeGR/tcpcon.cpp
Normal file
332
Code/Tools/RenegadeGR/tcpcon.cpp
Normal 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));
|
||||
}
|
||||
********/
|
||||
|
||||
|
||||
67
Code/Tools/RenegadeGR/tcpcon.h
Normal file
67
Code/Tools/RenegadeGR/tcpcon.h
Normal 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
|
||||
532
Code/Tools/RenegadeGR/tcpmgr.cpp
Normal file
532
Code/Tools/RenegadeGR/tcpmgr.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
162
Code/Tools/RenegadeGR/tcpmgr.h
Normal file
162
Code/Tools/RenegadeGR/tcpmgr.h
Normal 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
|
||||
809
Code/Tools/RenegadeGR/wtime.cpp
Normal file
809
Code/Tools/RenegadeGR/wtime.cpp
Normal 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;
|
||||
}
|
||||
128
Code/Tools/RenegadeGR/wtime.h
Normal file
128
Code/Tools/RenegadeGR/wtime.h
Normal 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
|
||||
Reference in New Issue
Block a user