Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA 2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

View file

@ -0,0 +1,59 @@
all = mangler
TARGET=mangler
TESTTARGET=manglertest
OBJECTS=mangler.o crc.o
TESTOBJECTS=manglertest.o crc.o
LINKFLAGS=
LINKDIRS=-L. -Lwlib -Lwnet
############################################################################
### -lpthread ### MUST MUST MUST be the last library!
############################################################################
LINKLIBS = -lwnet -lwlib -lsocket -lnsl -lposix4 -lpthread
INCDIR=-I. -Iwlib -Iwnet
CPPFLAGS=$(INCDIR) -c -gstabs+ -DDEBUG -D_UNIX
#CPPFLAGS=$(INCDIR) -c -gstabs+ -D_UNIX
CPPFILES=${OBJECTS:.o=.cpp}
CFILES=${OBJECTS:.o=.c}
.SUFFIXES : .c .cpp
$(TARGET) : $(OBJECTS)
g++ $(LINKFLAGS) $(OBJECTS) $(LINKDIRS) $(LINKLIBS) -o $(TARGET)
$(TESTTARGET) : $(TESTOBJECTS) $(TARGET)
g++ $(LINKFLAGS) $(TESTOBJECTS) $(LINKDIRS) $(LINKLIBS) -o $(TESTTARGET)
.cpp.o:
g++ $(CPPFLAGS) $<
.c.o:
g++ $(CPPFLAGS) $<
depend:
g++ -M $(INCDIR) ${CFILES} $(CPPFILES) > dependencies
clean:
-rm *.o $(TARGET) $(TESTTARGET) core *.dat
rebuild:
make clean; make
run:
make; $(TARGET)
debug:
gdb -s noxc noxc
dist:
make clean; make; cp wdtd gavin; cp START gavin; cp STOP gavin; cp world.wdt gavin;
#include dependencies

View file

@ -0,0 +1,181 @@
/*
** Command & Conquer Generals(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 <iostream>
#include <signal.h>
#ifdef _WINDOWS
#include <process.h> // *MUST* be included before ANY Wnet/Wlib headers if _REENTRANT is defined
#endif
#include "crc.h"
#include "configfile.h"
#include "threadfac.h"
#include "endian.h"
#include "xtime.h"
#include <filed.h>
#include <wstring.h>
#include <wdebug.h>
#include <udp.h>
/***************************************************************************
* Add_CRC -- Adds a value to a CRC *
* *
* INPUT: *
* crc ptr to crc *
* val value to add *
* *
* OUTPUT: *
* none *
* *
* WARNINGS: *
* none *
* *
* HISTORY: *
* 05/09/1995 BRR : Created. *
*=========================================================================*/
void Add_CRC(unsigned long *crc, unsigned char val)
{
int hibit;
//cout << "\t\t" << hex << val;
// val = htonl(val);
//cout << " / " << hex << val <<endl;
if ((*crc) & 0x80000000) {
hibit = 1;
} else {
hibit = 0;
}
(*crc) <<= 1;
(*crc) += val;
(*crc) += hibit;
//cout << hex << (*crc) <<endl;
}
void Build_Packet_CRC(unsigned char *buf, int len)
{
if (len < 5)
{
DBGMSG("Ack! Constructing a packet too small to hold a CRC!");
return;
}
if (!buf)
{
DBGMSG("Ack! Constructing a CRC for a void *");
return;
}
*((unsigned long *)buf) = 0;
unsigned long *crc_ptr = (unsigned long *)buf;
unsigned char *packetptr = (unsigned char*) (buf+4);
len -= 4; // look past CRC
for (int i=0 ; i<len ; i++) {
Add_CRC (crc_ptr, *packetptr++);
}
/*
int leftover = len & 3;
if (leftover) {
unsigned long val = 0;
unsigned char *c = (unsigned char *)packetptr;
for (int i=0; i<leftover; i++)
{
val += (c[i] << (i*8));
}
val = htonl(val);
Add_CRC (crc_ptr, val);
}
*/
*crc_ptr = htonl(*crc_ptr);
}
/***********************************************************************************************
* Passes_CRC_Check -- Checks the CRC for a packet *
* *
* *
* *
* INPUT: ptr to packet *
* *
* OUTPUT: true if packet passes CRC check *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/5/99 1:26PM ST : Created *
* 1/9/2001 2:21PM MDC: Ripped from RA2 (WinsockInterfaceClass in wsproto.cpp/queue.cpp) *
* 1/31/2001 4:30PM MDC: Converted to network-byte-order so Sparc boxes can talk with Intels. *
* 2/1/2001 4:07PM MDC: Converted back to Intel order to avoid messing with C&C packets *
*=============================================================================================*/
bool Passes_CRC_Check(unsigned char *buf, int len)
{
if (len < 5)
{
DBGMSG("Recieved packet too small to contain a CRC");
return false;
}
if (!buf)
{
DBGMSG("Ack! Checking a CRC for a void *");
return false;
}
unsigned long crc = 0;
unsigned long *crc_ptr = &crc;
unsigned char *packetptr = (unsigned char*) (buf+4);
len -= 4; // remove the CRC from packet size - just look at payload
for (int i=0 ; i<len ; i++) {
Add_CRC (crc_ptr, *packetptr++);
}
/*
int leftover = len & 3;
if (leftover) {
unsigned long val = 0;
unsigned char *c = (unsigned char *)packetptr;
for (int i=0; i<leftover; i++)
{
val += (c[i] << (i*8));
}
val = htonl(val);
Add_CRC (crc_ptr, val);
}
*/
crc = htonl(crc);
if (crc == *((unsigned long *)buf)) {
return (true);
}
DBGMSG("Invalid packet CRC");
return (false);
}

View file

@ -0,0 +1,26 @@
/*
** Command & Conquer Generals(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 __CRC_H__
#define __CRC_H__
void Build_Packet_CRC(unsigned char *buf, int len); // len includes 4-byte CRC at head
bool Passes_CRC_Check(unsigned char *buf, int len); // len includes 4-byte CRC at head
void Add_CRC(unsigned long *crc, unsigned long val);
#endif // __CRC_H__

View file

@ -0,0 +1,65 @@
/*
** Command & Conquer Generals(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 __ENDIAN_H__
#define __ENDIAN_H__
/*
** Network order is big-endian.
**
** Packet router and mangler order is big or little endian depending on server platform.
**
** Game client order is little endian.
*/
extern bool BigEndian;
template<class T> inline T Endian(T val)
{
if (!BigEndian) {
return(val);
}
/*
char temp[sizeof(T)];
*((T*)(&temp[0])) = val;
*/
T retval = 0;
/*
for (int i=0 ; i<sizeof(T) ; i++) {
retval <<= 8;
retval |= temp[i];
}
*/
int len = sizeof(T);
unsigned char *c = (unsigned char *)(&val);
for (int i=0; i<len; i++)
{
retval |= ( (*c++) << (8*i) );
}
return (retval);
}
#endif //__ENDIAN_H__

View file

@ -0,0 +1,208 @@
/*
** Command & Conquer Generals(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 <iostream.h>
#include <signal.h>
#ifdef _WINDOWS
#include <process.h> // *MUST* be included before ANY Wnet/Wlib headers if _REENTRANT is defined
#endif
#include "mangler.h"
#include "crc.h"
#include "endian.h"
#include <configfile.h>
#include "threadfac.h"
#include "xtime.h"
#include <filed.h>
#include <wstring.h>
#include <wdebug.h>
#include <udp.h>
void DisplayHelp(const char *prog)
{
cout << "Usage: " << prog << " <config file>" << endl;
exit(0);
}
int main(int argc, char **argv)
{
ConfigFile config;
FILE* conf;
if( argc <= 1 )
{
// No args - use a default config file
if ((conf = fopen("mangler.cfg", "r")) == NULL) {
cout << "Cannot open mangler.cfg for reading." << endl;
DisplayHelp(argv[0]);
}
config.readFile(conf);
fclose(conf);
}
else if( argc == 2 && (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "?") == 0 ||
strcmp(argv[1], "-h") == 0) )
DisplayHelp(argv[0]);
else if( argc == 2 )
{
// Use a user-supplied config file
if ((conf = fopen(argv[1], "r")) == NULL) {
cout << "Cannot open " << argv[1] << " for reading." << endl;
DisplayHelp(argv[0]);
}
config.readFile(conf);
fclose(conf);
}
// ----- LOGGING -----
// Setup debugging & logging output
Wstring output_file("mangler.log");
config.getString("LOGFILE", output_file);
Wstring backup_file;
backup_file = output_file;
backup_file += ".bak";
rename(output_file.get(),backup_file.get()); // save the old file
FileD output_device(output_file.get());
MsgManager::setAllStreams(&output_device);
DBGMSG("DBG working...");
INFMSG("INF working...");
WRNMSG("WRN working...");
if (htonl(0x12345678) == 0x12345678)
{
INFMSG("Host is network-byte-order");
}
else
{
INFMSG("Host is Intel-byte-order");
}
// ----- Initialize Winsock -----
#ifdef _WINDOWS
WORD verReq = MAKEWORD(2, 2);
WSADATA wsadata;
int err = WSAStartup(verReq, &wsadata);
if (err != 0) {
ERRMSG("Winsock Init failed.");
return 1;
}
if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2)) {
ERRMSG("Winsock DLL is not 2.2");
WSACleanup();
ERRMSG("Winsock Init failed.");
return 1;
}
INFMSG("Winsock Init done.");
#endif
// Set up a UDP listener
uint8 *buff=new uint8[1024];
int retval;
UDP udp;
UDP udp2;
UDP udp3;
UDP udp4;
int port = 4321;
config.getInt("PORT", port);
uint8 blitz = 0;
uint32 localIP = 0;
Wstring hostIPStr = "";
config.getString("IP", hostIPStr);
if (hostIPStr.length())
{
INFMSG("Binding to "<<hostIPStr.get()<<":"<<port<<"-"<<(port+3));
localIP = ntohl(inet_addr(hostIPStr.get()));
}
else
{
INFMSG("Binding to localhost:"<<port<<"-"<<(port+3));
}
retval = udp.Bind(localIP,(uint16)port);
retval |= udp2.Bind(localIP,(uint16)port+1);
retval |= udp3.Bind(localIP,(uint16)port+2);
retval |= udp4.Bind(localIP,(uint16)port+3);
if (retval != 0)
{
ERRMSG("Couldn't bind - error " << retval);
exit(1);
}
unsigned char buf[1024];
struct sockaddr_in addr;
int packet_size = sizeof(ManglerData);
INFMSG("sizeof(packet) == " << packet_size);
unsigned char *theAddr;
fd_set fdset;
while (1)
{
retval = udp.Wait(15, 0, fdset);
if (!retval)
continue;
//DBGMSG("Wait returned " << retval);
retval = udp.Read(buf, packet_size, &addr); // Wait until there is something on the socket
if (retval > 0)
{
ManglerData *packet = (ManglerData *)buf;
theAddr = (unsigned char *)&(addr.sin_addr.s_addr);
if (retval != packet_size)
{
WRNMSG("Recieved mis-sized packet (" << retval << " bytes) from " << theAddr[0] << "." << theAddr[1] << "." << theAddr[2] << "." << theAddr[3] << ":" << addr.sin_port);
}
else
{
if (!Passes_CRC_Check(buf, packet_size))
{
WRNMSG("Recieved a bad packet - good length!");
continue;
}
packet->NetCommandType = 44;
packet->MyMangledPortNumber = addr.sin_port; // not changing to host order, cause its in network byte order now, and the game will expect it to stay that way.
packet->MyMangledAddress[0] = theAddr[0];
packet->MyMangledAddress[1] = theAddr[1];
packet->MyMangledAddress[2] = theAddr[2];
packet->MyMangledAddress[3] = theAddr[3];
blitz = packet->BlitzMe;
INFMSG("Packet ID = " << packet->packetID);
Build_Packet_CRC(buf, packet_size);
udp.Write(buf,packet_size,ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
INFMSG("Saw " << (int)theAddr[0] << "." << (int)theAddr[1] << "." << (int)theAddr[2] << "." << (int)theAddr[3] << ":" << ntohs(addr.sin_port) << ((blitz)?" Blitzed":"") );
if (blitz)
{
udp2.Write(buf,packet_size,ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port)+1);
udp3.Write(buf,packet_size,ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port)+2);
udp4.Write(buf,packet_size,ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port)+3);
}
}
}
}
return 0;
}

View file

@ -0,0 +1,326 @@
# Microsoft Developer Studio Project File - Name="mangler" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=mangler - 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 "mangler.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 "mangler.mak" CFG="mangler - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "mangler - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "mangler - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "mangler"
# PROP Scc_LocalPath ".."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "mangler - 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 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /I "wlib" /I "wnet" /I "." /D "_WINDOWS" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# 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 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 /subsystem:console /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 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 /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "mangler - 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 /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "wlib" /I "wnet" /I "." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_WINDOWS" /D "DEBUG" /YX /FD /GZ /c
# 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 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 /subsystem:console /debug /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 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 /subsystem:console /debug /machine:I386
!ENDIF
# Begin Target
# Name "mangler - Win32 Release"
# Name "mangler - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Group "wlib"
# PROP Default_Filter ""
# Begin Source File
SOURCE=wlib\configfile.cpp
# End Source File
# Begin Source File
SOURCE=wlib\critsec.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\Makefile
# End Source File
# Begin Source File
SOURCE=wlib\monod.cpp
# End Source File
# Begin Source File
SOURCE=wlib\sem4.cpp
# End Source File
# Begin Source File
SOURCE=wlib\streamer.cpp
# End Source File
# Begin Source File
SOURCE=wlib\syslogd.cpp
# End Source File
# Begin Source File
SOURCE=wlib\threadfac.cpp
# End Source File
# Begin Source File
SOURCE=wlib\timezone.cpp
# End Source File
# Begin Source File
SOURCE=wlib\wdebug.cpp
# End Source File
# Begin Source File
SOURCE=wlib\wstring.cpp
# End Source File
# Begin Source File
SOURCE=wlib\wtime.cpp
# End Source File
# Begin Source File
SOURCE=wlib\xtime.cpp
# End Source File
# End Group
# Begin Group "wnet"
# PROP Default_Filter ""
# Begin Source File
SOURCE=wnet\field.cpp
# End Source File
# Begin Source File
SOURCE=.\wnet\Makefile
# End Source File
# Begin Source File
SOURCE=wnet\packet.cpp
# End Source File
# Begin Source File
SOURCE=wnet\tcp.cpp
# End Source File
# Begin Source File
SOURCE=wnet\udp.cpp
# End Source File
# End Group
# Begin Source File
SOURCE=.\crc.cpp
# End Source File
# Begin Source File
SOURCE=.\Makefile
# End Source File
# Begin Source File
SOURCE=.\mangler.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Group "wlib.H"
# PROP Default_Filter ""
# Begin Source File
SOURCE=wlib\arraylist.h
# End Source File
# Begin Source File
SOURCE=wlib\configfile.h
# End Source File
# Begin Source File
SOURCE=wlib\critsec.h
# End Source File
# Begin Source File
SOURCE=wlib\dictionary.h
# End Source File
# Begin Source File
SOURCE=wlib\filed.h
# End Source File
# Begin Source File
SOURCE=wlib\linkedlist.h
# End Source File
# Begin Source File
SOURCE=wlib\mboxd.h
# End Source File
# Begin Source File
SOURCE=wlib\monod.h
# End Source File
# Begin Source File
SOURCE=wlib\odevice.h
# End Source File
# Begin Source File
SOURCE=wlib\sem4.h
# End Source File
# Begin Source File
SOURCE=wlib\stderrd.h
# End Source File
# Begin Source File
SOURCE=wlib\stdoutd.h
# End Source File
# Begin Source File
SOURCE=wlib\streamer.h
# End Source File
# Begin Source File
SOURCE=wlib\syslogd.h
# End Source File
# Begin Source File
SOURCE=wlib\threadfac.h
# End Source File
# Begin Source File
SOURCE=wlib\threadsafe.h
# End Source File
# Begin Source File
SOURCE=wlib\timezone.h
# End Source File
# Begin Source File
SOURCE=wlib\ustring.h
# End Source File
# Begin Source File
SOURCE=wlib\wdebug.h
# End Source File
# Begin Source File
SOURCE=wlib\wstring.h
# End Source File
# Begin Source File
SOURCE=wlib\wstypes.h
# End Source File
# Begin Source File
SOURCE=wlib\wtime.h
# End Source File
# Begin Source File
SOURCE=wlib\xtime.h
# End Source File
# End Group
# Begin Group "wnet.H"
# PROP Default_Filter ""
# Begin Source File
SOURCE=wnet\field.h
# End Source File
# Begin Source File
SOURCE=wnet\packet.h
# End Source File
# Begin Source File
SOURCE=wnet\tcp.h
# End Source File
# Begin Source File
SOURCE=wnet\udp.h
# End Source File
# End Group
# Begin Source File
SOURCE=.\crc.h
# End Source File
# Begin Source File
SOURCE=.\endian.h
# End Source File
# Begin Source File
SOURCE=.\mangler.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
# Begin Source File
SOURCE=.\mangler.cfg
# End Source File
# Begin Source File
SOURCE=.\mangler.log
# End Source File
# End Target
# End Project

View file

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

View file

@ -0,0 +1,79 @@
/*
** Command & Conquer Generals(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 __MANGLER_H__
#define __MANGLER_H__
// Packet should consist of a GlobalHeaderType followed by a GlobalPacketType with the fields set as
// indicated.
/*
********************************** Defines **********************************
*/
// This is the number of additional ports to which to reply.
#define BLITZ_SIZE 3
#define MPLAYER_NAME_MAX 20
#define SERIAL_MAX 23
typedef unsigned char ForwardMaskType;
typedef enum NetCommandType {
NET_MANGLER_REQUEST = 43,
NET_MANGLER_RESPONSE = 44
} NetCommandType;
/*
** One byte alignment.
*/
#if !defined(__GNUC__)
#pragma pack(push, 1)
#define PACK
#else
#define PACK __attribute__ ((__packed__))
#endif
// size = 20 bytes
struct ManglerData {
unsigned int CRC PACK;
unsigned short magic PACK;
unsigned short packetID PACK;
unsigned short MyMangledPortNumber PACK;
unsigned short OriginalPortNumber PACK;
unsigned char MyMangledAddress[4] PACK;
unsigned char NetCommandType PACK;
unsigned char BlitzMe PACK;
unsigned short Padding PACK;
};
/*
This is for older GCC versions that can't do byte-packing.
Instead of declaring
GlobalPacketType p;
p.Command = NET_MANGLER_REQUEST;
you would do something like this:
GlobalPacketStruct p;
Eval(&p, GPCommand, NetCommandType) = NET_MANGLER_REQUEST;
*/
#define Eval(x, y, z) ( *((z *)(&((x)->payload[(y)]))) )
#if !defined(__GNUC__)
#pragma pack(pop)
#endif
#endif // __MANGLER_H__

View file

@ -0,0 +1,245 @@
/*
** Command & Conquer Generals(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 <iostream.h>
#include <signal.h>
#ifdef _WINDOWS
#include <process.h> // *MUST* be included before ANY Wnet/Wlib headers if _REENTRANT is defined
#endif
#include "mangler.h"
#include "crc.h"
#include <configfile.h>
#include "threadfac.h"
#include "endian.h"
#include "xtime.h"
#include <filed.h>
#include <wstring.h>
#include <wdebug.h>
#include <udp.h>
// ST - 2/1/01 12:46PM
bool BigEndian = false;
unsigned long ResolveIP(char *Server)
{
char serverName[100];
struct hostent *serverStruct;
struct in_addr *serverNode;
if (Server == NULL)
{
ERRMSG("Can't resolve NULL");
return 0;
}
if (isdigit(Server[0]))
return ( ntohl(inet_addr(Server)) );
strcpy(serverName, Server);
serverStruct = gethostbyname(Server);
if (serverStruct == NULL)
{
ERRMSG("Can't resolve " << Server);
return 0;
}
serverNode = (struct in_addr *) serverStruct->h_addr;
return ( ntohl(serverNode->s_addr) );
}
void DisplayHelp(const char *prog)
{
cout << "Usage: " << prog << " <config file>" << endl;
exit(0);
}
int main(int argc, char **argv)
{
ConfigFile config;
FILE* conf;
if( argc <= 1 )
{
// No args - use a default config file
if ((conf = fopen("manglertest.cfg", "r")) == NULL) {
cout << "Cannot open mangler.cfg for reading." << endl;
DisplayHelp(argv[0]);
}
config.readFile(conf);
fclose(conf);
}
else if( argc == 2 && (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "?") == 0 ||
strcmp(argv[1], "-h") == 0) )
DisplayHelp(argv[0]);
else if( argc == 2 )
{
// Use a user-supplied config file
if ((conf = fopen(argv[1], "r")) == NULL) {
cout << "Cannot open " << argv[1] << " for reading." << endl;
DisplayHelp(argv[0]);
}
config.readFile(conf);
fclose(conf);
}
// ----- LOGGING -----
// Setup debugging & logging output
Wstring output_file("manglertest.log");
config.getString("LOGFILE", output_file);
Wstring backup_file;
backup_file = output_file;
backup_file += ".bak";
rename(output_file.get(),backup_file.get()); // save the old file
FileD output_device(output_file.get());
MsgManager::setAllStreams(&output_device);
DBGMSG("DBG working...");
INFMSG("INF working...");
WRNMSG("WRN working...");
//
// See if our processor is big or little endian. Network order is big endian.
// ST - 2/1/01 12:11PM
//
if (htonl(0x12345678) == 0x12345678) {
BigEndian = true;
}
// ----- Initialize Winsock -----
#ifdef _WINDOWS
WORD verReq = MAKEWORD(2, 2);
WSADATA wsadata;
int err = WSAStartup(verReq, &wsadata);
if (err != 0) {
ERRMSG("Winsock Init failed.");
return 1;
}
if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2)) {
ERRMSG("Winsock DLL is not 2.2");
WSACleanup();
ERRMSG("Winsock Init failed.");
return 1;
}
INFMSG("Winsock Init done.");
#endif
// Set up a UDP listener
uint8 *buff=new uint8[1024];
int retval;
UDP udp;
int port = 4321;
config.getInt("MANGLERPORT", port);
int localport = 4444;
config.getInt("CLIENTPORT", localport);
retval = udp.Bind((uint32)0,(uint16)localport);
DBGMSG("Bind returned " << retval);
//-----------------------------------------------------------------------------------------
const int packet_size = sizeof(ManglerData);
INFMSG("sizeof(packet) == " << packet_size);
unsigned char buf[packet_size];
memset(buf, 0x44, packet_size); // init to something known for memory dumps :)
struct sockaddr_in addr;
int doBlitz = 0;
config.getInt("BLITZ", doBlitz);
if (doBlitz)
{
INFMSG("Requsting port blitz");
}
unsigned char *theAddr;
fd_set fdset;
unsigned long server_addr;
Wstring manglername = "localhost";
config.getString("MANGLERIP", manglername);
server_addr = ResolveIP(manglername.get());
if (!server_addr)
{
ERRMSG("Cannot resolve mangler server IP");
return 1;
}
ManglerData *packet = (ManglerData *)buf;
packet->NetCommandType = 12;
packet->packetID = 9999;
packet->BlitzMe = doBlitz;
packet->magic = htons((unsigned short)0xf00d);
Build_Packet_CRC(buf, packet_size);
DBGMSG("Writing to " << manglername.get() << ":" << port);
udp.Write(buf,packet_size,server_addr, 4321);
retval = udp.Wait(5, 0, fdset);
if (retval)
{
DBGMSG("Wait returned " << retval);
retval = udp.Read(buf, packet_size, &addr); // Wait until there is something on the socket
if (retval > 0)
{
theAddr = (unsigned char *)&(addr.sin_addr.s_addr);
if (retval != packet_size)
{
WRNMSG("Recieved mis-sized packet (" << retval << " bytes) from " << theAddr[0] << "." << theAddr[1] << "." << theAddr[2] << "." << theAddr[3] << ":" << addr.sin_port);
}
else
{
int packetCommand;
packetCommand = packet->NetCommandType;
if (!Passes_CRC_Check(buf, packet_size))
{
WRNMSG("CRC error!");
}
else if (packetCommand != 13)
{
WRNMSG("Returned packet had command type " << packetCommand);
}
else
{
int addr[4];
unsigned short retPort;
retPort = htons(packet->MyMangledPortNumber);
addr[0] = packet->MyMangledAddress[0];
addr[1] = packet->MyMangledAddress[1];
addr[2] = packet->MyMangledAddress[2];
addr[3] = packet->MyMangledAddress[3];
DBGMSG("Saw " << addr[0] << "." << addr[1] << "." << addr[2] << "." << addr[3] << ":" << retPort);
}
}
}
}
else
{
DBGMSG("Wait timed out");
}
return 0;
}

View file

@ -0,0 +1,318 @@
# Microsoft Developer Studio Project File - Name="manglertest" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=manglertest - 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 "manglertest.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 "manglertest.mak" CFG="manglertest - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "manglertest - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "manglertest - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "manglertest"
# PROP Scc_LocalPath ".."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "manglertest - 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 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /I "wlib" /I "wnet" /I "." /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WINDOWS" /YX /FD /c
# 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 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 /subsystem:console /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 /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "manglertest - 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 /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /I "wlib" /I "wnet" /I "." /D "_DEBUG" /D "_WINDOWS" /D "DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# 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 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 /subsystem:console /debug /machine:I386
# ADD LINK32 wsock32.lib 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 /subsystem:console /debug /machine:I386
!ENDIF
# Begin Target
# Name "manglertest - Win32 Release"
# Name "manglertest - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Group "wnet"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\wnet\field.cpp
# End Source File
# Begin Source File
SOURCE=.\wnet\Makefile
# End Source File
# Begin Source File
SOURCE=.\wnet\packet.cpp
# End Source File
# Begin Source File
SOURCE=.\wnet\tcp.cpp
# End Source File
# Begin Source File
SOURCE=.\wnet\udp.cpp
# End Source File
# End Group
# Begin Group "wlib"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\wlib\configfile.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\critsec.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\Makefile
# End Source File
# Begin Source File
SOURCE=.\wlib\monod.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\sem4.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\streamer.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\syslogd.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\threadfac.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\timezone.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\wdebug.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\wstring.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\wtime.cpp
# End Source File
# Begin Source File
SOURCE=.\wlib\xtime.cpp
# End Source File
# End Group
# Begin Source File
SOURCE=.\crc.cpp
# End Source File
# Begin Source File
SOURCE=.\manglertest.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Group "wnet.H"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\wnet\field.h
# End Source File
# Begin Source File
SOURCE=.\wnet\packet.h
# End Source File
# Begin Source File
SOURCE=.\wnet\tcp.h
# End Source File
# Begin Source File
SOURCE=.\wnet\udp.h
# End Source File
# End Group
# Begin Group "wlib.H"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\wlib\arraylist.h
# End Source File
# Begin Source File
SOURCE=.\wlib\configfile.h
# End Source File
# Begin Source File
SOURCE=.\wlib\critsec.h
# End Source File
# Begin Source File
SOURCE=.\wlib\dictionary.h
# End Source File
# Begin Source File
SOURCE=.\wlib\filed.h
# End Source File
# Begin Source File
SOURCE=.\wlib\linkedlist.h
# End Source File
# Begin Source File
SOURCE=.\wlib\mboxd.h
# End Source File
# Begin Source File
SOURCE=.\wlib\monod.h
# End Source File
# Begin Source File
SOURCE=.\wlib\odevice.h
# End Source File
# Begin Source File
SOURCE=.\wlib\sem4.h
# End Source File
# Begin Source File
SOURCE=.\wlib\stderrd.h
# End Source File
# Begin Source File
SOURCE=.\wlib\stdoutd.h
# End Source File
# Begin Source File
SOURCE=.\wlib\streamer.h
# End Source File
# Begin Source File
SOURCE=.\wlib\syslogd.h
# End Source File
# Begin Source File
SOURCE=.\wlib\threadfac.h
# End Source File
# Begin Source File
SOURCE=.\wlib\threadsafe.h
# End Source File
# Begin Source File
SOURCE=.\wlib\timezone.h
# End Source File
# Begin Source File
SOURCE=.\wlib\ustring.h
# End Source File
# Begin Source File
SOURCE=.\wlib\wdebug.h
# End Source File
# Begin Source File
SOURCE=.\wlib\wstring.h
# End Source File
# Begin Source File
SOURCE=.\wlib\wstypes.h
# End Source File
# Begin Source File
SOURCE=.\wlib\wtime.h
# End Source File
# Begin Source File
SOURCE=.\wlib\xtime.h
# End Source File
# End Group
# Begin Source File
SOURCE=.\crc.h
# End Source File
# Begin Source File
SOURCE=.\endian.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
# Begin Source File
SOURCE=.\manglertest.cfg
# End Source File
# Begin Source File
SOURCE=.\manglertest.log
# End Source File
# End Target
# End Project

View file

@ -0,0 +1,41 @@
###########################################################################
# WLIB library makefile
##########################################################################
#Define cc to be your C compiler
CC = g++
#This tells make how to go from a .cpp to a .o
.SUFFIXES: .cpp
.cpp.o:
${CC} ${CFLAGS} -c $<
INCLUDE = -I. -I..
CFLAGS = ${INCLUDE} -D_REENTRANT -DDEBUG -D_UNIX
AR = ar -r
#CC is dumb and won't include templates in a library uness you define
#CC -xar as your replacement for 'ar'
#AR = CC -xar
RM = rm -f
RANLIB = ranlib
############################################################################
#Dont mess with any of this stuff
OBJECTS = wtime.o monod.o wdebug.o sem4.o streamer.o syslogd.o wstring.o \
configfile.o threadfac.o critsec.o xtime.o timezone.o
LIBRARY = libwlib.a
all: $(LIBRARY)
$(LIBRARY): $(OBJECTS)
${RM} libwlib.a
${AR} libwlib.a $(OBJECTS)
#${AR} $(OBJECTS) -o libwlib.a
$(RANLIB) libwlib.a
clean:
- rm -f $(LIBRARY) *.o core

View file

@ -0,0 +1,709 @@
/*
** Command & Conquer Generals(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:
File Name : arraylist.h
Author : Neal Kettler
Start Date : Jan 19, 1997
Last Update : Jan 19, 1997
------------------------------------------------------------------------------
Array implementation of a list. Note: There are some freaky C++ memory tricks
going on here. If you think there's a leak, see me before changing it.
The way this works is that it allocates an array to hold 'N' items on the
first list add. It doesn't call the constructors for those 'N' items until
necessary (for efficiency). When an item is added to a slot then a new
class is constructed inside the array element using the placement new operator
and the class's copy constructor. When elements are removed the destructor
is then manually called on this memory location.
All data added to the list is copied so feel free to delete/destroy/modify
the original after an add.
You _must_ have a good copy constructor for any classes that you use this template
for! A good copy constructor is one that won't blindly duplicate pointers
that don't belong to them, etc...
\****************************************************************************/
#ifndef ARRAYLIST_HEADER
#define ARRAYLIST_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <new.h>
#include <math.h>
#include "wstypes.h"
//
// Usage: ArrayList<int> TheList;
//
template <class T>
class ArrayList
{
public:
ArrayList();
ArrayList(ArrayList<T> &other);
~ArrayList();
// Remove all entries from the lsit
void clear(void);
// Add a node after the zero based 'pos'
bit8 add(IN T &node,sint32 pos);
bit8 addTail(IN T &node);
bit8 addHead(IN T &node);
bit8 addSortedAsc(IN T &node); // Ascending
bit8 addSortedDes(IN T &node); // Descending
/*bit8 addNumSortedAsc(IN T &node); // Ascending
bit8 addNumSortedDes(IN T &node); // Descending*/
// Remove a node
bit8 remove(OUT T &node,sint32 pos);
bit8 remove(sint32 pos);
bit8 removeHead(OUT T &node);
bit8 removeTail(OUT T &node);
// Replace one obj with another
bit8 replace(IN T &node, sint32 pos);
// Get a node without removing from the list
bit8 get(OUT T &node,sint32 pos) RO;
bit8 getHead(OUT T &node) RO;
bit8 getTail(OUT T &node) RO;
// Get a pointer to the interally managed copy (careful!)
bit8 getPointer(OUT T **node,sint32 pos) RO;
// Get the number of entries in the list
sint32 length(void) RO;
// UNSAFE! for classes, see note below!
bit8 setSize(sint32 newsize, IN T &filler);
// Print information on the list
void print(FILE *out);
// assignment operator
ArrayList<T> &operator=(IN ArrayList<T> &other);
private:
sint32 _sortedLookup(IN T &target, int ascending);
sint32 Entries_; // Number of entries
sint32 Slots_; // Number of available slots
T *Vector_; // The actual memory where the list is held
enum
{
INITIAL_SIZE = 10
};
bit8 growVector(void); // Expand the number of slots
bit8 shrinkVector(void); // Reduce the number of slots
};
//Create the empty list
template <class T>
ArrayList<T>::ArrayList()
{
Entries_=0;
Slots_=0;
Vector_=NULL;
}
// copy constructor
template <class T>
ArrayList<T>::ArrayList(ArrayList<T> &other)
{
Entries_=0;
Slots_=0;
Vector_=NULL;
(*this)=other;
}
//Free all the memory...
template <class T>
ArrayList<T>::~ArrayList()
{
clear(); // Remove the entries & call destructors on them
delete[]((uint8*)Vector_); // this will prevent the destructors from
// gettting called on elements not
// containing valid objects.
//fprintf(stderr,"Arraylist destructor\n");
}
// assignment operator
template <class T>
ArrayList<T> &ArrayList<T>::operator=(IN ArrayList<T> &other)
{
T node;
clear();
for (int i=0; i<other.length(); i++)
{
other.get(node,i);
addTail(node);
}
return(*this);
}
// Remove all the entries and free the memory
template <class T>
void ArrayList<T>::clear()
{
for (int i=0; i<Entries_; i++)
{
(Vector_+i)->~T(); // Call the destructor manually. Don't try this
// at home kiddies!
}
Entries_=0;
}
// ************************* UNSAFE UNSAFE UNSAFE *************************
// Note - Don't call this on any type with a constructor/destructor since this
// is really dumb and just puts a new one of filler in. Well, it's kindof safe
// just be careful.
// It's fine for simple classes like ints though..
//
// Add/remove entries in a stupid manner...
//
// **************************************************************************
template <class T>
bit8 ArrayList<T>::setSize(sint32 newsize, IN T &filler)
{
int oldEntries=Entries_;
Entries_ = newsize;
if (newsize<0)
return(false);
// Grow the vector as much as we need to
while (newsize > Slots_)
growVector();
// Create new objects in the blank holes
for (int i=oldEntries; i<Entries_; i++)
{
// Now put the replacement object in there...
new((void *)(Vector_+i)) T(filler); // Trust me, this isn't a memory leak
}
// If we're at 33% usage or less, shrink the vector
if ((Entries_*3) <= Slots_) // don't do while, because I think shrink will never goto 0
shrinkVector();
return(true);
}
// When adding into a position, the new node goes at the zero based slot
// specified by pos. All other nodes get moved one slot down.
template <class T>
bit8 ArrayList<T>::add(IN T &node,sint32 pos)
{
if (pos > Entries_) // You can only access one of the end of the vector
pos=Entries_;
if (pos >= Slots_) // If we're at the end, grow the list
growVector();
if (Entries_ >= Slots_) // enuff space?
growVector();
// If we are insering into the middle or front of the list we have to
// slide the old objects forward.
if (pos < Entries_) // If there are elements after the add point
memmove(Vector_+pos+1,Vector_+pos,sizeof(T)*(Entries_-pos)); // move them forward
//fprintf(stderr,"Placement new to %p\n",(Vector_+pos));
// This uses the placement new operator. placement new allows us to
// specify the memory address for the new object. In this case we
// want it at the 'pos' index into our array.
new((void *)(Vector_+pos)) T((T &)node); // Trust me, this isn't a memory leak
Entries_++; // one new entry
return(TRUE);
}
// Add to the first node, all others get shifted down one slot
template <class T>
bit8 ArrayList<T>::addHead(IN T &node)
{
return(add(node,0));
}
// Append to the end of the list
template <class T>
bit8 ArrayList<T>::addTail(IN T &node)
{
return(add(node,length()));
}
// addSortedX only works (properly) if evrerything else in the list is added
// using addSorted.
template <class T>
bit8 ArrayList<T>::addSortedAsc(IN T &node)
{
sint32 pos = _sortedLookup(node, 1);
return(add(node, pos));
}
// addSortedX only works (properly) if evrerything else in the list is added
// using addSorted.
template <class T>
bit8 ArrayList<T>::addSortedDes(IN T &node)
{
sint32 pos = _sortedLookup(node, 0);
return(add(node, pos));
}
// This is the binary search used by addSorted
template <class T>
sint32 ArrayList<T>::_sortedLookup(IN T &target, int ascending)
{
int low, mid, high;
T* lowtarget;
T* hightarget;
T* midtarget;
// Trivial cases
if( Entries_ == 0 )
return 0;
low = 0;
high = Entries_ - 1;
while( 1 )
{
assert( low <= high );
mid = low + (int)(floor(((double)high - (double)low) / (double)2));
getPointer(&lowtarget, low);
getPointer(&hightarget, high);
getPointer(&midtarget, mid);
// Exact match
if( *midtarget == target ) return mid;
// Single element
if( high == low )
{
if( ascending )
{
if( target <= *lowtarget )
return low;
else
return low + 1;
}
else
{
if( target <= *lowtarget )
return low + 1;
else
return low;
}
}
// Two elemsnts
if( (high - low) == 1 )
{
if( ascending )
{
if( target <= *lowtarget )
return low;
else if( target <= *hightarget )
return high;
else
return high + 1;
}
else
{
if( target <= *hightarget )
return high + 1;
else if( target <= *lowtarget )
return high;
else
return low;
}
}
// Sorry, try again...
if( ascending )
{
if( target < *midtarget )
high = mid;
else
low = mid;
}
else
{
if( target < *midtarget )
low = mid;
else
high = mid;
}
}
}
/*// addNumSortedX works in much the same way as addSortedX, except that I needed
// it for a very specific thing. I needed a list of strings numerically sorted,
// not alphabetically sorted. Furthermore these strings were composed of numbers
// delimited by underscores. In the interest of keeping it generic, these
// functions take as args a node, a delimiting character, and a count of the
// number of fields to include in a sort. If this is the list of strings:
//
// 55_100, 2_5, 23_32, 98_445, 2_48, 8_88, 2_3, 2_4
//
// An alphabetical sort is:
//
// 2_3, 2_4, 2_48, 2_5, 55_100, 8_88, 98_445
//
// But a numerical sort by calling addNumSortedAsc(<whatever>, "_", 2) will result in:
//
// 2_3, 2_4, 2_5, 2_48, 8_88, 55_100, 98_445
//
// Yes...now that you mention it I am on crack...
//
template <class T>
bit8 ArrayList<T>::addNumSortedAsc(IN T &node, char delim, int fields)
{
sint32 pos = _numSortedLookup(node, delim, fields, 1);
return(add(node, pos));
}
// See addNumSortedAsc comment above.
template <class T>
bit8 ArrayList<T>::addSortedDes(IN T &node, char delim, int fields)
{
sint32 pos = _sortedLookup(node, delim, fields, 0);
return(add(node, pos));
}
// This is the binary search used by addSorted
template <class T>
sint32 ArrayList<T>::_numSortedLookup(IN T &target, char delim, int fields, int ascending)
{
int low, mid, high;
T* lowtarget;
T* hightarget;
T* midtarget;
// Trivial case
if( Entries_ == 0 )
return 0;
low = 0;
high = Entries_;
while( 1 )
{
assert( low <= high );
mid = low + (int)(floor(((double)high - (double)low) / (double)2));
getPointer(&lowtarget, low);
getPointer(&hightarget, high);
getPointer(&midtarget, mid);
// Exact match
if( *midtarget == target ) return mid;
// Single element
if( high == low )
{
if( ascending )
{
if( target <= *lowtarget )
return low;
else
return low + 1;
}
else
{
if( target <= *lowtarget )
return low + 1;
else
return low;
}
}
// Two elemsnts
if( (high - low) == 1 )
{
if( ascending )
{
if( target <= *lowtarget )
return low;
else
return high;
}
else
{
if( target <= *lowtarget )
return high;
else
return low;
}
}
// Sorry, try again...
if( ascending )
{
if( target < *midtarget )
high = mid;
else
low = mid;
}
else
{
if( target < *midtarget )
low = mid;
else
high = mid;
}
}
}*/
//
// Delete an item at this index and construct a new one in it's place
//
template <class T>
bit8 ArrayList<T>::replace(IN T &node, sint32 pos)
{
if (Entries_==0)
return(FALSE);
if (pos<0)
pos=0;
if (pos >= Entries_)
pos=Entries_-1;
(Vector_+pos)->~T(); // Call the destructor manually. Don't try this
// at home kiddies!
// Now put the replacement object in there...
new((void *)(Vector_+pos)) T(node); // Trust me, this isn't a memory leak
return(TRUE);
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 ArrayList<T>::remove(sint32 pos)
{
if (Entries_==0)
return(FALSE);
if (pos<0)
pos=0;
if (pos >= Entries_)
pos=Entries_-1;
(Vector_+pos)->~T(); // Call the destructor manually. Don't try this
// at home kiddies!
memmove(Vector_+pos,Vector_+pos+1,sizeof(T)*(Entries_-pos-1));
Entries_--;
// If we're at 33% usage or less, shrink the vector
if ( (Entries_*3) <= Slots_)
shrinkVector();
return(TRUE);
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 ArrayList<T>::remove(OUT T &node, sint32 pos)
{
bit8 retval;
retval=get(node,pos);
if (retval==FALSE)
return(FALSE);
return(remove(pos));
}
// Remove the first node of the list
template <class T>
bit8 ArrayList<T>::removeHead(OUT T &node)
{
return(remove(node,0));
}
// Remove the last node of the list
template <class T>
bit8 ArrayList<T>::removeTail(OUT T &node)
{
return(remove(node,Entries_-1));
}
// get a pointer to the internally managed object. Try and avoid this, but
// sometimes efficiency requires it...
// get a copy of an item
template <class T>
bit8 ArrayList<T>::getPointer(OUT T **node,sint32 pos) RO
{
if ((pos < 0)||(pos >= Entries_))
return(FALSE);
*node=&(Vector_[pos]);
return(TRUE);
}
// get a copy of an item
template <class T>
bit8 ArrayList<T>::get(OUT T &node,sint32 pos) RO
{
if ((pos < 0)||(pos >= Entries_))
return(FALSE);
node=Vector_[pos];
return(TRUE);
}
// get a copy of the first node of the list
template <class T>
bit8 ArrayList<T>::getHead(OUT T &node) RO
{
return(get(node,0));
}
// get a copy of the last node
template <class T>
bit8 ArrayList<T>::getTail(OUT T &node) RO
{
return(get(node,Entries_-1));
}
// just for debugging
template <class T>
void ArrayList<T>::print(FILE *out)
{
fprintf(out,"--------------------\n");
//for (int i=0; i<Entries_; i++)
// Vector_[i].print();
fprintf(out,"Entries: %d Slots: %d sizeof(T): %d\n",Entries_,Slots_,
sizeof(T));
fprintf(out,"--------------------\n");
}
// Return the current length of the list
template <class T>
sint32 ArrayList<T>::length(void) RO
{
return(Entries_);
}
// Grow the vector by a factor of 2X
template <class T>
bit8 ArrayList<T>::growVector(void)
{
if (Entries_ < Slots_) // Don't grow until we're at 100% usage
return(FALSE);
int newSlots=Entries_*2;
if(newSlots < INITIAL_SIZE)
newSlots=INITIAL_SIZE;
//fprintf(stderr,"Growing vector to: %d\n",newSlots);
// The goofy looking new below prevents operator new from getting called
// unnecessarily. This is severall times faster than allocating all of
// the slots as objects and then calling the assignment operator on them
// when they actually get used.
//
T *newVector=(T *)(new uint8[newSlots * sizeof(T)]);
memset(newVector,0,newSlots * sizeof(T)); // zero just to be safe
if (Vector_ != NULL)
memcpy(newVector,Vector_,Entries_*sizeof(T));
delete[]((uint8 *)Vector_); // Get rid of the old vector without calling
// destructors
Vector_=newVector;
Slots_=newSlots;
return(TRUE);
}
// Shrink the vector by a factor of 2X
template <class T>
bit8 ArrayList<T>::shrinkVector(void)
{
//fprintf(stderr,"Shrink called\n");
// Don't need to shrink until usage goes below 33%
if ( (Entries_*3) > Slots_)
return(FALSE);
int newSlots=Slots_/2;
if(newSlots < INITIAL_SIZE) // never shrink past initial size
newSlots=INITIAL_SIZE;
if (newSlots >= Slots_) // don't need to shrink
return(FALSE);
//fprintf(stderr,"Shrinking vector to: %d\n",newSlots);
// The goofy looking new below prevents operator new from getting called
// unnecessarily. This is severall times faster than allocating all of
// the slots as objects and then calling the assignment operator on them
// when they actually get used.
//
T *newVector=(T *)(new uint8[newSlots * sizeof(T)]);
if (Vector_ != NULL) // Vector_ better not be NULL!
memcpy(newVector,Vector_,Entries_*sizeof(T));
delete[]((uint8 *)Vector_); // Get rid of the old vector without calling
// destructors
Vector_=newVector;
Slots_=newSlots;
return(TRUE);
}
#endif

View file

@ -0,0 +1,483 @@
/*
** Command & Conquer Generals(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: Carpenter (The RedAlert ladder creator)
File Name : configfile.cpp
Author : Neal Kettler
Start Date : June 9, 1997
Last Update : June 17, 1997
This class will read in a config file and store the key value pairs for
later access. This is a fairly simple class, the config file is assumed
to be of the form:
#comment
key = value
The value can then be retrieved as a string or an integer. The key on
the left is used for retrieval and it must be specified in uppercase
for the 'get' functions. E.g. getString("KEY",valWstring);
\***************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "configfile.h"
#include "wdebug.h"
static uint32 Wstring_Hash(const Wstring &string);
static char *Eat_Spaces(char *string);
ConfigFile::ConfigFile() : Dictionary_(Wstring_Hash)
{ }
ConfigFile::~ConfigFile()
{ }
// Read and parse the config file. The key value pairs will be stored
// for later access by the getString/getInt functions.
bit8 ConfigFile::readFile(FILE *in)
{
char string[256];
char sectionname[256]; // section name like '[user parameters]'
Wstring key;
Wstring value;
char *cptr;
memset(string,0,256);
memset(sectionname,0,256);
sectionList.clear();
while (fgets(string,256,in))
{
cptr=Eat_Spaces(string);
if ((*cptr==0)||(*cptr=='#')) // '#' signals a comment
continue;
if (*cptr=='[') // new section
{
key=cptr;
key.truncate(']'); // remove after & including the ]
key.cat("]"); // put the ] back
strcpy(sectionname,key.get()); // set the current section name
Wstring wssectionname;
if (strlen(sectionname)==2) // clear section with a "[]"
{
sectionname[0]=0;
wssectionname.set("");
}
else
wssectionname.set(sectionname+1);
wssectionname.truncate(']');
sectionList.addTail(wssectionname);
continue;
}
if (strchr(cptr,'=')==NULL) // All config entries must have a '='
continue;
key=cptr;
key.truncate('=');
key.removeSpaces(); // No spaces allowed in the key
key.toUpper(); // make key all caps
// Add the section name to the end of the key
if (strlen(sectionname))
key.cat(sectionname);
cptr=Eat_Spaces(strchr(cptr,'=')+1); // Jump to after the '='
value=cptr;
value.truncate('\r');
value.truncate('\n');
value.truncate('#');
// Remove trailing spaces
while(isgraph(value.get()[strlen(value.get())-1])==0)
value.get()[strlen(value.get())-1]=0;
Critsec_.lock();
Dictionary_.add(key,value);
Critsec_.unlock();
}
return(TRUE);
}
//
// Enum through the config strings. To start, index & offset should be 0
// If retval is false you're done, ignore whatever's in key & value.
//
// Section specifies the configfile section. Set to NULL if you don't care.
//
bit8 ConfigFile::enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section) const
{
int seclen = strlen(section);
while(1)
{
Critsec_.lock();
if (Dictionary_.iterate(index,offset,key,value)==FALSE) // out of keys?
{
Critsec_.unlock();
return(FALSE);
}
Critsec_.unlock();
if (section==NULL) // no specified section, so any will do...
break;
if (strlen(section)+2 >= strlen(key.get())) // key should have form: X[section]
continue;
// Is this key part of our section?
const char *keystr = key.get() + strlen(key.get())-seclen-1;
if (strncmp(keystr,section,strlen(section))==0)
break;
}
key.truncate('['); // remove the section name
return(TRUE);
}
// Get a config entry as a string
bit8 ConfigFile::getString(IN Wstring &_key, Wstring &value, IN char *section) const
{
Wstring key(_key);
key.toUpper();
if (section) // append section name to key
{
key+="[";
key+=section;
key+="]";
}
Critsec_.lock();
bit8 retval=Dictionary_.getValue(key,value);
Critsec_.unlock();
if (retval==FALSE)
{
DBGMSG("Config entry missing: "<<key.get());
}
return(retval);
}
// Get a config entry as a string
bit8 ConfigFile::getString(IN char *key,Wstring &value, IN char *section) const
{
Wstring sKey;
sKey.set(key);
return(getString(sKey,value,section));
}
// Get a config entry as an integer
bit8 ConfigFile::getInt(IN Wstring &_key,sint32 &value, IN char *section) const
{
Wstring key(_key);
key.toUpper();
if (section) // append section name to key
{
key+="[";
key+=section;
key+="]";
}
Wstring svalue;
Critsec_.lock();
bit8 retval=Dictionary_.getValue(key,svalue);
Critsec_.unlock();
if (retval==FALSE)
{ DBGMSG("Config entry missing: "<<key.get()); }
if (retval==FALSE)
return(FALSE);
value=atol(svalue.get());
return(TRUE);
}
// Get a config entry as an integer
bit8 ConfigFile::getInt(IN char *key,sint32 &value, IN char *section) const
{
Wstring sKey;
sKey.set(key);
return(getInt(sKey,value,section));
}
// Get a config entry as an integer
bit8 ConfigFile::getInt(IN Wstring &_key,sint16 &value, IN char *section) const
{
Wstring key(_key);
key.toUpper();
if (section) // append section name to key
{
key+="[";
key+=section;
key+="]";
}
Wstring svalue;
Critsec_.lock();
bit8 retval=Dictionary_.getValue(key,svalue);
Critsec_.unlock();
if (retval==FALSE)
{ DBGMSG("Config entry missing: "<<key.get()); }
if (retval==FALSE)
return(FALSE);
value=atoi(svalue.get());
return(TRUE);
}
// Get a config entry as an integer
bit8 ConfigFile::getInt(IN char *key,sint16 &value, IN char *section) const
{
Wstring sKey;
sKey.set(key);
return(getInt(sKey,value,section));
}
/************* MDC; Added functionality for updating and saving config files ************/
// Remove an entry
bit8 ConfigFile::removeEntry(IN Wstring &_key, IN char *section)
{
Wstring key(_key);
key.toUpper();
if (section) // append section name to key
{
key+="[";
key+=section;
key+="]";
}
Critsec_.lock();
bit8 retval=Dictionary_.remove(key);
Critsec_.unlock();
if (retval==FALSE)
{ DBGMSG("Config entry missing: "<<key.get()); }
if (retval==FALSE)
return(FALSE);
return(TRUE);
}
// Remove an entry
bit8 ConfigFile::removeEntry(IN char *key, IN char *section)
{
Wstring sKey;
sKey.set(key);
return removeEntry(sKey, section);
}
// Set a config entry as a string
bit8 ConfigFile::setString(IN Wstring &_key, IN Wstring &value, IN char *section)
{
Wstring key(_key);
key.toUpper();
if (section) // append section name to key
{
key+="[";
key+=section;
key+="]";
}
else
{
section = ""; // give it a default
}
Critsec_.lock();
Dictionary_.remove(key);
bit8 retval=Dictionary_.add(key,value);
// Test for a new section
Wstring test;
int i;
for (i=0; i<sectionList.length(); i++)
{
sectionList.get(test, i);
if (!strcmp(test.get(), section))
break;
}
if (i == sectionList.length())
{
// New section!
//DBGMSG("New section " << section << ", " << sectionList.length() << " entries");
test.set(section);
sectionList.addTail(test);
}
Critsec_.unlock();
if (retval==FALSE)
{
DBGMSG("Config could not set entry: "<<key.get());
}
return(retval);
}
// Set a config entry as a string
bit8 ConfigFile::setString(IN char *key,IN Wstring &value, IN char *section)
{
Wstring sKey;
sKey.set(key);
return(setString(sKey,value,section));
}
// Set a config entry as an integer
bit8 ConfigFile::setInt(IN Wstring &_key,IN sint32 &value, IN char *section)
{
Wstring key(_key);
key.toUpper();
if (section) // append section name to key
{
key+="[";
key+=section;
key+="]";
}
else
{
section = ""; // give it a default
}
Wstring svalue;
svalue.setFormatted("%d", value);
Critsec_.lock();
Dictionary_.remove(key);
bit8 retval=Dictionary_.add(key,svalue);
// Test for a new section
Wstring test;
//DBGMSG("Testing " << sectionList.length() << " entries for " << section);
int i;
for (i=0; i<sectionList.length(); i++)
{
sectionList.get(test, i);
//DBGMSG("Looking at " << test.get());
if (!strcmp(test.get(), section))
break;
}
if (i == sectionList.length() && section)
{
// New section!
//DBGMSG("New section " << section << ", " << sectionList.length() << " entries");
test.set(section);
sectionList.addTail(test);
}
Critsec_.unlock();
if (retval==FALSE)
{ DBGMSG("Config could not set entry: "<<key.get()); }
if (retval==FALSE)
return(FALSE);
return(TRUE);
}
// Set a config entry as an integer
bit8 ConfigFile::setInt(IN char *key,IN sint32 &value, IN char *section)
{
Wstring sKey;
sKey.set(key);
return(setInt(sKey,value,section));
}
// Write config file to disk. Does not preserve comments, etc.
bit8 ConfigFile::writeFile(FILE *config)
{
if (!config)
{
ERRMSG("No FP on config file write!");
return FALSE;
}
int index = 0;
int offset = 0;
Wstring key;
Wstring value;
Wstring section;
//DBGMSG(sectionList.length() << " entries");
for (int i=0; i<sectionList.length(); i++)
{
sectionList.get(section, i);
//DBGMSG("Writing " << section.get());
fprintf(config, "[%s]\n", section.get());
index = 0;
offset = 0;
while (enumerate(index, offset, key, value, section.get())!=FALSE)
{
fprintf(config, "%s=%s\n", key.get(), value.get());
}
fprintf(config, "\n");
}
return TRUE;
}
/************* Static functions below **************/
// Given a Wstring, return a 32 bit integer that has a good numeric
// distributation for the purposes of indexing into a hash table.
static uint32 Wstring_Hash(const Wstring &string)
{
uint32 retval=0;
retval=string.length();
for (uint32 i=0; i<string.length(); i++)
{
retval+=*(string.get()+i);
retval+=i;
retval=(retval<<8)^(retval>>24); // ROL 8
}
return(retval);
}
static char *Eat_Spaces(char *string)
{
char *retval=string;
while (isspace(*retval))
retval++;
return(retval);
}

View file

@ -0,0 +1,76 @@
/*
** Command & Conquer Generals(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: Carpenter (The RedAlert ladder creator)
File Name : configfile.h
Author : Neal Kettler
Start Date : June 9, 1997
Last Update : May 13, 1999
\***************************************************************************/
#ifndef CONFIGFILE_HEADER
#define CONFIGFILE_HEADER
#include "wstypes.h"
#include "dictionary.h"
#include "wstring.h"
#include "critsec.h"
#include "arraylist.h"
class ConfigFile
{
public:
ConfigFile();
~ConfigFile();
bit8 readFile(FILE *config);
bit8 getString(IN Wstring &key,OUT Wstring &value, IN char *section=NULL) const;
bit8 getString(IN char *key,OUT Wstring &value, IN char *section=NULL) const;
bit8 getInt(IN Wstring &key,OUT sint32 &value, IN char *section=NULL) const;
bit8 getInt(IN char *key,OUT sint32 &value, IN char *section=NULL) const;
bit8 getInt(IN Wstring &key,OUT sint16 &value, IN char *section=NULL) const;
bit8 getInt(IN char *key,OUT sint16 &value, IN char *section=NULL) const;
// Enumerate through the config lines
bit8 enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section=NULL) const;
// Manual update of config file
bit8 setString(IN Wstring &key,IN Wstring &value, IN char *section=NULL);
bit8 setString(IN char *key,IN Wstring &value, IN char *section=NULL);
bit8 setInt(IN Wstring &key,IN sint32 &value, IN char *section=NULL);
bit8 setInt(IN char *key,IN sint32 &value, IN char *section=NULL);
bit8 removeEntry(IN Wstring &key, IN char *section=NULL);
bit8 removeEntry(IN char *key, IN char *section=NULL);
bit8 writeFile(FILE *config); // Does not preserve comments, etc
ArrayList<Wstring> sectionList; // stores the names of all sections
private:
Dictionary<Wstring,Wstring> Dictionary_; // stores the mappings from keys
// to value strings
// The lock is only needed around the immediate access to the dictionary, no writes
// are allowed so you don't need to worry about an outer lock around the enumerate
CritSec Critsec_; // lock around dictionary
};
#endif

View file

@ -0,0 +1,129 @@
/*
** Command & Conquer Generals(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 "critsec.h"
#include <assert.h>
#include "wlib/wdebug.h"
CritSec::CritSec()
{
#ifdef _UNIX
pthread_mutex_init(&Mutex_, NULL);
RefCount_ = 0;
#elif defined(_WIN32)
InitializeCriticalSection(&CritSec_);
#endif
}
CritSec::~CritSec()
{
#ifdef _UNIX
pthread_mutex_destroy(&Mutex_);
#elif defined(_WIN32)
DeleteCriticalSection(&CritSec_);
#endif
}
// The "lock" function blocks until the mutex is available.
// Returns 0 on success, error code on error.
//
// A thread that already has a lock will increment a reference count if it calls
// lock again. It must then call unlock() enough times to get the reference to 0.
//
// If refcount is not null you can get the current ref counter after the lock.
//
sint32 CritSec::lock(int *refcount) RO
{
#ifdef _UNIX
sint32 status;
// I TRY to get the lock. IF I succeed, OR if I fail because
// I already have the lock, I just increment the reference
// count and return.
if (((status = pthread_mutex_trylock(&Mutex_)) == 0) ||
((status == EBUSY) && (ThreadId_ == pthread_self())))
{
ThreadId_ = pthread_self();
RefCount_++;
if (refcount)
*refcount=RefCount_;
return(0);
}
// Otherwise, I wait for the lock.
if ((status = pthread_mutex_lock(&Mutex_)) == 0)
{
assert(RefCount_ == 0);
ThreadId_ = pthread_self();
RefCount_++;
}
else
{
ERRMSG("pthread_mutex_lock: " << strerror(errno));
}
if (refcount)
*refcount=RefCount_;
return(status);
#elif defined(_WIN32)
// TOFIX update the refcount
EnterCriticalSection(&CritSec_);
return(0);
#else
#error Must define either _WIN32 or _UNIX
#endif
}
// The "unlock" function release the critical section.
sint32 CritSec::unlock(void) RO
{
#ifdef _UNIX
sint32 status = 0;
assert(RefCount_ >= 0);
if (RefCount_ <= 0)
{
//ERRMSG("unlocking unlocked mutex!");
return(-1);
}
///assert(ThreadId_ == pthread_self());
if (ThreadId_ != pthread_self())
{
WRNMSG("tried to unlock a mutex not owned by self");
return(-1);
}
if (--RefCount_ == 0)
{
// Set thread id to zero -- we're going to release mutex
ThreadId_ = (pthread_t)0;
// Unlock the mutex.
if ((status = pthread_mutex_unlock(&Mutex_)) != 0)
ERRMSG("pthread_mutex_lock: " << strerror(errno));
}
return status;
#elif defined(_WIN32)
LeaveCriticalSection(&CritSec_);
return(0);
#endif
}

View file

@ -0,0 +1,62 @@
/*
** Command & Conquer Generals(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 CRITSEC_HEADER
#define CRITSEC_HEADER
#include "wstypes.h"
#ifdef _WIN32
#include <windows.h>
#include <winbase.h>
#elif defined(_UNIX)
#include <pthread.h>
#include <errno.h>
#endif
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
//
// Critical Section built either on a POSIX Mutex, or a Win32 Critical Section
//
// POSIX version is done by keeping a thread_id and a reference count. Win32 version
// just calls the built in functions.
//
class CritSec
{
public:
CritSec();
~CritSec();
sint32 lock(int *refcount=NULL) RO;
sint32 unlock(void) RO;
protected:
#ifdef _WIN32
mutable CRITICAL_SECTION CritSec_;
#else
mutable pthread_mutex_t Mutex_; // Mutex lock
mutable pthread_t ThreadId_; // Owner of mutex
mutable int RefCount_; // Reference count
#endif
};
#endif

View file

@ -0,0 +1,680 @@
/*
** Command & Conquer Generals(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: Carpenter (The RedAlert ladder creator)
File Name : dictionary.h
Author : Neal Kettler
Start Date : June 1, 1997
Last Update : June 17, 1997
This template file implements a hash dictionary. A hash dictionary is
used to quickly match a value with a key. This works well for very
large sets of data. A table is constructed that has some power of two
number of pointers in it. Any value to be put in the table has a hashing
function applied to the key. That key/value pair is then put in the
linked list at the slot the hashing function specifies. If everything
is working well, this is much faster than a linked list, but only if
your hashing function is good.
\****************************************************************************/
#ifndef DICTIONARY_HEADER
#define DICTIONARY_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "wstypes.h"
// Every entry in the hash dictionary must be an instance of the DNode
// template. 'K' and 'V' denote Key and Value.
template <class K,class V>
class DNode
{
public:
K key;
V value;
DNode<K,V> *hashNext;
};
template <class K,class V>
class Dictionary
{
public:
////////////////Dictionary(uint32 (* hashFn)(K &key));
// Note: I had to put this inside the class definition because VC5 sucks butt
//Create the empty hash dictionary
Dictionary(uint32 (*hashFn)(const K &key)) :
SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it
EXPAND_THRESHOLD(0.80), // When table is 80% full grow it
MIN_TABLE_SIZE(128) // must be a power of 2
{
log2Size=MIN_TABLE_SIZE;
size=MIN_TABLE_SIZE;
assert(size>=4);
tableBits=0;
while(log2Size) { tableBits++; log2Size>>=1; }
tableBits--;
size=1<<tableBits; //Just in case MIN_TABLE_SIZE wasn't a power of 2
entries=0;
keepSize=FALSE;
//Table is a pointer to a list of pointers (the hash table)
table=(DNode<K,V> **)new DNode<K,V>* [size];
assert(table!=NULL);
memset((void *)table,0,size*sizeof(void *));
hashFunc=hashFn;
}
~Dictionary();
void clear(void);
bit8 add(IN K &key,IN V &value);
bool getValue(IN K &key, OUT V &value) RO;
bool getPointer(IN K &key, OUT V **value) RO; // ptr to internal storage (Careful!)
void print(FILE *out) RO;
uint32 getSize(void) RO;
uint32 getEntries(void) RO;
bit8 contains(IN K &key) RO;
bit8 updateValue(IN K &key,IN V &value);
bit8 remove(IN K &key,OUT V &value);
bit8 remove(IN K &key);
bit8 removeAny(OUT K &key,OUT V &value);
bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) RO;
bit8 iterate(INOUT int &index,INOUT int &offset, OUT K &key, OUT V &value) RO;
Dictionary<K,V> &operator=(Dictionary<K,V> &other);
private:
void shrink(void); // halve the number of slots
void expand(void); // double the number of slots
DNode<K,V> **table; // This stores the lists at each slot
uint32 entries; // number of entries
uint32 size; // size of table
uint32 tableBits; // table is 2^tableBits big
uint32 log2Size; // Junk variable
bit8 keepSize; // If true don't shrink or expand
uint32 (* hashFunc)(IN K &key); // User provided hash function
uint32 keyHash(IN K &key) RO; // This will reduce to correct range
// See initilizer list of constructor for values
const double SHRINK_THRESHOLD; // When table is this % full shrink it
const double EXPAND_THRESHOLD; // When table is this % full grow it
const int MIN_TABLE_SIZE; // must be a power of 2
};
//Free all the memory...
template <class K,class V>
Dictionary<K,V>::~Dictionary()
{
clear(); // Remove the entries
delete[](table); // And the table as well
}
// Remove all the entries and free the memory
template <class K,class V>
void Dictionary<K,V>::clear()
{
DNode<K,V> *temp,*del;
uint32 i;
//free all the data
for (i=0; i<size; i++)
{
temp=table[i];
while(temp!=NULL)
{
del=temp;
temp=temp->hashNext;
delete(del);
}
table[i]=NULL;
}
entries=0;
while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE))
shrink();
}
template <class K,class V>
uint32 Dictionary<K,V>::keyHash(IN K &key) RO
{
uint32 retval=hashFunc(key);
retval &= ((1<<tableBits)-1);
assert(retval<getSize());
return(retval);
}
template <class K,class V>
void Dictionary<K,V>::print(FILE *out) RO
{
DNode<K,V> *temp;
uint32 i;
fprintf(out,"--------------------\n");
for (i=0; i<getSize(); i++)
{
temp=table[i];
fprintf(out," |\n");
fprintf(out,"[ ]");
while (temp!=NULL)
{
fprintf(out,"--[ ]");
temp=temp->hashNext;
}
fprintf(out,"\n");
}
fprintf(out,"--------------------\n");
}
template <class K, class V>
Dictionary<K,V> &Dictionary<K,V>::operator=(Dictionary<K,V> &other)
{
_ASSERTE(0);
}
//
// Iterate through all the records. Index is for the table, offset specifies the
// element in the linked list. Set both to 0 and continue calling till false
// is returned.
template <class K,class V>
bit8 Dictionary<K,V>::iterate(INOUT int &index,INOUT int &offset,
OUT V &value) RO
{
DNode<K,V> *temp;
// index out of range
if ((index<0)||(index >= (int)getSize()))
return(FALSE);
temp=table[index];
while ((temp==NULL)&&((++index) < (int)getSize()))
{
temp=table[index];
offset=0;
}
if (temp==NULL) // no more slots with data
return(FALSE);
uint32 i=0;
while ((temp!=NULL) && ((int)i < offset))
{
temp=temp->hashNext;
i++;
}
if (temp==NULL) // should never happen
return(FALSE);
value=temp->value;
if (temp->hashNext==NULL)
{
index++;
offset=0;
}
else
offset++;
return(TRUE);
}
//
// Iterate through all the records. Index is for the table, offset specifies the
// element in the linked list. Set both to 0 and continue calling till false
// is returned.
template <class K,class V>
bit8 Dictionary<K,V>::iterate(INOUT int &index,INOUT int &offset,
OUT K &key, OUT V &value) RO
{
DNode<K,V> *temp;
// index out of range
if ((index<0)||(index >= (int)getSize()))
return(FALSE);
temp=table[index];
while ((temp==NULL)&&((++index) < (int)getSize()))
{
temp=table[index];
offset=0;
}
if (temp==NULL) // no more slots with data
return(FALSE);
uint32 i=0;
while ((temp!=NULL) && ((int)i < offset))
{
temp=temp->hashNext;
i++;
}
if (temp==NULL) // should never happen
return(FALSE);
value=temp->value;
key=temp->key;
if (temp->hashNext==NULL)
{
index++;
offset=0;
}
else
offset++;
return(TRUE);
}
// Return the current size of the hash table
template <class K,class V>
uint32 Dictionary<K,V>::getSize(void) RO
{ return(size); }
// Return the current number of entries in the table
template <class K,class V>
uint32 Dictionary<K,V>::getEntries(void) RO
{ return(entries); }
// Does the Dictionary contain the key?
template <class K,class V>
bit8 Dictionary<K,V>::contains(IN K &key) RO
{
int offset;
DNode<K,V> *node;
offset=keyHash(key);
node=table[offset];
if (node==NULL)
{ return(FALSE); } // can't find it
while(node!=NULL)
{
if ((node->key)==key)
{ return(TRUE); }
node=node->hashNext;
}
return(FALSE);
}
// Try and update the value of an already existing object
template <class K,class V>
bit8 Dictionary<K,V>::updateValue(IN K &key,IN V &value)
{
sint32 retval;
retval=remove(key);
if (retval==FALSE)
return(FALSE);
add(key,value);
return(TRUE);
}
// Add to the dictionary (if key exists, value is updated with the new V)
template <class K, class V>
bit8 Dictionary<K,V>::add(IN K &key,IN V &value)
{
int offset;
DNode<K,V> *node,*item,*temp;
float percent;
item=(DNode<K,V> *)new DNode<K,V>;
assert(item!=NULL);
#ifdef KEY_MEM_OPS
memcpy(&(item->key),&key,sizeof(K));
#else
item->key=key;
#endif
#ifdef VALUE_MEM_OPS
memcpy(&(item->value),&value,sizeof(V));
#else
item->value=value;
#endif
item->hashNext=NULL;
//If key already exists, it will be overwritten
remove(key); // Hopefully this will be false...
offset=keyHash(key);
node=table[offset];
if (node==NULL)
{ table[offset]=item; }
else
{
temp=table[offset];
table[offset]=item;
item->hashNext=temp;
}
entries++;
percent=(float)entries;
percent/=(float)getSize();
if (percent>= EXPAND_THRESHOLD ) expand();
return(TRUE);
}
// Remove an item from the dictionary
template <class K,class V>
bit8 Dictionary<K,V>::remove(IN K &key,OUT V &value)
{
int offset;
DNode<K,V> *node,*last,*temp;
float percent;
if (entries==0)
return(FALSE);
percent=(float)(entries-1);
percent/=(float)getSize();
offset=keyHash(key);
node=table[offset];
last=node;
if (node==NULL)
return(FALSE);
//special case table points to thing to delete
#ifdef KEY_MEM_OPS
if (0==memcmp(&(node->key),&key,sizeof(K)))
#else
if ((node->key)==key)
#endif
{
#ifdef VALUE_MEM_OPS
memcpy(&value,&(node->value),sizeof(V));
#else
value=node->value;
#endif
temp=table[offset]->hashNext;
delete(table[offset]);
table[offset]=temp;
entries--;
if (percent <= SHRINK_THRESHOLD)
shrink();
return(TRUE);
}
node=node->hashNext;
bit8 retval=FALSE; // wow, didn't add this for years... (DOH!)
//Now the case if the thing to delete is not the first
while (node!=NULL)
{
#ifdef KEY_MEM_OPS
if (0==memcmp(&(node->key),&key,sizeof(K)))
#else
if (node->key==key)
#endif
{
#ifdef VALUE_MEM_OPS
memcpy(&value,&(node->value),sizeof(V));
#else
value=node->value;
#endif
last->hashNext=node->hashNext;
entries--;
delete(node);
retval=TRUE; // yes, we deleted something
break;
}
last=node;
node=node->hashNext;
}
if (percent <= SHRINK_THRESHOLD)
shrink();
return(retval);
}
template <class K,class V>
bit8 Dictionary<K,V>::remove(IN K &key)
{
V temp;
return(remove(key,temp));
}
// Remove some random K/V pair that's in the Dictionary
template <class K,class V>
bit8 Dictionary<K,V>::removeAny(OUT K &key,OUT V &value)
{
int offset;
DNode<K,V> *node,*last,*temp;
float percent;
if (entries==0)
return(FALSE);
percent=(entries-1);
percent/=(float)getSize();
int i;
offset=-1;
for (i=0; i<(int)getSize(); i++)
if (table[i]!=NULL)
{
offset=i;
break;
}
if (offset==-1) // Nothing there
return(FALSE);
node=table[offset];
last=node;
#ifdef KEY_MEM_OPS
memcpy(&key,&(node->key),sizeof(K));
#else
key=node->key;
#endif
#ifdef VALUE_MEM_OPS
memcpy(&value,&(node->value),sizeof(V));
#else
value=node->value;
#endif
temp=table[offset]->hashNext;
delete(table[offset]);
table[offset]=temp;
entries--;
if (percent <= SHRINK_THRESHOLD)
shrink();
return(TRUE);
}
template <class K,class V>
bool Dictionary<K,V>::getValue(IN K &key,OUT V &value) RO
{
V *valptr=NULL;
bool retval=getPointer(key,&valptr);
if (retval && valptr)
{
#ifdef VALUE_MEM_OPS
assert(0);
#else
value=*valptr;
#endif
}
return(retval);
}
// Try and avoid this since you're getting a pointer to the internally
// managed data!
template <class K,class V>
bool Dictionary<K,V>::getPointer(IN K &key,OUT V **valptr) RO
{
int offset;
DNode<K,V> *node;
if (entries==0)
return(FALSE);
offset=keyHash(key);
node=table[offset];
if (node==NULL)
return(FALSE);
#ifdef KEY_MEM_OPS
while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K))))
#else
while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't
#endif // have to do oper !=
{ node=node->hashNext; }
if (node==NULL)
{ return(FALSE); }
*valptr=&(node->value);
return(TRUE);
}
//A note about Shrink and Expand: They are never necessary, they are
//only here to improve performance of the hash table by reducing
//the length of the linked list at each table entry.
// Shrink the hash table by a factor of 2 (and relocate entries)
template <class K,class V>
void Dictionary<K,V>::shrink(void)
{
int i;
int oldsize;
uint32 offset;
DNode<K,V> **oldtable,*temp,*first,*next;
if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE))
return;
//fprintf(stderr,"Shrinking....\n");
oldtable=table;
oldsize=size;
size/=2;
tableBits--;
table=(DNode<K,V> **)new DNode<K,V>*[size];
assert(table!=NULL);
memset((void *)table,0,size*sizeof(void *));
for (i=0; i<oldsize; i++)
{
temp=oldtable[i];
while (temp!=NULL)
{
offset=keyHash(temp->key);
first=table[offset];
table[offset]=temp;
next=temp->hashNext;
temp->hashNext=first;
temp=next;
}
}
delete[](oldtable);
}
template <class K,class V>
void Dictionary<K,V>::expand(void)
{
int i;
int oldsize;
uint32 offset;
DNode<K,V> **oldtable,*temp,*first,*next;
if (keepSize==TRUE)
return;
//fprintf(stderr,"Expanding...\n");
oldtable=table;
oldsize=size;
size*=2;
tableBits++;
table=(DNode<K,V> **)new DNode<K,V>* [size];
assert(table!=NULL);
memset((void *)table,0,size*sizeof(void *));
for (i=0; i<oldsize; i++)
{
temp=oldtable[i];
while (temp!=NULL)
{
offset=keyHash(temp->key);
first=table[offset];
table[offset]=temp;
next=temp->hashNext;
temp->hashNext=first;
temp=next;
}
}
delete[](oldtable);
}
#endif

View file

@ -0,0 +1,51 @@
/*
** Command & Conquer Generals(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 FILED_HEADER
#define FILED_HEADER
#include "odevice.h"
class FileD : public OutputDevice
{
public:
FileD(IN char *filename, IN char *mode = "w")
{
out=fopen(filename,mode);
if (out==NULL)
out=fopen("FileDev.out",mode);
}
virtual ~FileD()
{ fclose(out); }
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memset(string,0,len+1);
memcpy(string,str,len);
fprintf(out,"%s",string);
delete[](string);
fflush(out);
return(len);
}
FILE *out;
};
#endif

View file

@ -0,0 +1,495 @@
/*
** Command & Conquer Generals(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:
File Name : linkedlist.h
Author : Neal Kettler
Start Date : June 19, 1997
Last Update : June 19, 1997
Linked list template. This is a fairly standard doubly linked list that
allows insertion and removal at any point in the list. A current pointer
is used to quickly access items when they are examined sequentially.
Copies of the data are stored instead of a pointer to the original.
If you want to store pointers then the template should be of a pointer type.
\****************************************************************************/
#ifndef LINKEDLIST_HEADER
#define LINKEDLIST_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "wstypes.h"
template <class T>
class LNode
{
public:
T Node;
LNode<T> *Next;
LNode<T> *Prev;
};
template <class T>
class LinkedList
{
public:
LinkedList();
LinkedList(LinkedList<T> &other);
~LinkedList();
// Remove all entries from the lsit
void clear(void);
// Add a node after the zero based 'pos'
bit8 add(IN T &node,sint32 pos, OUT T **newnodeptr=NULL);
bit8 addTail(IN T &node, OUT T **newnodeptr=NULL);
bit8 addHead(IN T &node, OUT T **newnodeptr=NULL);
// Remove a node
bit8 remove(OUT T &node,sint32 pos);
bit8 remove(sint32 pos);
bit8 removeHead(OUT T &node);
bit8 removeTail(OUT T &node);
// Get a node without removing from the list
bit8 get(OUT T &node,sint32 pos);
bit8 getHead(OUT T &node);
bit8 getTail(OUT T &node);
// Get a pointer to the internally managed data (careful!)
bit8 getPointer(OUT T **node, sint32 pos);
// Get the number of entries in the list
sint32 length(void);
// Print information on the list
void print(IN FILE *out);
// assignment operator
LinkedList<T> &operator=(LinkedList<T> &other);
private:
sint32 Entries; // Number of entries
LNode<T> *Head; // Head of the list
LNode<T> *Tail; // Tail of the list
LNode<T> *Current; // Current pointer & index for speed only
sint32 CurIndex;
};
//Create the empty list
template <class T>
LinkedList<T>::LinkedList()
{
Entries=0;
Head=Tail=Current=NULL;
CurIndex=-1; // Not valid when 0 entries
}
// copy constructor
template <class T>
LinkedList<T>::LinkedList(LinkedList<T> &other)
{
Entries=0;
Head=Tail=Current=NULL;
CurIndex=-1; // Not valid when 0 entries
(*this)=other;
}
//Free all the memory...
template <class T>
LinkedList<T>::~LinkedList()
{
clear(); // Remove the entries
}
// assignment operator
template <class T>
LinkedList<T> &LinkedList<T>::operator=(LinkedList<T> &other)
{
T node;
clear();
for (int i=0; i<other.length(); i++)
{
other.get(node,i);
addTail(node);
}
return(*this);
}
// Remove all the entries and free the memory
template <class T>
void LinkedList<T>::clear()
{
LNode<T> *temp,*del;
temp=Head;
while (temp) {
del=temp;
temp=temp->Next;
delete(del);
}
Entries=0;
CurIndex=-1;
Head=Tail=Current=NULL;
}
// When adding into a position, the new node goes at the zero based slot
// specified by pos. All other nodes get moved one slot down.
template <class T>
bit8 LinkedList<T>::add(IN T &node,sint32 pos, OUT T **newnodeptr)
{
LNode<T> *temp;
LNode<T> *item;
if (pos<0)
pos=0;
if (pos>Entries)
pos=Entries;
item=(LNode<T> *)new LNode<T>;
assert(item!=NULL);
item->Node=node; // copy the passed in object
item->Prev=NULL;
item->Next=NULL;
if (newnodeptr)
*newnodeptr=&(item->Node);
if ((pos==0)||(pos==Entries)) { // Both cases can be true for a new list!
if (pos==0) {
item->Next=Head;
if (Head)
Head->Prev=item;
Head=item;
}
if (pos==Entries) {
item->Prev=Tail;
if (Tail)
Tail->Next=item;
Tail=item;
}
Entries++;
Current=item;
CurIndex=pos;
return(TRUE);
}
// If control is here, we know the new node is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
item->Next=Current;
item->Prev=Current->Prev;
Current->Prev=item;
item->Prev->Next=item;
Current=item;
Entries++;
return(TRUE);
}
// Check the other possible speedup (adding after CurIndex)
if (pos==CurIndex+1) {
item->Next=Current->Next;
item->Prev=Current;
Current->Next=item;
item->Next->Prev=item;
Current=item;
CurIndex++;
Entries++;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
temp=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
temp=temp->Next;
assert(temp!=NULL);
}
item->Next=temp;
item->Prev=temp->Prev;
temp->Prev=item;
item->Prev->Next=item;
Current=item;
CurIndex=pos;
Entries++;
return(TRUE);
}
// Add to the first node, all others get shifted down one slot
template <class T>
bit8 LinkedList<T>::addHead(IN T &node, OUT T **newnodeptr)
{
return(add(node,0,newnodeptr));
}
// Append to the end of the list
template <class T>
bit8 LinkedList<T>::addTail(IN T &node, OUT T **newnodeptr)
{
return(add(node,length(),newnodeptr));
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(OUT T &node, sint32 pos)
{
////////LNode<T> *temp;
LNode<T> *item;
if (Entries==0)
return(FALSE);
if (pos<0)
pos=0;
if (pos>=Entries)
pos=Entries-1;
if ((pos==0)||(pos==Entries-1)) { // Both can be true for a 1 item list
if (pos==0) {
item=Head;
if (item->Next)
item->Next->Prev=NULL;
Head=item->Next;
node=item->Node;
Current=Head;
CurIndex=0;
}
if (pos==Entries-1) {
item=Tail;
if (item->Prev)
item->Prev->Next=NULL;
Tail=item->Prev;
node=item->Node;
Current=Tail;
CurIndex=Entries-2;
}
delete(item);
Entries--;
if (Entries==0) { // Super paranoia check
assert(Current==NULL);
assert(CurIndex==-1);
assert(Head==NULL);
assert(Tail==NULL);
}
return(TRUE);
}
// If control is here, we know the target node is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
item=Current;
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
// CurIndex stays the same
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// Check the other possible speedup (removing after CurIndex)
if (pos==CurIndex+1) {
item=Current->Next;
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
CurIndex++;
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
item=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
item=item->Next;
assert(item!=NULL);
}
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
CurIndex=pos;
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(sint32 pos)
{
T temp_node;
return(remove(temp_node,pos));
}
// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::removeHead(OUT T &node)
{
return(remove(node,0));
}
// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::removeTail(OUT T &node)
{
return(remove(node,Entries-1));
}
template <class T>
bit8 LinkedList<T>::get(OUT T &node, sint32 pos)
{
T *objptr;
bool retval=getPointer(&objptr,pos);
if (retval && objptr)
node=*objptr;
return(retval);
}
template <class T>
bit8 LinkedList<T>::getPointer(OUT T **node,sint32 pos)
{
if ((node==0)||(Entries==0))
return(FALSE);
LNode<T> *item;
if (pos<0)
{
//pos=0;
return(FALSE);
}
if (pos>=Entries)
{
//pos=Entries-1;
return(FALSE);
}
if (pos==0) {
*node=&(Head->Node);
return(TRUE);
} else if (pos==Entries-1) {
*node=&(Tail->Node);
return(TRUE);
}
// If control reaches here, we know target is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
*node=&(Current->Node);
return(TRUE);
} else if (pos==CurIndex+1) {
*node=&(Current->Next->Node);
CurIndex++;
Current=Current->Next;
return(TRUE);
} else if (pos==CurIndex-1) {
*node=&(Current->Prev->Node);
CurIndex--;
Current=Current->Prev;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
item=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
item=item->Next;
assert(item!=NULL);
}
*node=&(item->Node);
CurIndex=pos;
Current=item;
return(TRUE);
}
// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::getHead(OUT T &node)
{
return(get(node,0));
}
// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::getTail(OUT T &node)
{
return(get(node,Entries-1));
}
template <class T>
void LinkedList<T>::print(IN FILE *out)
{
LNode<T> *temp;
fprintf(out,"--------------------\n");
fprintf(out,"Entries = %d\n",length());
fprintf(out,"H = %8p C = %8p (%d) T = %8p\n",Head,Current,CurIndex,Tail);
temp=Head;
while (temp) {
fprintf(out," %8p<-((%8p))->%8p \n",temp->Prev,temp,temp->Next);
temp=temp->Next;
}
fprintf(out,"--------------------\n");
}
// Return the current length of the list
template <class T>
sint32 LinkedList<T>::length(void) {
return(Entries);
}
#endif

View file

@ -0,0 +1,40 @@
/*
** Command & Conquer Generals(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 MBOXD_HEADER
#define MBOXD_HEADER
#include "odevice.h"
class MboxD : public OutputDevice
{
public:
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memset(string,0,len+1);
memcpy(string,str,len);
MessageBox(NULL,string,"Debug Message", MB_OK | MB_ICONINFORMATION);
delete[](string);
return(len);
}
};
#endif

View file

@ -0,0 +1,57 @@
/*
** Command & Conquer Generals(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 "monod.h"
MonoD::MonoD(void)
{
#ifdef _WIN32
unsigned long retval;
handle = CreateFile("\\\\.\\MONO", GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE)
{
DeviceIoControl(handle, (DWORD)IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0,
&retval,0);
}
#endif
}
MonoD::~MonoD()
{
#ifdef _WIN32
CloseHandle(handle);
handle=NULL;
#endif
}
int MonoD::print(const char *str, int len)
{
#ifdef _WIN32
unsigned long retval;
WriteFile(handle, str, len, &retval, NULL);
////DeviceIoControl(handle, (DWORD)IOCTL_MONO_PRINT_RAW, (void *)str, len, NULL, 0,
//// &retval,0);
return(len);
#else
for (int i=0; i<len; i++)
fprintf(stderr,"%c",str[i]);
return(len);
#endif
}

View file

@ -0,0 +1,74 @@
/*
** Command & Conquer Generals(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 MONOD_HEADER
#define MONOD_HEADER
#include <stdlib.h>
#include <stdio.h>
#include "odevice.h"
///////////////////////// WIN32 ONLY ///////////////////////////////////
#ifdef _WIN32
#include <windows.h>
#include <winioctl.h>
/*
** This is the identifier for the Monochrome Display Driver
*/
#define FILE_DEVICE_MONO 0x00008000
/*
** These are the IOCTL commands supported by the Monochrome Display Driver.
*/
#define IOCTL_MONO_HELP_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_CLEAR_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_PRINT_RAW CTL_CODE(FILE_DEVICE_MONO, 0x802, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_CURSOR CTL_CODE(FILE_DEVICE_MONO, 0x803, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SCROLL CTL_CODE(FILE_DEVICE_MONO, 0x804, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_BRING_TO_TOP CTL_CODE(FILE_DEVICE_MONO, 0x805, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_ATTRIBUTE CTL_CODE(FILE_DEVICE_MONO, 0x806, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_PAN CTL_CODE(FILE_DEVICE_MONO, 0x807, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_LOCK CTL_CODE(FILE_DEVICE_MONO, 0x808, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_UNLOCK CTL_CODE(FILE_DEVICE_MONO, 0x809, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80A, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_RESET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80B, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80C, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_CLEAR_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80D, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_FILL_ATTRIB CTL_CODE(FILE_DEVICE_MONO, 0x80E, METHOD_BUFFERED, FILE_WRITE_DATA)
#endif // ifdef _WIN32
class MonoD : public OutputDevice
{
public:
MonoD();
~MonoD();
virtual int print(const char *str,int len);
private:
#ifdef _WIN32
HANDLE handle;
#endif
};
#endif

View file

@ -0,0 +1,32 @@
/*
** Command & Conquer Generals(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 ODEVICE_HEADER
#define ODEVICE_HEADER
// This virtual base class provides an interface for output devices
// that can be used for the debugging package.
class OutputDevice
{
public:
OutputDevice() {}
virtual ~OutputDevice() {};
virtual int print(const char *s,int len)=0;
};
#endif

View file

@ -0,0 +1,182 @@
/*
** Command & Conquer Generals(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/>.
*/
/******************************************************************************\
sem4.cpp Neal Kettler
Simple Posix semaphore class
This is useful because the constructor will automatically call sem_init
and you don't have to worry about it. It also allows for other semaphore
libraries if you don't have posix.
\******************************************************************************/
#include "sem4.h"
#ifdef _REENTRANT
Sem4::Sem4()
{
#ifndef _WINDOWS
sem_init(&sem,1,1);
#else
sem = CreateSemaphore(NULL, 1, 1, NULL);
#endif
}
Sem4::Sem4(uint32 value)
{
#ifndef _WINDOWS
sem_init(&sem,1,value);
#else
sem = CreateSemaphore(NULL, value, value, NULL);
#endif
}
Sem4::~Sem4()
{
#ifndef _WINDOWS
sem_destroy(&sem);
#else
if (sem) CloseHandle(sem);
#endif
}
sint32 Sem4::Wait(void) const
{
#ifndef _WINDOWS
return(sem_wait((sem_t *)&sem));
#else
if (!sem)
return -1; // no semaphore!
DWORD dwWaitResult = WaitForSingleObject(sem, INFINITE);
switch (dwWaitResult) {
case WAIT_OBJECT_0: // The semaphore object was signaled.
return 0;
break;
case WAIT_TIMEOUT: // Should not happen ;)
return -1;
break;
}
return -1;
#endif
}
sint32 Sem4::Post(void) const
{
#ifndef _WINDOWS
return(sem_post((sem_t *)&sem));
#else
if (!sem)
return -1;
if (!ReleaseSemaphore(sem, 1 ,NULL))
return -1;
return 0;
#endif
}
sint32 Sem4::TryWait(void) const
{
#ifndef _WINDOWS
return(sem_trywait((sem_t *)&sem));
#else
if (!sem)
return -1;
DWORD dwWaitResult = WaitForSingleObject(sem, 0L);
switch (dwWaitResult) {
case WAIT_OBJECT_0: // The semaphore object was signaled.
return 0;
break;
case WAIT_TIMEOUT:
return -1;
break;
}
return -1;
#endif
}
sint32 Sem4::GetValue(int *sval) const
{
#ifndef _WINDOWS
return(sem_getvalue((sem_t *)&sem,sval));
#else
if (!sem)
return -1;
long prev;
if (!ReleaseSemaphore(sem, 0, &prev))
return -1;
if (sval)
*sval = prev;
return 0;
#endif
}
sint32 Sem4::Destroy(void)
{
#ifndef _WINDOWS
return(sem_destroy(&sem));
#else
return CloseHandle(sem);
#endif
}
#else
/****************************************************************************
non threaded versions that do nothing
*****************************************************************************/
Sem4::Sem4()
{
}
Sem4::Sem4(uint32)
{
}
Sem4::~Sem4()
{
}
sint32 Sem4::Wait(void) const
{
return(0);
}
sint32 Sem4::Post(void) const
{
return(0);
}
sint32 Sem4::TryWait(void) const
{
return(0);
}
sint32 Sem4::GetValue(int *) const
{
return(0);
}
sint32 Sem4::Destroy(void)
{
return(0);
}
#endif

View file

@ -0,0 +1,64 @@
/*
** Command & Conquer Generals(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 SEM4_HEADER
#define SEM4_HEADER
#include <limits.h>
#ifndef _WINDOWS
#include <unistd.h>
#endif
#include "wstypes.h"
#ifdef _REENTRANT
#ifndef _WINDOWS
#include <semaphore.h>
#else
#include <windows.h>
#endif // _WINDOWS
#endif // _REENTRANT
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
class Sem4
{
private:
#ifdef _REENTRANT
#ifndef _WINDOWS
sem_t sem;
#else
HANDLE sem;
#endif
#endif
public:
Sem4();
Sem4(uint32 value);
~Sem4();
sint32 Wait(void) const;
sint32 TryWait(void) const;
sint32 Post(void) const;
sint32 GetValue(int *sval) const;
sint32 Destroy(void);
};
#endif

View file

@ -0,0 +1,39 @@
/*
** Command & Conquer Generals(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 STDERRD_HEADER
#define STDERRD_HEADER
#include "odevice.h"
class StderrD : public OutputDevice
{
public:
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memset(string,0,len+1);
memcpy(string,str,len);
fprintf(stderr,"%s",string);
delete[](string);
return(len);
}
};
#endif

View file

@ -0,0 +1,40 @@
/*
** Command & Conquer Generals(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 STDOUTD_HEADER
#define STDOUTD_HEADER
#include "odevice.h"
class StdoutD : public OutputDevice
{
public:
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memcpy(string,str,len);
string[len]=0;
fprintf(stdout,"%s",string);
fflush(stdout);
delete[](string);
return(len);
}
};
#endif

View file

@ -0,0 +1,140 @@
/*
** Command & Conquer Generals(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 "streamer.h"
#ifdef _WIN32
#include <windows.h>
#endif
Streamer::Streamer() : streambuf()
{
int state=unbuffered();
unbuffered(0); // 0 = buffered, 1 = unbuffered
}
Streamer::~Streamer()
{
///////// calling sync seems to cause crashes here on Win32
//sync();
/////////
delete[](base());
}
int Streamer::setOutputDevice(OutputDevice *device)
{
Output_Device=device;
return(0);
}
// put n chars from string into buffer
int Streamer::xsputn(const char* buf, int size) //implementation of sputn
{
if (size<=0) // Nothing to do
return(0);
const unsigned char *ptr=(const unsigned char *)buf;
for (int i=0; i<size; i++, ptr++)
{
if(*ptr=='\n')
{
if (overflow(*ptr)==EOF)
return(i);
}
else if (sputc(*ptr)==EOF)
return(i);
}
return(size);
}
// Flush the buffer and make more room if needed
int Streamer::overflow(int c)
{
if (c==EOF)
return(sync());
if ((pbase()==0) && (doallocate()==0))
return(EOF);
if((pptr() >= epptr()) && (sync()==EOF))
return(EOF);
else {
sputc(c);
if ((unbuffered() && c=='\n' || pptr() >= epptr())
&& sync()==EOF) {
return(EOF);
}
return(c);
}
}
// This is a write only stream, this should never happen
int Streamer::underflow(void)
{
return(EOF);
}
int Streamer::doallocate()
{
if (base()==NULL)
{
char *buf=new char[(2*STREAMER_BUFSIZ)]; // deleted by destructor
memset(buf,0,2*STREAMER_BUFSIZ);
// Buffer
setb(
buf, // base pointer
buf+STREAMER_BUFSIZ, // ebuf pointer (end of buffer);
0); // 0 = manual deletion of buff
// Get area
setg(
buf, // eback
buf, // gptr
buf); // egptr
buf+=STREAMER_BUFSIZ;
// Put area
setp(buf,buf+STREAMER_BUFSIZ);
return(1);
}
else
return(0);
}
int Streamer::sync()
{
if (pptr()<=pbase()) {
return(0);
}
int wlen=pptr()-pbase();
if (Output_Device)
{
Output_Device->print(pbase(),wlen);
}
if (unbuffered()) {
setp(pbase(),pbase());
}
else {
setp(pbase(),pbase()+STREAMER_BUFSIZ);
}
return(0);
}

View file

@ -0,0 +1,66 @@
/*
** Command & Conquer Generals(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 STREAMER_HEADER
#define STREAMER_HEADER
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <iostream.h>
#include <string.h>
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#include "odevice.h"
#ifndef STREAMER_BUFSIZ
// This limits the number of characters that can be sent to a single 'print'
// call. If your debug message is bigger than this, it will get split over
// multiple 'print' calls. That's usually not a problem.
#define STREAMER_BUFSIZ 2048
#endif
// Provide a streambuf interface for a class that can 'print'
class Streamer : public streambuf
{
public:
Streamer();
virtual ~Streamer();
int setOutputDevice(OutputDevice *output_device);
protected:
// Virtual methods from streambuf
int xsputn(const char* s, int n); // buffer some characters
int overflow(int = EOF); // flush buffer and make more room
int underflow(void); // Does nothing
int sync();
int doallocate(); // allocate a buffer
OutputDevice *Output_Device;
};
#endif

View file

@ -0,0 +1,39 @@
/*
** Command & Conquer Generals(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 "syslogd.h"
SyslogD::SyslogD(char *ident,int logopt,int facility,int _priority)
{
#ifndef _WINDOWS
openlog(ident,logopt,facility);
priority=_priority;
#endif
}
int SyslogD::print(const char *str, int len)
{
#ifndef _WINDOWS
char *temp_str=new char[len+1];
memset(temp_str,0,len+1);
strncpy(temp_str,str,len);
syslog(priority,temp_str);
delete[](temp_str);
#endif
return(len);
}

View file

@ -0,0 +1,48 @@
/*
** Command & Conquer Generals(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 SYSLOGD_HEADER
#define SYSLOGD_HEADER
#include <stdlib.h>
#include <stdio.h>
#ifndef _WINDOWS
#include <syslog.h>
#endif
#include <string.h>
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#include "odevice.h"
// Windows doesn't have a syslog equivalent (does it?), so this class does little there
class SyslogD : public OutputDevice
{
public:
SyslogD(char *ident,int logopt,int facility,int priority);
virtual int print(const char *str,int len);
private:
int priority;
};
#endif

View file

@ -0,0 +1,221 @@
/*
** Command & Conquer Generals(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/>.
*/
#define THREADFAC_CODE
#include "threadfac.h"
int Runnable::ThreadCount_ = 0;
CritSec Runnable::CritSec_; // to protect ThreadCount_
// MDC: Made all this dependent on _REENTRANT being defined so VC++ doesn't complain on
// single-threaded programs...
//
// Note: I chose the following type signature for thread functions
// void function(void *data);
//
//
// Since Win32 & POSIX have different type signatures for the thread entry points
// an intermediate system-dependent function in this file gets called first.
// That function then calls the system independent version. So the system dependent
// version needs 2 Items 1) The address of the _real_ thread func 2) the data
// to pass. We only have 1 argument available, so you figure out the rest...
//
// This is for internal use only
//
struct ThreadInformation
{
void *startPoint; // The address of the _real_ thread function, or class
void *data; // data to pass to real thread function or class
bit8 destroy; // only applies to classes, should delete after execution?
};
//
// Start a thread inside a class
//
bit8 ThreadFactory::startThread(Runnable &runable, void *data, bit8 destroy)
{
#ifdef _REENTRANT
{
Runnable::CritSec_.lock();
Runnable::ThreadCount_++;
Runnable::CritSec_.unlock();
}
ThreadInformation *tInfo=new ThreadInformation;
tInfo->startPoint=(void *)&runable;
tInfo->data=data;
tInfo->destroy=destroy;
#ifdef _WIN32
// Under windows call _beginthreadex instead of CreateThread so you can
// use all the normal C library stuff. (IMPORTANT!!!)
uint32 handle;
uint32 stup1d;
handle=_beginthreadex(NULL,0, threadClassLauncher, tInfo, 0, &stup1d);
if (handle!=NULL)
return(TRUE);
else
{
{
runable.CritSec_.lock();
runable.ThreadCount_--; // Ok, so it didn't really start
runable.CritSec_.unlock();
}
return(FALSE);
}
#else // UNIX
// Setup thread attributes for client threads
int retval;
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM);
retval=pthread_create(NULL,&threadAttr, threadClassLauncher, tInfo);
if (retval==0)
return(TRUE);
else
{
{
runable.CritSec_.lock();
runable.ThreadCount_--; // Ok, so it didn't really start
runable.CritSec_.unlock();
}
return(FALSE);
}
#endif
#else
return (FALSE);
#endif /* _REENTRANT */
}
//
// Start a thread inside a function
//
bit8 ThreadFactory::startThread(void (*start_func)(void *), void *data)
{
#ifdef _REENTRANT
ThreadInformation *tInfo=new ThreadInformation;
tInfo->startPoint=start_func;
tInfo->data=data;
#ifdef _WIN32
// Under windows call _beginthreadex instead of CreateThread so you can
// use all the normal C library stuff. (IMPORTANT!!!)
uint32 handle;
unsigned temp;
handle=_beginthreadex(NULL,0, threadFuncLauncher, tInfo, 0, &temp);
if (handle!=NULL)
return(TRUE);
return(FALSE);
#else // UNIX
// Setup thread attributes for client threads
int retval;
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM);
retval=pthread_create(NULL,&threadAttr, threadFuncLauncher, tInfo);
if (retval==0)
return(TRUE);
else
return(FALSE);
#endif
#else
return(FALSE);
#endif /* REENTRANT */
}
#ifdef _WIN32
unsigned __stdcall threadFuncLauncher(void *temp)
#else // UNIX
void *threadFuncLauncher(void *temp)
#endif
{
ThreadInformation *tInfo=(ThreadInformation *)temp;
void (*start_func)(void *);
start_func=(void (*)(void *))tInfo->startPoint;
void *data=tInfo->data;
delete(tInfo);
start_func(data);
return(0);
}
#ifdef _WIN32
unsigned __stdcall threadClassLauncher(void *temp)
#else // UNIX
void *threadClassLauncher(void *temp)
#endif
{
ThreadInformation *tInfo=(ThreadInformation *)temp;
Runnable *thrClass=(Runnable *)tInfo->startPoint;
void *data=tInfo->data;
bit8 destroy=tInfo->destroy;
delete(tInfo);
thrClass->run(data);
if (destroy) // May want to free memory after thread finishes
delete(thrClass);
{
Runnable::CritSec_.lock();
Runnable::ThreadCount_--;
Runnable::CritSec_.unlock();
}
#ifdef _WIN32
ExitThread(0); // is this really needed?
#endif
return(0);
}
Runnable::Runnable()
{ }
Runnable::~Runnable()
{ }
// Is there a thread running in this class
bit8 Runnable::isRunning(void)
{
// Don't need to lock a simple assignment
int temp=ThreadCount_;
return((temp>0)?TRUE:FALSE);
}
// How many threads are running in this class
int Runnable::getThreadCount(void)
{
// Don't need to lock a simple assignment
int temp=ThreadCount_;
return(temp);
}
#undef THREADFAC_CODE

View file

@ -0,0 +1,124 @@
/*
** Command & Conquer Generals(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/>.
*/
//
// Platform independent thread creation (Win32 & POSIX)
//
#ifndef THREADFAC_HEADER
#define THREADFAC_HEADER
#ifdef _WIN32
#include <process.h>
#endif
#include "wstypes.h"
#include <stdlib.h>
#ifdef _WIN32
#include <wtypes.h>
#else // UNIX
#include <pthread.h>
#endif
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#include "critsec.h"
#ifdef THREADFAC_CODE
// This is the fake thread entry point for functions
#ifdef _WIN32
static unsigned __stdcall threadFuncLauncher(void *temp);
#else // UNIX
static void *threadFuncLauncher(void *temp);
#endif
// Fake entry point for classes
#ifdef _WIN32
static unsigned __stdcall threadClassLauncher(void *temp);
#else // UNIX
static void *threadClassLauncher(void *temp);
#endif
#endif
// Forward definition of base class for threaded classes
class Runnable;
//
// Call the static method startThread to begin a new thread.
//
class ThreadFactory
{
public:
static bit8 startThread(void (*start_func)(void *), void *data);
static bit8 startThread(Runnable &runable, void *data, bit8 destroy=FALSE);
};
//
// Base class for when you want a thread to execute inside a class
// instead of a function.
//
class Runnable
{
public:
Runnable();
virtual ~Runnable();
// ThreadFactory needs to be able to access the private
// IsRunning_ field.
friend class ThreadFactory;
// So do the threadClassLaunchers
#ifdef _WIN32
friend static unsigned __stdcall threadClassLauncher(void *temp);
#else // UNIX
friend void *threadClassLauncher(void *temp);
#endif
virtual void run(void *data)=0; // Thread entry point
void startThread(void *data,bit8 destroy=FALSE) // nice way to start a thread
{
ThreadFactory::startThread(*this,data,destroy);
};
// Is there a thread running in this class?
static bit8 isRunning(void);
// Get the count of threads running inside this class
static int getThreadCount();
private:
static int ThreadCount_;
static CritSec CritSec_; // to protect ThreadCount_
};
#endif

View file

@ -0,0 +1,62 @@
/*
** Command & Conquer Generals(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/>.
*/
//
// threadsafe.h
//
// If you include this file and call non-threadsafe functions, it'll
// prevent your program from compiling. It's meant to be a slap
// on the wrist in case you forget and call an unsafe function
// from a threadsafe program.
//
//
// Reminder to self - use sigwait, not signal handlers!
//
#ifdef _REENTRANT
#ifndef THREADSAFE_HEADER
#define THREADSAFE_HEADER
#define strtok ("strtok() is not MT-SAFE!")
#define ascctime ("asctime() is not MT-SAFE!")
// Can't just do ctime, as Windows' objidl.h uses it as a FILETIME thingie
#define ctime(x) ("ctime() is not MT-SAFE!")
#define gmtime ("gmtime() is not MT-SAFE!")
#define localtime ("localtime() is not MT-SAFE!")
#define tzset ("tzset() is not MT-SAFE!")
#define tzsetwall ("tzsetwall() is not MT-SAFE!")
#define readdir ("readdir() is not MT-SAFE!")
#define rand ("rand() is not MT-SAFE!")
#define srand ("srand() is not MT-SAFE!")
#define random ("random() is not MT-SAFE!")
#define srandom ("srandom() is not MT-SAFE!")
#define tmpnam ("tmpnam() is not MT-SAFE!")
#define vfork ("vfork() is not MT-SAFE!")
#define system ("system() is not MT-SAFE!")
#define popen ("popen() is not MT-SAFE!")
#define pclose ("pclose() is not MT-SAFE!")
#define ctermid ("ctermid() is not MT-SAFE!")
#define getlogin ("getlogin() is not MT-SAFE!");
#endif // THREADSAFE_HEADER
#endif // _REENTRANT

View file

@ -0,0 +1,71 @@
/*
** Command & Conquer Generals(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 "wlib/xtime.h"
#include "timezone.h"
void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset) {
timezone_str = "Unknown Timezone";
timezone_offset = 0;
#ifdef _WINDOWS
struct _timeb wintime;
_ftime(&wintime);
if (wintime.dstflag) {
// Daylight savings time
if (_daylight) {
timezone_str = _tzname[1];
}
} else {
timezone_str = _tzname[0];
}
timezone_offset = wintime.timezone * 60; // its in minutes...
#endif
#ifndef _WINDOWS
struct timeval unixtime;
struct timezone unixtzone;
gettimeofday(&unixtime,&unixtzone);
struct tm unixtm;
localtime_r(&unixtime.tv_sec, &unixtm);
if (unixtm.tm_isdst) {
// Daylight savings time
if (daylight) timezone_str = tzname[1];
timezone_offset = altzone;
} else {
timezone_str = tzname[0];
timezone_offset = timezone;
}
#endif
}
const char * TimezoneString(void) {
const char *timezone_str;
int timezone_offset;
GetTimezoneInfo(timezone_str, timezone_offset);
return timezone_str;
}
int TimezoneOffset(void) {
const char *timezone_str;
int timezone_offset;
GetTimezoneInfo(timezone_str, timezone_offset);
return timezone_offset;
}

View file

@ -0,0 +1,41 @@
/*
** Command & Conquer Generals(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/>.
*/
/****************************************************************************\
timezone Matthew D. Campbell
This is just a couple of convenience functions for determining what timezone
we are now in. It even accounts for daylight savings! One caveat is that it
only tells you info about what the daylight savings info is now, not 5 minutes
from now, not 2 hours ago. Oh well.
\****************************************************************************/
#ifndef _TIMEZONE_H_
#define _TIMEZONE_H_
// Just fill in both the timezone description and its offset from GMT
void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset);
// Returns the description of the current timezone (daylight savings included)
const char * TimezoneString(void);
// Returns the offset from GMT of the current timezone
int TimezoneOffset(void);
#endif // _TIMEZONE_H_

View file

@ -0,0 +1,61 @@
/*
** Command & Conquer Generals(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 USTRING_HEADER
#define USTRING_HEADER
#include <stdlib.h>
#include <stdio.h>
#include <iostream.h>
#include <string>
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#define MAX_BYTES_PER_CHAR 1
template <class charT>
class UstringT : public basic_string<charT, string_char_traits<charT> >
{
public:
explicit UstringT(int max_charlength) {
set_max_bytelength(max_charlength*MAX_BYTES_PER_CHAR);
}
UstringT() { max_bytelength=4000; }
size_t get_max_bytelength(void) { return(max_bytelength); }
void set_max_bytelength(size_t max) { max_bytelength=max; }
bool operator==(const UstringT<charT> &other)
{
const basic_string<charT, string_char_traits<charT> > *other_basic=&other;
const basic_string<charT, string_char_traits<charT> > *this_basic=this;
return((*other_basic)==(*this_basic));
}
private:
size_t max_bytelength;
};
typedef UstringT<char> Ustring;
#endif

View file

@ -0,0 +1,190 @@
/*
** Command & Conquer Generals(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 <stdlib.h>
#include "wdebug.h"
#include "streamer.h"
#include "odevice.h"
static MsgManager *msg_manager=NULL;
static int debug_enabled=0;
static ostream *debug_ostream=NULL;
static Streamer debug_streamer;
static int info_enabled=0;
static ostream *info_ostream=NULL;
static Streamer info_streamer;
static int warn_enabled=0;
static ostream *warn_ostream=NULL;
static Streamer warn_streamer;
static int error_enabled=0;
static ostream *error_ostream=NULL;
static Streamer error_streamer;
// Don't dare touch this semaphore in application code!
#ifdef USE_DEBUG_SEM
Sem4 DebugLibSemaphore;
#else
CritSec DebugLibSemaphore;
#endif
int MsgManager::setAllStreams(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
debug_streamer.setOutputDevice(device);
delete(debug_ostream);
debug_ostream=new ostream(&debug_streamer);
info_streamer.setOutputDevice(device);
delete(info_ostream);
info_ostream=new ostream(&info_streamer);
warn_streamer.setOutputDevice(device);
delete(warn_ostream);
warn_ostream=new ostream(&warn_streamer);
error_streamer.setOutputDevice(device);
delete(error_ostream);
error_ostream=new ostream(&error_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::ReplaceAllStreams(FileD * output_device, IN char *device_filename, IN char *copy_filename)
{
DebugLibSemaphore.Wait();
delete(debug_ostream);
delete(info_ostream);
delete(warn_ostream);
delete(error_ostream);
if (output_device != NULL)
{
delete(output_device);
output_device = NULL;
}
rename(device_filename, copy_filename);
// FileD new_device(device_filename);
output_device = new FileD(device_filename);
debug_streamer.setOutputDevice(output_device);
debug_ostream = new ostream(&debug_streamer);
info_streamer.setOutputDevice(output_device);
info_ostream=new ostream(&info_streamer);
warn_streamer.setOutputDevice(output_device);
warn_ostream = new ostream(&warn_streamer);
error_streamer.setOutputDevice(output_device);
error_ostream = new ostream(&error_streamer);
DebugLibSemaphore.Post();
return(0);
}
int MsgManager::setDebugStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
debug_streamer.setOutputDevice(device);
delete(debug_ostream);
debug_ostream=new ostream(&debug_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::setInfoStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
info_streamer.setOutputDevice(device);
delete(info_ostream);
info_ostream=new ostream(&info_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::setWarnStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
warn_streamer.setOutputDevice(device);
delete(warn_ostream);
warn_ostream=new ostream(&warn_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::setErrorStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
error_streamer.setOutputDevice(device);
delete(error_ostream);
error_ostream=new ostream(&error_streamer);
DEBUGUNLOCK;
return(0);
}
ostream *MsgManager::debugStream(void)
{
return(debug_ostream);
}
ostream *MsgManager::infoStream(void)
{
return(info_ostream);
}
ostream *MsgManager::warnStream(void)
{
return(warn_ostream);
}
ostream *MsgManager::errorStream(void)
{
return(error_ostream);
}

View file

@ -0,0 +1,317 @@
/*
** Command & Conquer Generals(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/>.
*/
/*****************************************************************************\
wdebug Neal Kettler
MT-LEVEL
MT-Safe
The debugging module is pretty good for debugging and it has some message
printing stuff as well. The basic idea is that you write a class that
inherits from OutputDevice (several are provided) and assign that output
device to a stream. There are seperate streams for debugging, information,
warning, and error messages. Each one can have a seperate output device,
or they can all have the same one. Debugging messages only get compiled
in if your module defines 'DEBUG'. If you don't define debug, then not even
the text of the debugging message gets into the binary. All the other
output streams get printed regardless of whether DEBUG is defined.
Sample usage:
FileD debug_device("gameres.debug"); // create a file device
MsgManager::setDebugStream(&debug_device);
DBGMSG("This debug message #" << 1 << " you use C++ streams");
Note that since these are defines you really don't need to put a semicolon
at the end, and it can be bad in situations like this:
if (x)
DBGMSG("Stuff is broken");
else
DBGMSG("Stuff is NOT broken");
This won't compile, read the code until you figure it out. Only then
will you be ready to leave grasshopper.
\*****************************************************************************/
#ifndef WDEBUG_HEADER
#define WDEBUG_HEADER
#define USE_DEBUG_SEM
#include "wstypes.h"
#ifdef _WINDOWS
#include <iostream.h>
#include <strstrea.h>
#else
#include <iostream>
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#endif
#ifdef USE_DEBUG_SEM
#include "sem4.h"
#else
#include "critsec.h"
#endif
#include "odevice.h"
#include "streamer.h"
#include "xtime.h"
#include "timezone.h" // MDC
#include "filed.h"
// This is needed because the streams return a pointer. Every time you
// change the output device the old stream is deleted, and a new one
// is created.
// MDC: Added a macro to switch between semaphores & critsecs to debug a
// problem in Win32.
#ifdef USE_DEBUG_SEM
extern Sem4 DebugLibSemaphore;
#define DEBUGLOCK DebugLibSemaphore.Wait()
#define DEBUGUNLOCK DebugLibSemaphore.Post()
#else
extern CritSec DebugLibSemaphore;
#define DEBUGLOCK DebugLibSemaphore.lock()
#define DEBUGUNLOCK DebugLibSemaphore.unlock()
#endif
// Print an information message
#define INFMSG(X)\
{\
char timebuf[40]; \
Xtime now; \
now -= TimezoneOffset(); \
now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \
DEBUGLOCK; \
if (MsgManager::infoStream()) \
(*(MsgManager::infoStream())) << "INF " << timebuf << " [" << \
__FILE__ << " " << __LINE__ << "] " << X << endl; \
DEBUGUNLOCK; \
}
// Print a warning message
#define WRNMSG(X)\
{\
char timebuf[40]; \
Xtime now; \
now -= TimezoneOffset(); \
now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \
DEBUGLOCK; \
if (MsgManager::warnStream()) \
(*(MsgManager::warnStream())) << "WRN " << timebuf << " [" << \
__FILE__ << " " << __LINE__ << "] " << X << endl; \
DEBUGUNLOCK; \
}
// Print an error message
#define ERRMSG(X)\
{\
char timebuf[40]; \
Xtime now; \
now -= TimezoneOffset(); \
now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \
DEBUGLOCK; \
if (MsgManager::errorStream()) \
(*(MsgManager::errorStream())) << "ERR " << timebuf << " [" << \
__FILE__ << " " << __LINE__ << "] " << X << endl; \
DEBUGUNLOCK; \
}
// Just get a stream to the information device, no extra junk
#define INFSTREAM(X)\
{\
DEBUGLOCK; \
if (MsgManager::infoStream()) \
(*(MsgManager::infoStream())) << X;\
DEBUGUNLOCK; \
}
// Just get a stream to the warning device, no extra junk
#define WRNSTREAM(X)\
{\
DEBUGLOCK; \
if (MsgManager::warnStream()) \
(*(MsgManager::warnStream())) << X;\
DEBUGUNLOCK; \
}
// Just get a stream to the error device, no extra junk
#define ERRSTREAM(X)\
{\
DEBUGLOCK; \
if (MsgManager::errorStream()) \
(*(MsgManager::errorStream())) << X;\
DEBUGUNLOCK; \
}
#ifndef DEBUG
// No debugging, no debug messages.
// Note that anything enclosed in "DBG()" will NOT get executed
// unless DEBUG is defined.
// They are defined to {} for consistency when DEBUG is defined
#define DBG(X)
#define DBGSTREAM(X) {}
#define PVAR(v) {}
#define DBGMSG(X) {}
#define VERBOSE(X) {}
#else // DEBUG _is_ defined
// Execute only if in debugging mode
#define DBG(X) X
// In Windows, send a copy to the debugger window
#ifdef _WINDOWS
// Print a variable
#define PVAR(v) \
{ \
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \
"]: " << ##V << " = " << V << endl; \
strstream __s;\
__s << __FILE__ << "[" << __LINE__ << \
"]: " << ##V << " = " << V << '\n' << '\0';\
OutputDebugString(__s.str());\
DEBUGUNLOCK; \
}
#define DBGMSG(X)\
{\
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \
" " << __LINE__ << "] " << X << endl;\
strstream __s;\
__s << "DBG [" << __FILE__ << \
" " << __LINE__ << "] " << X << '\n' << '\0';\
OutputDebugString(__s.str());\
DEBUGUNLOCK; \
}
// Just get a stream to the debugging device, no extra junk
#define DBGSTREAM(X)\
{\
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(MsgManager::debugStream())) << X;\
strstream __s;\
__s << X << '\0';\
OutputDebugString(__s.str());\
DEBUGUNLOCK; \
}
// Verbosely execute a statement
#define VERBOSE(X)\
{ \
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \
"]: " << ##X << endl; X \
strstream __s;\
__s << __FILE__ << "[" << __LINE__ << \
"]: " << ##X << '\n' << '\0';\
OutputDebugString(__s.str());\
DEBUGUNLOCK; \
}
#else // _WINDOWS
// Print a variable
#define PVAR(v) \
{ \
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \
"]: " << ##V << " = " << V << endl; \
DEBUGUNLOCK; \
}
#define DBGMSG(X)\
{\
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \
" " << __LINE__ << "] " << X << endl;\
DEBUGUNLOCK; \
}
// Just get a stream to the debugging device, no extra junk
#define DBGSTREAM(X)\
{\
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(MsgManager::debugStream())) << X;\
DEBUGUNLOCK; \
}
// Verbosely execute a statement
#define VERBOSE(X)\
{ \
DEBUGLOCK; \
if (MsgManager::debugStream()) \
(*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \
"]: " << ##X << endl; X \
DEBUGUNLOCK; \
}
#endif // _WINDOWS
#endif // DEBUG
//#undef DEBUGLOCK
//#undef DEBUGUNLOCK
class MsgManager
{
protected:
MsgManager();
public:
static int setAllStreams(OutputDevice *device);
static int ReplaceAllStreams(FileD *output_device, IN char *device_filename, IN char *copy_filename);
static int setDebugStream(OutputDevice *device);
static int setInfoStream(OutputDevice *device);
static int setWarnStream(OutputDevice *device);
static int setErrorStream(OutputDevice *device);
static void enableDebug(int flag);
static void enableInfo(int flag);
static void enableWarn(int flag);
static void enableError(int flag);
static ostream *debugStream(void);
static ostream *infoStream(void);
static ostream *warnStream(void);
static ostream *errorStream(void);
};
#endif

View file

@ -0,0 +1,597 @@
/*
** Command & Conquer Generals(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: Carpenter (The RedAlert ladder creator)
File Name : string.cpp
Author : Neal Kettler
Start Date : June 1, 1997
Last Update : June 17, 1997
A fairly typical string class. This string class always copies any input
string to it's own memory (for assignment or construction).
\***************************************************************************/
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "wstring.h"
#define PADSIZE 32 // include a little padding on alloc for future growth
Wstring::Wstring() : str(NULL), strsize(0)
{ }
Wstring::Wstring(IN char *string):str(NULL), strsize(0)
{ set(string); }
Wstring::Wstring(IN Wstring &other):str(NULL), strsize(0)
{
if (other.str!=NULL)
{
str=new char[strlen(other.str)+PADSIZE+1];
strsize=strlen(other.str)+PADSIZE+1;
strcpy(str,other.str);
}
}
Wstring::~Wstring()
{
clear();
}
bool Wstring::operator<(IN Wstring &other) RO
{
if (str == NULL && other.str == NULL)
return false;
if (str == NULL)
return true;
return ( strcmp(str, other.str) < 0 );
}
bit8 Wstring::operator==(IN char *other) RO
{
if ((str==NULL)&&(other==NULL))
return(TRUE);
if(strcmp(str, other) != 0)
return(FALSE);
else
return(TRUE);
}
bit8 Wstring::operator==(IN Wstring &other) RO
{
if((str == NULL) && (other.str == NULL))
return(TRUE);
if((str == NULL) || (other.str == NULL))
return(FALSE);
if(strcmp(str, other.str) != 0)
return(FALSE);
else
return(TRUE);
}
bit8 Wstring::operator!=(IN char *other) RO
{
if(strcmp(str, other) != 0)
return(TRUE);
else
return(FALSE);
}
bit8 Wstring::operator!=(IN Wstring &other) RO
{
if((str == NULL) && (other.str == NULL))
return(FALSE);
if((str == NULL) || (other.str == NULL))
return(TRUE);
if(strcmp(str, other.str) != 0)
return(TRUE);
else
return(FALSE);
}
Wstring &Wstring::operator=(IN char *other)
{
set(other);
return(*this);
}
Wstring &Wstring::operator=(IN Wstring &other)
{
if(*this == other)
return(*this);
set(other.get());
return(*this);
}
bit8 Wstring::cat(IN char *s)
{
uint32 len;
if (s==NULL) // it's OK to cat nothing
return(TRUE);
// Determine the length of the resultant string.
len = strlen(s) + 1;
if(str)
len += strlen(str);
// Space check
strgrow(len);
strcat(str, s);
return(TRUE);
}
bit8 Wstring::cat(uint32 size, IN char *s)
{
uint32 len;
// Determine the length of the resultant string.
len = size + 1;
if(str)
len += strlen(str);
// Allocate memory for the new string.
strgrow(len);
strncat(str, s, size);
str[len-1]=0; // make sure null term'd
return(TRUE);
}
bit8 Wstring::cat(IN Wstring &other)
{
return cat(other.get());
}
Wstring &Wstring::operator+=(IN char *string)
{
cat(string);
return(*this);
}
Wstring &Wstring::operator+=(IN Wstring &other)
{
cat(other.get());
return(*this);
}
Wstring Wstring::operator+(IN char *string)
{
Wstring temp = *this;
temp.cat(string);
return(temp);
}
Wstring Wstring::operator+(IN Wstring &s)
{
Wstring temp = *this;
temp.cat(s);
return(temp);
}
//
// This function deletes 'count' characters indexed by `pos' from the Wstring.
// If `pos'+'count' is > the length of the array, the last 'count' characters
// of the string are removed. If an error occurs, FALSE is returned.
// Otherwise, TRUE is returned. Note: count has a default value of 1.
//
//
char Wstring::remove(sint32 pos,sint32 count)
{
//char *s;
sint32 len;
len = (sint32)strlen(str);
if(pos+count > len)
pos = len - count;
if (pos < 0)
{
count+=pos; // If they remove before 0, ignore up till beginning
pos=0;
}
if (count<=0)
return(FALSE);
memmove(str+pos,str+pos+count,len-pos-count+1);
return(TRUE);
}
// Remove all instances of a char from the string
bit8 Wstring::removeChar(char c)
{
int len=0;
char *cptr=NULL;
bit8 removed=FALSE;
if (str==NULL)
return(FALSE);
len=strlen(str);
while ((cptr=strchr(str,c)) !=NULL)
{
memmove(cptr,cptr+1,len-1-((int)(cptr-str)));
len--;
str[len]=0;
removed=TRUE;
}
return(removed);
}
void Wstring::removeSpaces(void)
{
removeChar(' ');
removeChar('\t');
}
void Wstring::clear(void)
{
if(str)
delete[](str);
strsize=0;
str=NULL;
}
// This is usually used for raw storage instead of string ops...
void Wstring::setSize(sint32 size)
{
clear();
if (size<0)
return;
str=new char[size];
strsize=size;
memset(str,0,size);
}
void Wstring::cellCopy(char *dest, uint32 len)
{
uint32 i;
strncpy(dest, str, len);
for(i = (uint32)strlen(str); i < len; i++)
dest[i] = ' ';
dest[len] = 0;
}
char *Wstring::get(void) RO
{
if(!str)
return "";
return str;
}
char Wstring::get(uint32 index) RO
{
if(index < strlen(str))
return str[index];
return(0);
}
uint32 Wstring::length(void) RO
{
if(str == NULL)
return(0);
return((uint32)strlen(str));
}
// Insert at given position and shift old stuff to right
bit8 Wstring::insert(char *instring, uint32 pos)
{
if (str==NULL)
return(set(instring));
if (pos>strlen(str))
pos=strlen(str);
strgrow(strlen(str)+strlen(instring)+1);
memmove(str+pos+strlen(instring),str+pos,strlen(str)-pos+1);
memmove(str+pos,instring,strlen(instring));
return(TRUE);
}
// This function inserts the character specified by `k' into the string at the
// position indexed by `pos'. If `pos' is >= the length of the string, it is
// appended to the string. If an error occurs, FALSE is returned. Otherwise,
// TRUE is returned.
bit8 Wstring::insert(char k, uint32 pos)
{
char temp[2];
temp[0]=k;
temp[1]=0;
return(insert(temp,pos));
}
// Joe Howes (05/19/2000): This function inserts commas to nicely format a
// large number (i.e. 1234567890 -> 1,234,567,890). It doesn't really care
// if the string is really a number or not.
bit8 Wstring::beautifyNumber()
{
int len = length();
int accum = 3 - (len % 3);
int numcommas = 0;
if( accum == 3 ) accum = -1;
for(int i = 0; i < len; i++)
{
if( accum == 3 )
{
insert(',', i + numcommas);
numcommas++;
}
accum = ( accum == 3 || accum == -1 ) ? 1 : accum + 1;
}
return(TRUE);
}
// This function replaces any occurences of the string pointed to by
// `replaceThis' with the string pointed to by `withThis'. If an error
// occurs, FALSE is returned. Otherwise, TRUE is returned.
bit8 Wstring::replace(IN char *replaceThis,IN char *withThis)
{
Wstring dest;
char *foundStr, *src;
uint32 len;
src=get();
while(src && src[0])
{
foundStr = strstr(src, replaceThis);
if(foundStr)
{
len = (uint32)foundStr - (uint32)src;
if(len)
{
if(!dest.cat(len, src))
return(FALSE);
}
if(!dest.cat(withThis))
return(FALSE);
src = foundStr + strlen(replaceThis);
}
else
{
if(!dest.cat(src))
return(FALSE);
src=NULL;
}
}
return(set(dest.get()));
}
bit8 Wstring::set(IN char *s)
{
//uint32 len;
strgrow(strlen(s)+1);
strcpy(str,s);
return(TRUE);
}
bit8 Wstring::set(char c, uint32 index)
{
if(index >= (uint32)strlen(str))
return FALSE;
str[index] = c;
return TRUE;
}
char Wstring::set(uint32 size, IN char *string)
{
//uint32 len;
strgrow(size+1);
strncpy(str,string,size);
str[size]=0;
return(TRUE);
}
// Added by Joe Howes. Takes a printf formatted string and a set of args.
// The expanded string must not exceed 1k or twice the length of the format
// string, whichever is larger. It would probably be better to traverse
// the format string and properly calculate, the length so this will
// work in all cases, but this should be good enough for 99% of Wstring usage.
char Wstring::setFormatted(IN char *msg, ...)
{
if( msg == NULL || strlen(msg) <= 0 ) return FALSE;
char* string;
va_list args;
int len = (strlen(msg) < 1024) ? 1024 : (strlen(msg)*2);
string = new char[len];
va_start(args, msg);
vsprintf(string, msg, args);
va_end(args);
set(string);
delete[] string;
return(TRUE);
}
// This function converts all alphabetical characters in the string to lower
// case.
void Wstring::toLower(void)
{
uint32 i;
int strlength=length();
for(i = 0; i < (uint32)strlength; i++)
{
if((str[i] >= 'A') && (str[i] <= 'Z'))
str[i] = (sint8)tolower(str[i]);
}
}
// This function converts all alphabetical characters in the string to upper
// case.
void Wstring::toUpper(void)
{
uint32 i;
int strlength=length();
for(i = 0; i < (uint32)strlength; i++)
{
if((str[i] >= 'a') && (str[i] <= 'z'))
str[i] = (sint8)toupper(str[i]);
}
}
// This function truncates the string so its length will match the specified
// `len'. If an error occurs, FALSE is returned. Otherwise, TRUE is returned.
bit8 Wstring::truncate(uint32 len)
{
strgrow(len+1);
str[len]=0;
return(TRUE);
}
// Truncate the string after the character 'c' (gets rid of 'c' as well)
// Do nothing if 'c' isn't in the string
bit8 Wstring::truncate(char c)
{
sint32 len;
if (str==NULL)
return(FALSE);
char *cptr=strchr(str,c);
if (cptr==NULL)
return(FALSE);
len=(sint32)(cptr-str);
truncate((uint32)len);
return(TRUE);
}
// Get a token from this string that's seperated by one or more
// chars from the 'delim' string , start at offset & return offset
sint32 Wstring::getToken(int offset,char *delim,Wstring &out) RO
{
int i;
sint32 start;
sint32 stop;
if (offset<0) // check for bad input
return(-1);
for (i=offset; i<(int)length(); i++) {
if(strchr(delim,str[i])==NULL)
break;
}
if (i>=(int)length())
return(-1);
start=i;
for (; i<(int)length(); i++) {
if(strchr(delim,str[i])!=NULL)
break;
}
stop=i-1;
out.set(str+start);
out.truncate((uint32)stop-start+1);
return(stop+1);
}
// Get the first line of text after offset. Lines are terminated by '\r\n' or '\n'
sint32 Wstring::getLine(int offset, Wstring &out)
{
int i;
sint32 start;
sint32 stop;
start=i=offset;
if (start >= (sint32)length())
return(-1);
for (; i<(int)length(); i++) {
if(strchr("\r\n",str[i])!=NULL)
break;
}
stop=i;
if ((str[stop]=='\r')&&(str[stop+1]=='\n'))
stop++;
out.set(str+start);
out.truncate((uint32)stop-start+1);
return(stop+1);
}
//
// Make sure there's AT LEAST length bytes in this string object
//
void Wstring::strgrow(int length)
{
if (str==NULL)
{
str=new char[length+PADSIZE];
str[0]=0;
strsize=length+PADSIZE;
return;
}
else if (strsize >= length) // no need to alloc more data
return;
else // bah, gotta grow...
{
char *newstr=new char[length+PADSIZE];
strsize=length+PADSIZE;
strcpy(newstr,str);
delete[](str);
str=newstr;
return;
}
}

View file

@ -0,0 +1,93 @@
/*
** Command & Conquer Generals(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: Carpenter (The RedAlert ladder creator)
File Name : main.cpp
Author : Neal Kettler
Start Date : June 1, 1997
Last Update : June 17, 1997
\****************************************************************************/
#ifndef WSTRING_HEADER
#define WSTRING_HEADER
#include <stdio.h>
#include <stdlib.h>
#include "wstypes.h"
class Wstring
{
public:
Wstring();
Wstring(IN Wstring &other);
Wstring(IN char *string);
~Wstring();
void clear(void);
bit8 cat(IN char *string);
bit8 cat(uint32 size,IN char *string);
bit8 cat(IN Wstring &string);
void cellCopy(OUT char *dest, uint32 len);
char remove(sint32 pos, sint32 count);
bit8 removeChar(char c);
void removeSpaces(void);
char *get(void) RO;
char get(uint32 index) RO;
uint32 length(void) RO;
bit8 insert(char c, uint32 pos);
bit8 insert(char *instring, uint32 pos);
bit8 beautifyNumber();
bit8 replace(IN char *replaceThis,IN char *withThis);
char set(IN char *str);
char set(uint32 size,IN char *str);
bit8 set(char c, uint32 index);
char setFormatted(IN char *str, ...); // Added by Joe Howes
void setSize(sint32 bytes); // create an empty string
void toLower(void);
void toUpper(void);
bit8 truncate(uint32 len);
bit8 truncate(char c); // trunc after char c
sint32 getToken(int offset,char *delim,Wstring &out) RO;
sint32 getLine(int offset, Wstring &out);
void strgrow(int length);
bit8 operator==(IN char *other) RO;
bit8 operator==(IN Wstring &other) RO;
bit8 operator!=(IN char *other) RO;
bit8 operator!=(IN Wstring &other) RO;
Wstring &operator=(IN char *other);
Wstring &operator=(IN Wstring &other);
Wstring &operator+=(IN char *other);
Wstring &operator+=(IN Wstring &other);
Wstring operator+(IN char *other);
Wstring operator+(IN Wstring &other);
bool operator<(IN Wstring &other) RO;
private:
char *str; // Pointer to allocated string.
int strsize; // allocated data length
};
#endif

View file

@ -0,0 +1,118 @@
/*
** Command & Conquer Generals(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: Carpenter (The RedAlert ladder creator)
File Name : wstypes.h
Author : Neal Kettler
Start Date : June 3, 1997
Last Update : June 17, 1997
Standard type definitions for the sake of portability and readability.
\***************************************************************************/
#ifndef WSTYPES_HEADER
#define WSTYPES_HEADER
#ifdef _REENTRANT // reentrant = threaded
// Headers with non threadsafe libs need to come before my hacky
// threadsafe.h otherwise they won't compile
#include <time.h>
#ifndef _WINDOWS
#define _POSIX_C_SOURCE 199506L
#define _POSIX_PTHREAD_SEMANTICS
#define __EXTENSIONS__
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef _WINDOWS
#include <unistd.h>
#include <sys/time.h>
#include <dirent.h>
#else
#include <time.h>
#include <sys/timeb.h>
#endif
#include "threadsafe.h" // enforce threadsafe-only calls
#endif
#define adelete(X) (delete[](X))
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif
#ifndef MAX
#define MAX(x,y) (((x)>(y))?(x):(y))
#endif
#ifndef NULL
#define NULL 0
#endif
//These are used for readability purposes mostly, when a method takes a
// pointer or reference these help specify what will happen to the data
// that is sent in.
#ifdef IN
#undef IN
#endif
#define IN const
#define OUT
#define INOUT
#define _IN_ const
// Used to declare a function or method as const or Read Only
#define RO const
typedef char bit8;
typedef char sint8;
typedef unsigned char uint8;
typedef signed short int sint16;
typedef unsigned short int uint16;
typedef signed int sint32;
typedef unsigned int uint32;
typedef float float32;
typedef double float64;
#define MAX_BIT8 0x1
#define MAX_UINT32 0xFFFFFFFF
#define MAX_UINT16 0xFFFF
#define MAX_UINT8 0xFF
#define MAX_SINT32 0x7FFFFFFF
#define MAX_SINT16 0x7FFF
#define MAX_SINT8 0x7F
#ifdef _WINDOWS
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#endif

View file

@ -0,0 +1,802 @@
/*
** Command & Conquer Generals(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"};
// MDC: Windows doesn't provide a localtime_r, so make our own...
#ifdef _WINDOWS
#ifdef _REENTRANT
#include "critsec.h"
static CritSec localtime_critsec;
#undef localtime
struct tm *localtime(const time_t *clockval);
#endif // _REENTRANT
static struct tm *localtime_r(const time_t *clockval, struct tm *res) {
#ifdef _REENTRANT
localtime_critsec.lock();
#endif
struct tm *static_tm = localtime(clockval);
res = (struct tm *)memcpy(res, static_tm, sizeof(tm));
#ifdef _REENTRANT
localtime_critsec.unlock();
#endif
return res;
}
#endif // _WINDOWS
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"
// CHANGE: Joe Howes 06/30/99
// To specify 12-hour format, use "aa" instead of "hh".
// The hours will be 12 hour and the string will be
// appended with " AM" or " PM".
bit8 Wtime::FormatTime(char *out, char *format)
{
int lastWasH=0;
int ampmflag = 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;
}
else if (strncmp(ptr,"aa",2)==0)
{
uint32 tmp = (GetHour() <= 12) ? GetHour() : GetHour() - 12;
sprintf(out+strlen(out),"%02ld", tmp);
lastWasH=2; // needs to be 1 after top of loop decs it
ptr+=2;
ampmflag = 1;
}
else // an unknown char, move to next
ptr++;
}
if(ampmflag)
{
char ampm[4];
if( GetHour() < 12 )
strcpy(ampm, " AM");
else
strcpy(ampm, " PM");
sprintf(out+strlen(out), "%s", ampm);
}
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 t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
return(tptr->tm_sec);
}
uint32 Wtime::GetMinute(void) const
{
struct tm t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
return(tptr->tm_min);
}
uint32 Wtime::GetHour(void) const
{
struct tm t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
return(tptr->tm_hour);
}
uint32 Wtime::GetMDay(void) const
{
struct tm t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
return(tptr->tm_mday);
}
uint32 Wtime::GetWDay(void) const
{
struct tm t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
return(tptr->tm_wday+1);
}
uint32 Wtime::GetYDay(void) const
{
struct tm t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
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 t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
return(tptr->tm_mon+1);
}
uint32 Wtime::GetYear(void) const
{
struct tm t;
struct tm *tptr;
tptr=localtime_r((time_t *)&sec,&t);
if ((tptr->tm_year)>=70)
return((tptr->tm_year)+1900);
else
return((tptr->tm_year)+2000);
}
bit8 Wtime::GetSign(void) const
{
return(sign);
}
// 1 = *this > other
//-1 = *this < other
// 0 = *this == other
int Wtime::Compare(const Wtime &other) const
{
if ((sec==other.sec)&&(usec==other.usec))
return(0); // equal
else if (sec>other.sec)
return(1);
else if (sec<other.sec)
return(-1);
else if (usec>other.usec)
return(1);
else
return(-1);
}
bit8 Wtime::operator == ( const Wtime &other ) const
{
bit8 retval=Compare(other);
if (retval==0)
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator != ( const Wtime &other ) const
{
bit8 retval=Compare(other);
if (retval==0)
return(FALSE);
else
return(TRUE);
}
bit8 Wtime::operator < ( const Wtime &other ) const
{
int retval=Compare(other);
if (retval==-1)
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator > ( const Wtime &other ) const
{
int retval=Compare(other);
if (retval==1)
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator <= ( const Wtime &other ) const
{
int retval=Compare(other);
if ((retval==-1)||(retval==0))
return(TRUE);
else
return(FALSE);
}
bit8 Wtime::operator >= ( const Wtime &other ) const
{
int retval=Compare(other);
if ((retval==1)||(retval==0))
return(TRUE);
else
return(FALSE);
}
// None of the operators pay attention to sign
// only the functions that begin with 'Signed'
void Wtime::SignedAdd(const Wtime &other)
{
Wtime temp;
if ((sign==POSITIVE)&&(other.sign==POSITIVE))
{
*this+=other;
sign=POSITIVE;
}
else if ((sign==POSITIVE)&&(other.sign==NEGATIVE))
{
if (*this>other)
{
*this-=other;
sign=POSITIVE;
}
else
{
temp=other;
temp-=*this;
*this=temp;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==POSITIVE))
{
if (*this<other)
{
temp=other;
temp-=*this;
*this=temp;
sign=POSITIVE;
}
else
{
*this-=other;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==NEGATIVE))
{
*this+=other;
sign=NEGATIVE;
}
}
// None of the operators pay attention to sign
// only the functions that begin with 'Signed'
void Wtime::SignedSubtract(const Wtime &other)
{
Wtime temp;
if ((sign==POSITIVE)&&(other.sign==NEGATIVE))
{
*this+=other;
sign=POSITIVE;
}
else if ((sign==POSITIVE)&&(other.sign==POSITIVE))
{
if (*this>other)
{
*this-=other;
sign=POSITIVE;
}
else
{
temp=other;
temp-=*this;
*this=temp;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==NEGATIVE))
{
if (*this<other)
{
temp=other;
temp-=*this;
*this=temp;
sign=POSITIVE;
}
else
{
*this-=other;
sign=NEGATIVE;
}
}
else if ((sign==NEGATIVE)&&(other.sign==POSITIVE))
{
*this+=other;
sign=NEGATIVE;
}
}
Wtime &Wtime::operator += (const Wtime &other)
{
sec+=other.sec;
usec+=other.usec;
if (usec>1000000)
{
sec++;
usec-=1000000;
}
return *this;
}
Wtime &Wtime::operator -= (const Wtime &other)
{
sint32 temp;
if (Compare(other)==-1)
{
sec=0; // can't handle negative time
usec=0;
return *this;
}
sec-=other.sec;
temp=(sint32)usec;
temp-=(sint32)other.usec;
if (temp<0)
{
sec--;
temp+=1000000;
}
usec=temp;
return *this;
}
Wtime Wtime::operator - (Wtime &other)
{
Wtime temp(*this);
temp-=other;
return(temp);
}
Wtime Wtime::operator + (Wtime &other)
{
Wtime temp(*this);
temp+=other;
return(temp);
}
Wtime &Wtime::operator = (const Wtime &other)
{
sign=other.sign;
sec=other.sec;
usec=other.usec;
return *this;
}
Wtime &Wtime::operator += (const uint32 other)
{
sec+=other;
return *this;
}
Wtime &Wtime::operator -= (const uint32 other)
{
sec-=other;
return *this;
}
Wtime Wtime::operator - (uint32 other)
{
Wtime temp(*this);
temp-=other;
return(temp);
}
Wtime Wtime::operator + (uint32 other)
{
Wtime temp(*this);
temp+=other;
return(temp);
}
Wtime &Wtime::operator = (const uint32 other)
{
sign=POSITIVE;
sec=other;
usec=0;
return *this;
}

View file

@ -0,0 +1,128 @@
/*
** Command & Conquer Generals(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 "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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,146 @@
/*
** Command & Conquer Generals(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/>.
*/
/****************************************************************************\
xtime Neal Kettler
This is version 2 of the Wtime library (now xtime). It now supports
time storage from the year 0 to well after the sun
will have gone supernova (OK, OK I admit it'll break in the
year 5 Million.)
The call to update the current time will break in 2038.
Hopefully by then somebody will replace the lame time()
function :-)
\****************************************************************************/
#ifndef XTIME_HEADER
#define XTIME_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 "wstypes.h"
class Xtime
{
public:
Xtime(); // init to system time
Xtime( Xtime &other );
Xtime( time_t other ); // 1970-2038
~Xtime();
void addSeconds(sint32 seconds);
bit8 getTime(int &month, int &mday, int &year, int &hour, int &minute,
int &second) const;
bit8 setTime(int month, int mday, int year, int hour, int minute,
int second);
void update(); // Update members sec & usec to system time
// This will break after 2038
/********
void PrintTime(FILE *out) const;
void PrintTime(char *out) const;
void PrintDate(FILE *out) const;
void PrintDate(char *out) const;
**********/
sint32 getDay(void) const; // Get days since year 0
sint32 getMsec(void) const; // Get milliseconds into the day
void setDay(sint32 day);
void setMsec(sint32 msec);
void set(sint32 newday, sint32 newmsec);
bit8 ParseDate(char *in);
bit8 FormatTime(char *out, char *format);
bit8 getTimeval(struct timeval &tv);
// All of these may return -1 if the time is invalid
int getSecond(void) const; // Second (0-60) (60 is for a leap second)
int getMinute(void) const; // Minute (0-59)
int getHour(void) const; // Hour (0-23)
int getMDay(void) const; // Day of Month (1-31)
int getWDay(void) const; // Day of Week (1-7)
int getYDay(void) const; // Day of Year (1-366) (366 = leap yr)
int getMonth(void) const; // Month (1-12)
int getYWeek(void) const; // Week of Year (1-53)
int getYear(void) const; // Year (e.g. 1997)
// Modify the time components. Return FALSE if fail
bit8 setSecond(sint32 sec);
bit8 setMinute(sint32 min);
bit8 setHour(sint32 hour);
bit8 setYear(sint32 year);
bit8 setMonth(sint32 month);
bit8 setMDay(sint32 mday);
void normalize(void); // move msec overflows to the day
// Compare two times
int compare(const Xtime &other) const;
// comparisons
bit8 operator == ( const Xtime &other ) const;
bit8 operator != ( const Xtime &other ) const;
bit8 operator < ( const Xtime &other ) const;
bit8 operator > ( const Xtime &other ) const;
bit8 operator <= ( const Xtime &other ) const;
bit8 operator >= ( const Xtime &other ) const;
// assignments
Xtime &operator = (const Xtime &other);
Xtime &operator = (const time_t other);
// signed
Xtime &operator += (const Xtime &other);
Xtime &operator -= (const Xtime &other);
Xtime operator + (Xtime &other);
Xtime operator - (Xtime &other);
Xtime &operator += (const time_t other);
Xtime &operator -= (const time_t other);
Xtime operator + (time_t other);
Xtime operator - (time_t other);
protected:
sint32 day_; // days since Jan 1, 0
sint32 msec_; // milliseconds (thousandths of a sec)
};
#endif

View file

@ -0,0 +1,33 @@
###########################################################################
# TCP library makefile
##########################################################################
#Define cc to be your C compiler
CC = g++
CFLAGS = -gstabs ${INCLUDE} -D_REENTRANT #-DDEBUG
#This tells make how to go from a .cpp to a .o
.SUFFIXES: .cpp
.cpp.o:
${CC} ${CFLAGS} -c $<
INCLUDE = -I.. -I.
AR = ar -r
RM = rm -f
RANLIB = ranlib
############################################################################
#Dont mess with any of this stuff
OBJECTS = tcp.o udp.o packet.o field.o
LIBRARY = libwnet.a
all: $(LIBRARY)
$(LIBRARY): $(OBJECTS)
$(RM) $(LIBRARY)
$(AR) $(LIBRARY) $(OBJECTS)
$(RANLIB) $(LIBRARY)
clean:
- rm -f $(LIBRARY) $(OBJECTS) core

View file

@ -0,0 +1,319 @@
/*
** Command & Conquer Generals(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>
#ifndef _WINDOWS
#include <netinet/in.h>
#else
#define Win32_Winsock
#include <windows.h>
#endif
#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);
}
void *FieldClass::Get_Data(void)
{
return(Data);
}
char *FieldClass::Get_ID(void)
{
return(ID);
}
/**************************************************************************
* PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format *
* *
* INPUT: FIELD * to the data field we need to convert *
* *
* OUTPUT: none *
* *
* HISTORY: *
* 04/22/1996 PWG : Created. *
*========================================================================*/
void FieldClass::Host_To_Net(void)
{
//
// Before we convert the data type, we should convert the actual data
// sent.
//
switch (DataType) {
case TYPE_CHAR:
case TYPE_UNSIGNED_CHAR:
case TYPE_STRING:
break;
case TYPE_SHORT:
case TYPE_UNSIGNED_SHORT:
*((unsigned short *)Data) = htons(*((unsigned short *)Data));
break;
case TYPE_LONG:
case TYPE_UNSIGNED_LONG:
*((unsigned long *)Data) = htonl(*((unsigned long *)Data));
break;
//
// Might be good to insert some type of error message here for unknown
// datatypes -- but will leave that for later.
//
default:
break;
}
//
// Finally convert over the data type and the size of the packet.
//
DataType = htons(DataType);
Size = htons(Size);
}
/**************************************************************************
* PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format *
* *
* INPUT: FIELD * to the data field we need to convert *
* *
* OUTPUT: none *
* *
* HISTORY: *
* 04/22/1996 PWG : Created. *
*========================================================================*/
void FieldClass::Net_To_Host(void)
{
//
// Finally convert over the data type and the size of the packet.
//
DataType = ntohs(DataType);
Size = ntohs(Size);
//
// Before we convert the data type, we should convert the actual data
// sent.
//
switch (DataType) {
case TYPE_CHAR:
case TYPE_UNSIGNED_CHAR:
case TYPE_STRING:
break;
case TYPE_SHORT:
case TYPE_UNSIGNED_SHORT:
*((unsigned short *)Data) = ntohs(*((unsigned short *)Data));
break;
case TYPE_LONG:
case TYPE_UNSIGNED_LONG:
*((unsigned long *)Data) = ntohl(*((unsigned long *)Data));
break;
//
// Might be good to insert some type of error message here for unknown
// datatypes -- but will leave that for later.
//
default:
break;
}
}

View file

@ -0,0 +1,101 @@
/*
** Command & Conquer Generals(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 * Get_Data(void); // get the datatype of this field
char * Get_ID(void); // get the datatype of this field
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
char *Data; // pointer to the data portion of this field
FieldClass *Next; // pointer to the next field in the field list
};

View file

@ -0,0 +1,522 @@
/*
** Command & Conquer Generals(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>
#ifndef _WINDOWS
#include <netinet/in.h>
#else
#define Win32_Winsock
#include <windows.h>
#endif
#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;
}
// gks 9/25/2000
FieldClass *PacketClass::Get_Field_At(int position)
{
int i = 0;
FieldClass *current = Head;
for (; (current && (i < position)); current = current->Next, i++) {
}
if (current) return current;
else return NULL;
}
// gks 9/25/2000
int PacketClass::Get_Num_Fields()
{
int i = 0;
for (FieldClass *current = Head; current; current = current->Next, i++) {}
return i;
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* char & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, char &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((char *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* unsigned char & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, unsigned char &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned char *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* short & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, short &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((short *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* unsigned short & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, unsigned short &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned short *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* long & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, long &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((long *)field->Data);
}
return((field) ? true : false);
}
bit8 PacketClass::Get_Field(char *id, int &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((int *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data as a string *
* *
* INPUT: char * - the id of the field that holds the data. *
* char * - the string to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The string is not changed if the field is not found. It *
* is assumed that the string variabled specified by the *
* pointer is large enough to hold the data. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, char *data)
{
FieldClass *field = Find_Field(id);
if (field) {
strcpy(data, (char *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* unsigned long & - the reference to store the data into *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 04/23/1996 PWG : Created. *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, unsigned long &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned long *)field->Data);
}
return((field) ? true : false);
}
bit8 PacketClass::Get_Field(char *id, unsigned &data)
{
FieldClass *field = Find_Field(id);
if (field) {
data = *((unsigned *)field->Data);
}
return((field) ? true : false);
}
/**************************************************************************
* GET_FIELD -- Find specified name and returns data *
* *
* INPUT: char * - the id of the field that holds the data. *
* void * - the reference to store the data into *
* int - the length of the buffer passed in *
* *
* OUTPUT: true if the field was found, false if it was not. *
* *
* WARNINGS: The data reference is not changed if the field is not *
* found. *
* *
* HISTORY: *
* 6/4/96 4:46PM ST : Created *
*========================================================================*/
bit8 PacketClass::Get_Field(char *id, void *data, int &length)
{
FieldClass *field = Find_Field(id);
if (field) {
memcpy (data, field->Data, MIN(field->Size, length));
length = (int) field->Size;
}
return((field) ? true : false);
}
unsigned short PacketClass::Get_Field_Size(char* id)
{
FieldClass *field = Find_Field(id);
if (field)
return field->Get_Size();
else
return 0;
}

View file

@ -0,0 +1,108 @@
/*
** Command & Conquer Generals(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);
// gks 9/25/2000
FieldClass *Get_Field_At(int position);
int Get_Num_Fields();
char *Create_Comms_Packet(int &size);
private:
unsigned short Size;
short ID;
FieldClass *Head;
FieldClass *Current;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,199 @@
/*
** Command & Conquer Generals(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/>.
*/
/****************************************************************************\
TCP Neal Kettler neal@westwood.com
\****************************************************************************/
#ifndef TCP_HEADER
#define TCP_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#ifdef _WINDOWS
#include <winsock.h>
#include <io.h>
#define close _close
#define read _read
#define write _write
#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 signed int SOCKET;
#endif
#ifdef AIX
#include <sys/select.h>
#endif
#define DEFAULT_PROTOCOL 0
#include "wlib/wstypes.h"
#include "wlib/wdebug.h"
#include "wlib/wtime.h"
class TCP
{
// DATA ---------------
private:
int mode; // client or server
sint32 fd; // the primary FD
uint32 myIP; // after bind myIP & myPort will be
uint16 myPort; // whatever we bound to
struct sockaddr_in addr;
int maxFD; // value of the biggest FD
int clientCount; // how many clients open
sint32 inputDelay; // default delay for semi-blocking reads
sint32 outputDelay; // default delay for semi-blocking writes
enum ConnectionState
{
CLOSED,
CONNECTING,
CONNECTED
} connectionState; // What state is client FD in
public:
enum
{
CLIENT = 1,
SERVER = 2
};
// These defines specify a system independent way to
// get error codes for socket services.
enum
{
OK, // Everything's cool
UNKNOWN, // There was an error of unknown type
ISCONN, // The socket is already connected
INPROGRESS, // The socket is non-blocking and the operation
// isn't done yet
ALREADY, // The socket is already attempting a connection
// but isn't done yet
AGAIN, // Try again.
ADDRINUSE, // Address already in use
ADDRNOTAVAIL, // That address is not available on the remote host
BADF, // Not a valid FD
CONNREFUSED, // Connection was refused
INTR, // Operation was interrupted
NOTSOCK, // FD wasn't a socket
PIPE, // That operation just made a SIGPIPE
WOULDBLOCK, // That operation would block
INVAL, // Invalid
TIMEDOUT // Timeout
};
// for client list (if this is a server)
fd_set clientList;
// CODE ----------------
public:
TCP(int newMode);
TCP(int newMode,sint16 socket);
~TCP();
bit8 Bind(uint32 IP,uint16 port,bit8 reuseAddr=FALSE);
bit8 Bind(char *Host,uint16 port,bit8 reuseAddr=FALSE);
sint32 GetMaxFD(void);
bit8 Connect(uint32 IP,uint16 port);
bit8 Connect(char *Host,uint16 port);
bit8 ConnectAsync(uint32 IP,uint16 port);
bit8 ConnectAsync(char *Host,uint16 port);
bit8 IsConnected(sint32 whichFD=0);
sint32 GetFD(void);
sint32 GetClientCount(void) { return(clientCount); }
// Get IP or Port of a connected endpoint
uint32 GetRemoteIP(sint32 whichFD=0);
uint16 GetRemotePort(sint32 whichFD=0);
sint32 GetConnection(void);
sint32 GetConnection(struct sockaddr *clientAddr);
void WaitWrite(sint32 whichFD=0);
bit8 CanWrite(sint32 whichFD=0);
sint32 Write(const uint8 *msg,uint32 len,sint32 whichFD=0);
sint32 WriteNB(uint8 *msg,uint32 len,sint32 whichFD=0);
sint32 EncapsulatedWrite(uint8 *msg,uint32 len,sint32 whichFD=0);
sint32 WriteString(char *msg,sint32 whichFD=0);
sint32 Printf(sint32 whichFD,const char *format,...);
sint32 Read(uint8 *msg,uint32 len,sint32 whichFD=0);
sint32 TimedRead(uint8 *msg,uint32 len,int seconds,sint32 whichFD=0);
sint32 Peek(uint8 *msg,uint32 len,sint32 whichFD=0);
sint32 EncapsulatedRead(uint8 *msg,uint32 len,sint32 whichFD=0);
char *Gets(char *string,int n,int whichFD=0);
// Wait on all sockets (or a specified one)
// return when ready for reading (or timeout occurs)
int Wait(sint32 sec,sint32 usec,fd_set &returnSet,sint32 whichFD=0);
int Wait(sint32 sec,sint32 usec,fd_set &inputSet,fd_set &returnSet);
int GetStatus(void);
void ClearStatus(void);
//sint32 GetSockStatus(sint32 whichFD=0);
// give up ownership of the socket without closing it
void DisownSocket(void);
sint32 Close(sint32 whichFD=0);
sint32 CloseAll(void); // close all sockets (same as close for client)
sint32 SetBlocking(bit8 block,sint32 whichFD=0);
// Set default delays for semi-blocking reads & writes
// default input = 5, output = 5
// this is new and not used everywhere
//
bit8 SetInputDelay(sint32 delay) { inputDelay=delay; return(TRUE); };
bit8 SetOutputDelay(sint32 delay) { outputDelay=delay; return(TRUE); };
};
#endif

View file

@ -0,0 +1,372 @@
/*
** Command & Conquer Generals(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 "udp.h"
#include "wlib/wdebug.h"
UDP::UDP()
{
fd=0;
}
UDP::~UDP()
{
}
sint32 UDP::Bind(char *Host,uint16 port)
{
char hostName[100];
struct hostent *hostStruct;
struct in_addr *hostNode;
if (isdigit(Host[0]))
return ( Bind( ntohl(inet_addr(Host)), port) );
strcpy(hostName, Host);
hostStruct = gethostbyname(Host);
if (hostStruct == NULL)
return (0);
hostNode = (struct in_addr *) hostStruct->h_addr;
return ( Bind(ntohl(hostNode->s_addr),port) );
}
// You must call bind, implicit binding is for sissies
// Well... you can get implicit binding if you pass 0 for either arg
sint32 UDP::Bind(uint32 IP,uint16 Port)
{
int retval;
int status;
IP=htonl(IP);
Port=htons(Port);
addr.sin_family=AF_INET;
addr.sin_port=Port;
addr.sin_addr.s_addr=IP;
fd=socket(AF_INET,SOCK_DGRAM,DEFAULT_PROTOCOL);
#ifdef _WINDOWS
if (fd==SOCKET_ERROR)
fd=-1;
#endif
if (fd==-1)
return(UNKNOWN);
retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr));
#ifdef _WINDOWS
if (retval==SOCKET_ERROR)
retval=-1;
#endif
if (retval==-1)
{
status=GetStatus();
//CERR("Bind failure (" << status << ") IP " << IP << " PORT " << Port )
return(status);
}
int namelen=sizeof(addr);
getsockname(fd, (struct sockaddr *)&addr, &namelen);
myIP=ntohl(addr.sin_addr.s_addr);
myPort=ntohs(addr.sin_port);
retval=SetBlocking(FALSE);
if (retval==-1)
fprintf(stderr,"Couldn't set nonblocking mode!\n");
return(OK);
}
bit8 UDP::getLocalAddr(uint32 &ip, uint16 &port)
{
ip=myIP;
port=myPort;
return(OK);
}
// private function
sint32 UDP::SetBlocking(bit8 block)
{
#ifdef _WINDOWS
unsigned long flag=1;
if (block)
flag=0;
int retval;
retval=ioctlsocket(fd,FIONBIO,&flag);
if (retval==SOCKET_ERROR)
return(UNKNOWN);
else
return(OK);
#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(UNKNOWN);
}
return(OK);
#endif
}
sint32 UDP::Write(uint8 *msg,uint32 len,uint32 IP,uint16 port)
{
sint32 retval;
struct sockaddr_in to;
// This happens frequently
if ((IP==0)||(port==0)) return(ADDRNOTAVAIL);
errno=0;
to.sin_port=htons(port);
to.sin_addr.s_addr=htonl(IP);
to.sin_family=AF_INET;
ClearStatus();
retval=sendto(fd,(char *)msg,len,0,(struct sockaddr *)&to,sizeof(to));
#ifdef _WINDOWS
if (retval==SOCKET_ERROR)
retval=-1;
#endif
return(retval);
}
sint32 UDP::Read(uint8 *msg,uint32 len,sockaddr_in *from)
{
sint32 retval;
int alen=sizeof(sockaddr_in);
if (from!=NULL)
{
retval=recvfrom(fd,(char *)msg,len,0,(struct sockaddr *)from,&alen);
#ifdef _WINDOWS
if (retval==SOCKET_ERROR)
retval=-1;
#endif
}
else
{
retval=recvfrom(fd,(char *)msg,len,0,NULL,NULL);
#ifdef _WINDOWS
if (retval==SOCKET_ERROR)
retval=-1;
#endif
}
return(retval);
}
void UDP::ClearStatus(void)
{
#ifndef _WINDOWS
errno=0;
#endif
}
UDP::sockStat UDP::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
}
//
// Wait for net activity on this socket
//
int UDP::Wait(sint32 sec,sint32 usec,fd_set &returnSet)
{
fd_set inputSet;
FD_ZERO(&inputSet);
FD_SET(fd,&inputSet);
return(Wait(sec,usec,inputSet,returnSet));
}
//
// Wait for net activity on a list of sockets
//
int UDP::Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet)
{
Wtime timeout,timenow,timethen;
fd_set backupSet;
int retval=0,done,givenMax;
bit8 noTimeout=FALSE;
timeval tv;
returnSet=givenSet;
backupSet=returnSet;
if ((sec==-1)&&(usec==-1))
noTimeout=TRUE;
timeout.SetSec(sec);
timeout.SetUsec(usec);
timethen+=timeout;
givenMax=fd;
for (uint32 i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1
{
if (FD_ISSET(i,&givenSet))
givenMax=i;
}
///DBGMSG("WAIT fd="<<fd<<" givenMax="<<givenMax);
done=0;
while( ! done)
{
if (noTimeout)
retval=select(givenMax+1,&returnSet,0,0,NULL);
else
{
timeout.GetTimevalMT(tv);
retval=select(givenMax+1,&returnSet,0,0,&tv);
}
if (retval>=0)
done=1;
else if ((retval==-1)&&(errno==EINTR)) // in case of signal
{
if (noTimeout==FALSE)
{
timenow.Update();
timeout=timethen-timenow;
}
if ((noTimeout==FALSE)&&(timenow.GetSec()==0)&&(timenow.GetUsec()==0))
done=1;
else
returnSet=backupSet;
}
else // maybe out of memory?
{
done=1;
}
}
///DBGMSG("Wait retval: "<<retval);
return(retval);
}
// Set the kernel buffer sizes for incoming, and outgoing packets
//
// Linux seems to have a buffer max of 32767 bytes for this,
// (which is the default). If you try and set the size to
// greater than the default it just sets it to 32767.
bit8 UDP::SetInputBuffer(uint32 bytes)
{
#ifndef _WINDOWS
int retval,arg=bytes;
retval=setsockopt(fd,SOL_SOCKET,SO_RCVBUF,
(char *)&arg,sizeof(int));
if (retval==0)
return(TRUE);
else
return(FALSE);
#else
return(FALSE);
#endif
}
// Same note goes for the output buffer
bit8 UDP::SetOutputBuffer(uint32 bytes)
{
#ifndef _WINDOWS
int retval,arg=bytes;
retval=setsockopt(fd,SOL_SOCKET,SO_SNDBUF,
(char *)&arg,sizeof(int));
if (retval==0)
return(TRUE);
else
return(FALSE);
#else
return(FALSE);
#endif
}
// Get the system buffer sizes
int UDP::GetInputBuffer(void)
{
#ifndef _WINDOWS
int retval,arg=0,len=sizeof(int);
retval=getsockopt(fd,SOL_SOCKET,SO_RCVBUF,
(char *)&arg,&len);
return(arg);
#else
return(0);
#endif
}
int UDP::GetOutputBuffer(void)
{
#ifndef _WINDOWS
int retval,arg=0,len=sizeof(int);
retval=getsockopt(fd,SOL_SOCKET,SO_SNDBUF,
(char *)&arg,&len);
return(arg);
#else
return(0);
#endif
}

View file

@ -0,0 +1,114 @@
/*
** Command & Conquer Generals(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 UDP_HEADER
#define UDP_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#ifdef _WINDOWS
#include <winsock.h>
#include <io.h>
#define close _close
#define read _read
#define write _write
#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>
#endif
#ifdef AIX
#include <sys/select.h>
#endif
#define DEFAULT_PROTOCOL 0
#include <wlib/wstypes.h>
#include <wlib/wtime.h>
class UDP
{
// DATA
private:
sint32 fd;
uint32 myIP;
uint16 myPort;
struct sockaddr_in addr;
// These defines specify a system independent way to
// get error codes for socket services.
enum sockStat
{
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
};
// CODE
private:
sint32 SetBlocking(bit8 block);
public:
UDP();
~UDP();
sint32 Bind(uint32 IP,uint16 port);
sint32 Bind(char *Host,uint16 port);
sint32 Write(uint8 *msg,uint32 len,uint32 IP,uint16 port);
sint32 Read(uint8 *msg,uint32 len,sockaddr_in *from);
sockStat GetStatus(void);
void ClearStatus(void);
int Wait(sint32 sec,sint32 usec,fd_set &returnSet);
int Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet);
bit8 getLocalAddr(uint32 &ip, uint16 &port);
sint32 getFD(void) { return(fd); }
bit8 SetInputBuffer(uint32 bytes);
bit8 SetOutputBuffer(uint32 bytes);
int GetInputBuffer(void);
int GetOutputBuffer(void);
};
#endif